// Copyright Epic Games, Inc. All Rights Reserved. #if WITH_TESTS #include "CoreMinimal.h" #include "JsonUtils/JsonConversion.h" #include "Misc/AutomationTest.h" #include "Serialization/JsonSerializer.h" #include "Tests/TestHarnessAdapter.h" #if WITH_LOW_LEVEL_TESTS #include "TestCommon/Expectations.h" #endif TEST_CASE_NAMED(FJsonConversionAutomationTest, "System::Engine::FileSystem::JSON::Conversion", "[ApplicationContextMask][SmokeFilter]") { const TCHAR* JsonText = TEXT(R"json( { "int": 1234, "round_down": 1.1, "round_up" : 1.9, "string": "hello world", "on": true, "off": false, "object": { "value": 42 }, "array": [ "aaa", "bbb", "ccc", "ddd" ], "null": null } )json"); // Checks to make a RapidJson -> default Json conversion is identical auto CheckEqual = [&](const TSharedPtr& JsonObjectA, const UE::Json::FDocument& RapidJsonDocument) { TOptional RootObject = UE::Json::GetRootObject(RapidJsonDocument); REQUIRE(RootObject.IsSet()); TSharedPtr JsonObjectB = UE::Json::ConvertRapidJsonToSharedJsonObject(*RootObject); REQUIRE(JsonObjectB); TSharedPtr IntValueA = JsonObjectA->TryGetField(TEXT("int")); TSharedPtr IntValueB = JsonObjectB->TryGetField(TEXT("int")); REQUIRE(IntValueA); REQUIRE(IntValueB); REQUIRE(IntValueA->Type == EJson::Number); REQUIRE(IntValueB->Type == EJson::Number); REQUIRE(IntValueA->AsNumber() == IntValueB->AsNumber()); TSharedPtr RoundDownValueA = JsonObjectA->TryGetField(TEXT("round_down")); TSharedPtr RoundDownValueB = JsonObjectB->TryGetField(TEXT("round_down")); REQUIRE(RoundDownValueA); REQUIRE(RoundDownValueB); REQUIRE(RoundDownValueA->Type == EJson::Number); REQUIRE(RoundDownValueB->Type == EJson::Number); REQUIRE(RoundDownValueA->AsNumber() == RoundDownValueB->AsNumber()); // default JSON does some rounding when converting, ensure that is kept during the conversion int32 RoundDownA = 0; int32 RoundDownB = 0; REQUIRE(RoundDownValueA->TryGetNumber(RoundDownA)); REQUIRE(RoundDownValueB->TryGetNumber(RoundDownB)); REQUIRE(RoundDownA == RoundDownB); TSharedPtr RoundUpValueA = JsonObjectA->TryGetField(TEXT("round_down")); TSharedPtr RoundUpValueB = JsonObjectB->TryGetField(TEXT("round_down")); REQUIRE(RoundUpValueA); REQUIRE(RoundUpValueB); REQUIRE(RoundUpValueA->Type == EJson::Number); REQUIRE(RoundUpValueB->Type == EJson::Number); REQUIRE(RoundUpValueA->AsNumber() == RoundUpValueB->AsNumber()); // default JSON does some rounding when converting from float to integer, ensure that is kept during the conversion int32 RoundUpA = 0; int32 RoundUpB = 0; REQUIRE(RoundUpValueA->TryGetNumber(RoundUpA)); REQUIRE(RoundUpValueB->TryGetNumber(RoundUpB)); REQUIRE(RoundUpA == RoundUpB); TSharedPtr StringValueA = JsonObjectA->TryGetField(TEXT("string")); TSharedPtr StringValueB = JsonObjectB->TryGetField(TEXT("string")); REQUIRE(StringValueA); REQUIRE(StringValueB); REQUIRE(StringValueA->Type == EJson::String); REQUIRE(StringValueB->Type == EJson::String); REQUIRE(StringValueA->AsString() == StringValueB->AsString()); TSharedPtr OnValueA = JsonObjectA->TryGetField(TEXT("on")); TSharedPtr OnValueB = JsonObjectB->TryGetField(TEXT("on")); REQUIRE(OnValueA); REQUIRE(OnValueB); REQUIRE(OnValueA->Type == EJson::Boolean); REQUIRE(OnValueB->Type == EJson::Boolean); REQUIRE(OnValueA->AsBool() == OnValueB->AsBool()); TSharedPtr OffValueA = JsonObjectA->TryGetField(TEXT("off")); TSharedPtr OffValueB = JsonObjectB->TryGetField(TEXT("off")); REQUIRE(OffValueA); REQUIRE(OffValueB); REQUIRE(OffValueA->Type == EJson::Boolean); REQUIRE(OffValueB->Type == EJson::Boolean); REQUIRE(OffValueA->AsBool() == OffValueB->AsBool()); TSharedPtr ObjectValueA = JsonObjectA->TryGetField(TEXT("object")); TSharedPtr ObjectValueB = JsonObjectB->TryGetField(TEXT("object")); REQUIRE(ObjectValueA); REQUIRE(ObjectValueB); REQUIRE(ObjectValueA->Type == EJson::Object); REQUIRE(ObjectValueB->Type == EJson::Object); TSharedPtr ObjectA = ObjectValueA->AsObject(); TSharedPtr ObjectB = ObjectValueB->AsObject(); REQUIRE(ObjectA); REQUIRE(ObjectB); TSharedPtr ObjectFieldA = ObjectA->TryGetField(TEXT("value")); TSharedPtr ObjectFieldB = ObjectB->TryGetField(TEXT("value")); REQUIRE(ObjectFieldA); REQUIRE(ObjectFieldB); REQUIRE(ObjectFieldA->Type == EJson::Number); REQUIRE(ObjectFieldB->Type == EJson::Number); REQUIRE(ObjectFieldA->AsNumber() == ObjectFieldB->AsNumber()); TSharedPtr ArrayValueA = JsonObjectA->TryGetField(TEXT("array")); TSharedPtr ArrayValueB = JsonObjectB->TryGetField(TEXT("array")); REQUIRE(ArrayValueA); REQUIRE(ArrayValueB); REQUIRE(ArrayValueA->Type == EJson::Array); REQUIRE(ArrayValueB->Type == EJson::Array); TArray> ArrayA = ArrayValueA->AsArray(); TArray> ArrayB = ArrayValueB->AsArray(); REQUIRE(!ArrayA.IsEmpty()); REQUIRE(!ArrayB.IsEmpty()); REQUIRE(ArrayA.Num() == ArrayB.Num()); for (int32 Idx = 0; Idx < ArrayA.Num(); ++Idx) { const TSharedPtr& ItemA = ArrayA[Idx]; const TSharedPtr& ItemB = ArrayB[Idx]; REQUIRE(ItemA); REQUIRE(ItemB); REQUIRE(ItemA->Type == EJson::String); REQUIRE(ItemB->Type == EJson::String); REQUIRE(ItemA->AsString() == ItemB->AsString()); } }; TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(JsonText); TSharedPtr< FJsonObject > DefaultLoadedJson; REQUIRE(FJsonSerializer::Deserialize(Reader, DefaultLoadedJson)); // convert to RapidJson TOptional ConvertedDocument = UE::Json::ConvertSharedJsonToRapidJsonDocument(*DefaultLoadedJson); REQUIRE(ConvertedDocument.IsSet()); CheckEqual(DefaultLoadedJson, *ConvertedDocument); TValueOrError LoadedDocument = UE::Json::Parse(JsonText); FString ErrorMessage = LoadedDocument.HasError() ? LoadedDocument.GetError().CreateMessage(JsonText) : FString(); REQUIRE_MESSAGE(ErrorMessage, LoadedDocument.HasValue()); CheckEqual(DefaultLoadedJson, LoadedDocument.GetValue()); } #endif // WITH_TESTS