JsonConverter in DataConfig

UE comes with a handy module JsonUtilities that handles conversion between USTRUCTs and JSON. In this example we've implemented similar functionalities that behaves almost identical to stock FJsonConverter.

// DataConfigExtra/Private/DataConfig/Extra/Types/DcJsonConverter.cpp
FString Str = TEXT(R"(
    {
        "strField" : "Foo",
        "nestField" : {
            "strArrayField" : [
                "One",
                "Two",
                "Three"
            ],
            "strIntMapField" : {
                "One": 1,
                "Two": 2,
                "Three": 3
            }
        },
        "intField" : 253,
        "boolField" : true
    }
)");

{
	FDcTestJsonConverter1 Lhs;
	bool LhsOk = DcExtra::JsonObjectStringToUStruct(Str, &Lhs);

    FDcTestJsonConverter1 Rhs;
	bool RhsOk = FJsonObjectConverter::JsonObjectStringToUStruct(Str, &Rhs);
}    
{
	FString Lhs;
	bool LhsOk = DcExtra::UStructToJsonObjectString(Data, Lhs);

	FString Rhs;
	bool RhsOk = FJsonObjectConverter::UStructToJsonObjectString(Data, Rhs);
}

DcExtra::JsonObjectStringToUStruct() body is trivia as it delegates most of the work to DcDeserializer:

// DataConfigExtra/Private/DataConfig/Extra/Types/DcJsonConverter.cpp
bool JsonObjectReaderToUStruct(FDcReader* Reader, FDcPropertyDatum Datum)
{
	FDcResult Ret = [&]() -> FDcResult {
		using namespace JsonConverterDetails;
		LazyInitializeDeserializer();

		FDcPropertyWriter Writer(Datum);

		FDcDeserializeContext Ctx;
		Ctx.Reader = Reader;
		Ctx.Writer = &Writer;
		Ctx.Deserializer = &Deserializer.GetValue();
		DC_TRY(Ctx.Prepare());

		DC_TRY(Deserializer->Deserialize(Ctx));
		return DcOk();
	}();

	if (!Ret.Ok())
	{
		DcEnv().FlushDiags();
		return false;
	}
	else
	{
		return true;
	}
}

The serializing function DcExtra::UStructToJsonObjectString() needs some customization as default DcJsonWriter and DcSerializer handlers behaves a bit different against stock FDcJsonConverter:

  • It serialize field names as camelCase.
  • It uses platform dependent line endings, that is \r\n on Windows.
  • It have subtle new line breaking rules on nested array and object, and on spacing around : token.

The good news is that one can customize these behaviors with DataConfig to match it:

// DataConfigExtra/Public/DataConfig/Extra/Types/DcJsonConverter.h
template<typename InStructType>
static bool UStructToJsonObjectString(const InStructType& InStruct, FString& OutJsonString)
{
    static FDcJsonWriter::ConfigType _JSON_CONVERTER_CONFIG = []
	{
		FDcJsonWriter::ConfigType Config = FDcJsonWriter::DefaultConfig;
		Config.IndentLiteral = TEXT("\t");
		Config.LineEndLiteral = LINE_TERMINATOR;
		Config.LeftSpacingLiteral = TEXT("");
		Config.bNestedArrayStartsOnNewLine = false;
		Config.bNestedObjectStartsOnNewLine = true;
		return Config;
	}();

	FDcJsonWriter Writer(_JSON_CONVERTER_CONFIG);
    ...
}

// DataConfigExtra/Private/DataConfig/Extra/Types/DcJsonConverter.cpp
static FDcResult HandlerStructRootSerializeCamelCase(FDcSerializeContext& Ctx)
{
    ...
	else if (CurPeek == EDcDataEntry::Name)
	{
		FName Value;
		DC_TRY(Ctx.Reader->ReadName(&Value));
		DC_TRY(Ctx.Writer->WriteString(FJsonObjectConverter::StandardizeCase(Value.ToString())));
    }
}

We aim to support flexible serialization and formatting behaviors without modifying DataConfigCore code: