Incorrect Json Deserialization for Subsequent Operations

Hi Community,

I am using FJsonObjectConverter for json deserialization. The result for the first deserialization is correct, but I get an incorrect result for subsequent operations. Maybe I have encountered a situation that belongs to the nature of C++ that I am not familiar with. I am sharing the code and debug results. How do you think I can fix the problem? Thanks.

Debug Result :

As you can see in the json data the “character” property is null but the debugger shows it as previous deserialized data.

Code :


UFUNCTION(BlueprintCallable, Category = "InJson", CustomThunk,
		meta = (CustomStructureParam = "StructToFill", DisplayName = "JsonStringToStruct"))
	static bool JsonStringToStruct(const FString& JsonString, UStruct*& StructToFill);

DECLARE_FUNCTION(execJsonStringToStruct)
	{
		P_GET_PROPERTY_REF(FStrProperty, JsonString);

		Stack.Step(Stack.Object, nullptr);
		const FStructProperty* StructProperty = CastField<FStructProperty>(Stack.MostRecentProperty);
		void* StructPtr = Stack.MostRecentPropertyAddress;
		
		P_FINISH;

		UStruct* StructDefinition = StructProperty->Struct;
		bool bSuccess;

		P_NATIVE_BEGIN

			bSuccess = Inner_JsonStringToStruct(JsonString, StructDefinition, StructPtr);

		P_NATIVE_END

		*static_cast<bool*>(RESULT_PARAM) = bSuccess;
	}

	static bool Inner_JsonStringToStruct(const FString& JsonString, UStruct* StructDefinition, void* StructPtr)
	{
		TSharedPtr<FJsonObject> JsonObject;
		const TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(JsonString);
		if (!FJsonSerializer::Deserialize(JsonReader, JsonObject) || !JsonObject.IsValid())
		{
			return false;
		}

		if (!FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), StructDefinition, StructPtr, 0, 0))
		{
			return false;
		}

		return true;
	}


Can you show the header declaration of that function ?
Those lines :

I also added the header declaration to the topic. Thanks for reminding.

UFUNCTION(BlueprintCallable, Category = "InJson", CustomThunk,
	meta = (CustomStructureParam = "StructToFill", DisplayName = "JsonStringToStruct"))
static bool JsonStringToStruct(const FString& JsonString, UStruct*& StructToFill);

The function works just fine with a simple struct for me.
I’m giving it json with a random value every second and it produces expected result every time.

image

What does your struct look like ? Especially that character part ?
Is it an object reference, or a nested struct ?
How about other struct members, do they update correctly on subsequent operations ?

1 Like

Ok so I have experimented a bit and as I suspected, null is not compatible as a nested struct value. So as a result when the string is parsed, the character:null part is interpreted as an object type which does not match the character property type, and it is skipped. And since blueprint graphs seemingly retain persistent memory for pins, it retains its previous value.

I can think of two possible solutions :

  1. Avoid having null as a possible json input for structs. Null is for pointers, and the only pointers in blueprints are for objects. Nested structs are not pointers and cannot be null. Replace null with whatever the default empty struct looks like. For example I see a companions field which seems to be an array. The corresponding empty json would be :
"character" : { "companions": [] }

If you are lucky maybe simply replacing "character":null with "character":{} could be enough for the underlying parser to reset the struct. That’s a pure guess though.


  1. Alternatively, modify your function a bit to reset the struct memory before each parsing.
    Add this bit just before calling Inner_JsonStringToStruct :
StructProperty->ClearValue(StructPtr);
1 Like

Hi Chatouille,

Firstly thanks a lot for your effort. I tried your recommendations but thats not solved the problem. Maybe i didnt apply the solution well idk. Finally i found a way. Honestly i dont know this is the perfect solution ot not, but its working…

I carried the JsonStringToStruct function to a ActorComponent and then i created and destroyed it on the deserialization operation. Inner blueprint view looks like that :

Actor blueprint :

ActorComponent blueprint :

Also I going to take care of the error handling.

Thanks.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.