I’ve been trying to follow some examples that I’ve seen in the forums and here on AnswerHub to create a JSON string. The following is the code that does the writing:
FString JsonStr;
TSharedRef <TJsonWriter<TCHAR>> JsonWriter = TJsonWriterFactory<>::Create(&JsonStr);
// Write data
JsonWriter->WriteObjectStart("TestData"); // This does NOT work
//JsonWriter->WriteObjectStart(); // But this works
JsonWriter->WriteValue("Name", TEXT("Bob"));
JsonWriter->WriteValue("Age", 32);
JsonWriter->WriteObjectEnd();
As annotated in the code, if I use the WriteObjectStart() method, then the JSON string prints correctly. If, however, I use WriteObjectStart(“TestData”), the program crashes. Based on the code snippets that I’ve seen, this should work. Is there something obvious that I am doing wrong? I am using 4.5.1 of the engine. Any help would be greatly appreciated.
The following is what shows up in the Call Stack in Visual Studio, if it helps:
> UE4Editor-DemoProject-Win64-DebugGame.dll!TArray<enum EJson::Type,FDefaultAllocator>::Last(int c) Line 590 C++
UE4Editor-DemoProject-Win64-DebugGame.dll!TJsonWriter<wchar_t,TPrettyJsonPrintPolicy<wchar_t> >::WriteObjectStart(const FString & Identifier) Line 50 C++
UE4Editor-DemoProject-Win64-DebugGame.dll!ADemoProjectPlayerController::GetJsonString() Line 281 C++
UE4Editor-DemoProject-Win64-DebugGame.dll!ADemoProjectPlayerController::JsonString() Line 305 C++
UE4Editor-DemoProject-Win64-DebugGame.dll!ADemoProjectPlayerController::execJsonString(FFrame & Stack, void * const Result) Line 15 C++
Hi DM_Actual,
You should firstly call WriteObjectStart();
Try this:
FString JsonStr;
TSharedRef <TJsonWriter<TCHAR>> JsonWriter = TJsonWriterFactory<>::Create(&JsonStr);
// Write data
JsonWriter->WriteObjectStart();
JsonWriter->WriteObjectStart("TestData");
JsonWriter->WriteValue("Name", TEXT("Bob"));
JsonWriter->WriteValue("Age", 32);
JsonWriter->WriteObjectEnd();
JsonWriter->WriteObjectEnd();
JsonWriter->Close();
Best regards,
Thank you, . Yes, that solved the problem, but I was left wondering why. I looked at the documentation for TJsonWriter and I think I understand why my code didn’t work. WriteObjectStart() has
Stack.Push( EJson::Object );
whereas WriteObjectStart(FString), contains
check( Stack.Top() == EJson::Object );
Therefore, you must always start with WriteObjectStart(). I though that the JSON spec allowed for a name (optional) for the outermost object. Am I mistaken, or are there engine-related reasons why it must be this way?
Thank you again for your quick answer.
The top level token must be an object, not a pair. Take a look at the Json spec - it states that a Json object is either empty ({}) or a comma separated list of member pairs ({members}).
WriteObjectStart() writes the beginning of an object, whereas WriteObjectStart(Identifier) writes the beginning of an object value that is part of a string:value member pair.
The assertion in the latter function ensures that object value members are only written inside an object. Since you did not actually create an object to hold the member, it is correct that the assertion triggers. The solution suggested by DM_Actual is the right way to do this.
I agree that the top level token is an object, but I believe that it can have a name, just like the subobjects. If you look at the JSON Javadoc, you see the following example:
myString = new JSONObject().put("JSON", "Hello, World!").toString();
produces the string {"JSON": "Hello, World"}.
You can see that there is only one JSON object, and it has a name. In UE4’s implementation, however, solitary objects cannot be named – they must be wrapped in an outer object that has no name. I was wondering why this restriction exists in UE4, as wrapping an object is a (small) waste of bandwidth, and slightly more cumbersome. Thanks for your help.