wrong converion with FJsonObjectConverter::UStructToJsonObjectString

I have a problem with convertion a struct to json string with FJsonObjectConverter::UStructToJsonObjectString:

This is my struct:

USTRUCT()
struct FSignupRequestStruct
{
GENERATED_BODY()

UPROPERTY()
FString Username;

UPROPERTY()
FString Password;

FSignupRequestStruct()
{
	Username.Empty();
	Password.Empty();
}

};

Using this function while Play-In-Editor correctly convert this struct to:
{
“username” : “player”,
“password”: “Password123!”
}

but in packaged build it converts to:
{
“userName” : “player”,
“password”: “Password123!”
}

I have no idea, how this could happend!
The same code without modifications run different in PIE and packaged build!

Any help would be greatly appreciated!

(post deleted by author)

I had the same problem, I wanted to send a json message to the server after packaging, but the field in the struct was tampered with and the sending failed.

I looked up the source code of the FJsonObjectConverter::UStructToJsonObjectString function and found that there is a line include official addition operation when createing the field name of json:

FString VariableName = StandardizeCase(Property->GetAuthoredName());

The comment to this function is

FName case insensitivity can make the casing of UPROPERTIES unpredictable. Attempt to standardize output.

and its realization

// this probably won't work for all cases, consider downcasing the string fully
FString FixedString = StringIn;
FixedString[0] = FChar::ToLower(FixedString[0]); // our JSON classes/variable start lower case
FixedString.ReplaceInline(TEXT("ID"), TEXT("Id"), ESearchCase::CaseSensitive); // Id is standard instead of ID, some of our fnames use ID
return FixedString;

You’ll notice that it puts the first letter in lower case and changes the ID to Id. At first I didn’t understand why we would add this step instead of letting the user decide the case, until I discovered our problem: the case of the field names converted by the function after packaging was inconsistent with the code.

I looked up the forums and found that it was probably caused by serialization, since the function used to get the field name was GetAuthoredName(), and the data eventually returned inside it was return NamePrivate.ToString(); , however, FName may be not case-sensitive in packaged builds,you can check out this forum link

https://forums.unrealengine.com/t/how-to-stop-case-correction-when-converting-from-struct-to-json-in-packaged-build/450097

This may be why the official added the additional operation of StandardizeCase, and indicated in the comment FName case insensitivity can make the casing of UPROPERTIES unpredictable

Based on this, here’s a stupid idea that came to mind.

I copied the code of the FJsonObjectConverter class, including its .cpp and .h files, so that I could modify it. I extended the StandardizeCase function and added the modified case words that I encountered there. Then I used this custom class to convert the wrong structures, and it turned out to be feasible.

This is my custom StandardizeCase code implementation

FString FixedString = StringIn;
FixedString.ReplaceInline(TEXT("ID"), TEXT("Id"), ESearchCase::CaseSensitive); 
FixedString.ReplaceInline(TEXT("number"),TEXT("Number"), ESearchCase::CaseSensitive);
FixedString[0] = FChar::ToLower(FixedString[0]);
return FixedString;

And this is the log output of my struct when it is packaged and transformed into JSON.

StructCode:

UPROPERTY(EditAnywhere,BlueprintReadWrite,Category="Json",DisplayName="ProgrammeNumber")
FString programmeNumber;

UPROPERTY(EditAnywhere,BlueprintReadWrite,Category="Json",DisplayName="ProjectNumber")
FString projectNumber;
	
UPROPERTY(EditAnywhere,BlueprintReadWrite,Category="Json",DisplayName="ProjectId")
FString projectId;

UPROPERTY(EditAnywhere,BlueprintReadWrite,Category="Json",DisplayName="ProgrammeName")
FString programmeName;

UPROPERTY(EditAnywhere,BlueprintReadWrite,Category="Json",DisplayName="ModuleNumbers")
TArray<FString> moduleNumbers;

LogAfterPackagedBefore:

{
	"programmeNumber": "",
	"projectnumber": "",
	"ProjectId": "",
	"programmeName": "",
	"moduleNumbers": []
}

To:

{
	"programmeNumber": "",
	"projectNumber": "",
	"projectId": "",
	"programmeName": "",
	"moduleNumbers": []
}

Although this solution is not perfect, it does solve my problem. Perhaps the official did not expect that except the “Id” field would be easily modified, other strange fields can alse be modified, such as my “N” turn to “n” in “projectnumber” and " p" turn to “P” in “projectId”.

It’s not clear that there will be a better way in the future, and I’ve thought about getting the Metadata "DisplayName", but it’s not available in the release Build. I hope there is a better way in the future, and welcome to discuss!