// Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. #include "VaRestJsonObject.h" #include "VaRestDefines.h" #include "VaRestJsonParser.h" #include "VaRestJsonValue.h" #include "HAL/FileManager.h" #include "Misc/Paths.h" #include "Policies/CondensedJsonPrintPolicy.h" #include "Serialization/JsonSerializer.h" #include "Serialization/JsonWriter.h" typedef TJsonWriterFactory> FCondensedJsonStringWriterFactory; typedef TJsonWriter> FCondensedJsonStringWriter; UVaRestJsonObject::UVaRestJsonObject(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , JsonObj(MakeShared()) { } void UVaRestJsonObject::Reset() { JsonObj = MakeShared(); } TSharedRef& UVaRestJsonObject::GetRootObject() { return JsonObj; } void UVaRestJsonObject::SetRootObject(const TSharedPtr& JsonObject) { if (JsonObject.IsValid()) { JsonObj = JsonObject.ToSharedRef(); } else { UE_LOG(LogVaRest, Error, TEXT("%s: Trying to set invalid json object as root one. Reset now."), *VA_FUNC_LINE); Reset(); } } ////////////////////////////////////////////////////////////////////////// // Serialization FString UVaRestJsonObject::EncodeJson() const { FString OutputString; const auto Writer = TJsonWriterFactory<>::Create(&OutputString); FJsonSerializer::Serialize(JsonObj, Writer); return OutputString; } FString UVaRestJsonObject::EncodeJsonToSingleString() const { FString OutputString; const auto Writer = FCondensedJsonStringWriterFactory::Create(&OutputString); FJsonSerializer::Serialize(JsonObj, Writer); return OutputString; } bool UVaRestJsonObject::DecodeJson(const FString& JsonString, bool bUseIncrementalParser) { if (bUseIncrementalParser) { const int32 BytesRead = DeserializeFromTCHARBytes(JsonString.GetCharArray().GetData(), JsonString.Len()); // JsonObj is always valid, but read bytes is zero when something went wrong if (BytesRead > 0) { return true; } } else { const TSharedRef> Reader = TJsonReaderFactory<>::Create(*JsonString); TSharedPtr OutJsonObj; if (FJsonSerializer::Deserialize(Reader, OutJsonObj)) { JsonObj = OutJsonObj.ToSharedRef(); return true; } } // If we've failed to deserialize the string, we should clear our internal data Reset(); UE_LOG(LogVaRest, Error, TEXT("Json decoding failed for: %s"), *JsonString); return false; } ////////////////////////////////////////////////////////////////////////// // FJsonObject API FString UVaRestJsonObject::GetFieldTypeString(const FString& FieldName) const { if (!JsonObj->HasTypedField(FieldName)) { return TEXT("Null"); } else if (!JsonObj->HasTypedField(FieldName)) { return TEXT("String"); } else if (!JsonObj->HasTypedField(FieldName)) { return TEXT("Number"); } else if (!JsonObj->HasTypedField(FieldName)) { return TEXT("Boolean"); } else if (!JsonObj->HasTypedField(FieldName)) { return TEXT("Object"); } else if (!JsonObj->HasTypedField(FieldName)) { return TEXT("Array"); } UE_LOG(LogVaRest, Warning, TEXT("Field with name %s type unknown"), *FieldName); return ""; } TArray UVaRestJsonObject::GetFieldNames() const { TArray Result; JsonObj->Values.GetKeys(Result); return Result; } bool UVaRestJsonObject::HasField(const FString& FieldName) const { if (FieldName.IsEmpty()) { return false; } return JsonObj->HasField(FieldName); } void UVaRestJsonObject::RemoveField(const FString& FieldName) { if (FieldName.IsEmpty()) { return; } JsonObj->RemoveField(FieldName); } UVaRestJsonValue* UVaRestJsonObject::GetField(const FString& FieldName) const { if (FieldName.IsEmpty()) { return nullptr; } TSharedPtr NewVal = JsonObj->TryGetField(FieldName); if (NewVal.IsValid()) { UVaRestJsonValue* NewValue = NewObject(); NewValue->SetRootValue(NewVal); return NewValue; } return nullptr; } void UVaRestJsonObject::SetField(const FString& FieldName, UVaRestJsonValue* JsonValue) { if (FieldName.IsEmpty()) { return; } JsonObj->SetField(FieldName, JsonValue->GetRootValue()); } ////////////////////////////////////////////////////////////////////////// // FJsonObject API Helpers (easy to use with simple Json objects) float UVaRestJsonObject::GetNumberField(const FString& FieldName) const { if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogVaRest, Warning, TEXT("No field with name %s of type Number"), *FieldName); return 0.0f; } return JsonObj->GetNumberField(FieldName); } void UVaRestJsonObject::SetNumberField(const FString& FieldName, float Number) { if (FieldName.IsEmpty()) { return; } JsonObj->SetNumberField(FieldName, Number); } void UVaRestJsonObject::SetNumberFieldDouble(const FString& FieldName, double Number) { if (FieldName.IsEmpty()) { return; } JsonObj->SetNumberField(FieldName, Number); } int32 UVaRestJsonObject::GetIntegerField(const FString& FieldName) const { if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogVaRest, Warning, TEXT("No field with name %s of type Number"), *FieldName); return 0; } return JsonObj->GetIntegerField(FieldName); } void UVaRestJsonObject::SetIntegerField(const FString& FieldName, int32 Number) { if (FieldName.IsEmpty()) { return; } JsonObj->SetNumberField(FieldName, Number); } int64 UVaRestJsonObject::GetInt64Field(const FString& FieldName) const { if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogVaRest, Warning, TEXT("No field with name %s of type Number"), *FieldName); return 0; } return static_cast(JsonObj->GetNumberField(FieldName)); } void UVaRestJsonObject::SetInt64Field(const FString& FieldName, int64 Number) { if (FieldName.IsEmpty()) { return; } JsonObj->SetNumberField(FieldName, Number); } FString UVaRestJsonObject::GetStringField(const FString& FieldName) const { if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogVaRest, Warning, TEXT("No field with name %s of type String"), *FieldName); return TEXT(""); } return JsonObj->GetStringField(FieldName); } void UVaRestJsonObject::SetStringField(const FString& FieldName, const FString& StringValue) { if (FieldName.IsEmpty()) { return; } JsonObj->SetStringField(FieldName, StringValue); } bool UVaRestJsonObject::GetBoolField(const FString& FieldName) const { if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogVaRest, Warning, TEXT("No field with name %s of type Boolean"), *FieldName); return false; } return JsonObj->GetBoolField(FieldName); } void UVaRestJsonObject::SetBoolField(const FString& FieldName, bool InValue) { if (FieldName.IsEmpty()) { return; } JsonObj->SetBoolField(FieldName, InValue); } TArray UVaRestJsonObject::GetArrayField(const FString& FieldName) const { TArray OutArray; if (FieldName.IsEmpty()) { return OutArray; } if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogVaRest, Warning, TEXT("%s: No field with name %s of type Array"), *VA_FUNC_LINE, *FieldName); return OutArray; } TArray> ValArray = JsonObj->GetArrayField(FieldName); for (auto Value : ValArray) { UVaRestJsonValue* NewValue = NewObject(); NewValue->SetRootValue(Value); OutArray.Add(NewValue); } return OutArray; } void UVaRestJsonObject::SetArrayField(const FString& FieldName, const TArray& InArray) { if (FieldName.IsEmpty()) { return; } TArray> ValArray; // Process input array and COPY original values for (auto InVal : InArray) { if (InVal == nullptr) continue; const TSharedPtr JsonVal = InVal->GetRootValue(); switch (InVal->GetType()) { case EVaJson::None: break; case EVaJson::Null: ValArray.Add(MakeShareable(new FJsonValueNull())); break; case EVaJson::String: ValArray.Add(MakeShareable(new FJsonValueString(JsonVal->AsString()))); break; case EVaJson::Number: ValArray.Add(MakeShareable(new FJsonValueNumber(JsonVal->AsNumber()))); break; case EVaJson::Boolean: ValArray.Add(MakeShareable(new FJsonValueBoolean(JsonVal->AsBool()))); break; case EVaJson::Array: ValArray.Add(MakeShareable(new FJsonValueArray(JsonVal->AsArray()))); break; case EVaJson::Object: ValArray.Add(MakeShareable(new FJsonValueObject(JsonVal->AsObject()))); break; default: break; } } JsonObj->SetArrayField(FieldName, ValArray); } void UVaRestJsonObject::MergeJsonObject(UVaRestJsonObject* InJsonObject, bool Overwrite) { if (!InJsonObject || !InJsonObject->IsValidLowLevel()) { return; } TArray Keys = InJsonObject->GetFieldNames(); for (auto Key : Keys) { if (Overwrite == false && HasField(Key)) { continue; } SetField(Key, InJsonObject->GetField(Key)); } } UVaRestJsonObject* UVaRestJsonObject::GetObjectField(const FString& FieldName) const { if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogVaRest, Warning, TEXT("%s: No field with name %s of type Object"), *VA_FUNC_LINE, *FieldName); return nullptr; } const TSharedPtr JsonObjField = JsonObj->GetObjectField(FieldName); UVaRestJsonObject* OutRestJsonObj = NewObject(); OutRestJsonObj->SetRootObject(JsonObjField); return OutRestJsonObj; } void UVaRestJsonObject::SetObjectField(const FString& FieldName, UVaRestJsonObject* JsonObject) { if (FieldName.IsEmpty() || !JsonObject || !JsonObject->IsValidLowLevel()) { return; } JsonObj->SetObjectField(FieldName, JsonObject->GetRootObject()); } void UVaRestJsonObject::SetMapFields_string(const TMap& Fields) { for (auto& field : Fields) { SetStringField(field.Key, field.Value); } } void UVaRestJsonObject::SetMapFields_uint8(const TMap& Fields) { SetMapFields_Impl(Fields); } void UVaRestJsonObject::SetMapFields_int32(const TMap& Fields) { SetMapFields_Impl(Fields); } void UVaRestJsonObject::SetMapFields_int64(const TMap& Fields) { SetMapFields_Impl(Fields); } void UVaRestJsonObject::SetMapFields_bool(const TMap& Fields) { SetMapFields_Impl(Fields); } ////////////////////////////////////////////////////////////////////////// // Array fields helpers (uniform arrays) TArray UVaRestJsonObject::GetNumberArrayField(const FString& FieldName) const { return GetTypeArrayField(FieldName); } TArray UVaRestJsonObject::GetIntegerArrayField(const FString& FieldName) const { return GetTypeArrayField(FieldName); } void UVaRestJsonObject::SetNumberArrayField(const FString& FieldName, const TArray& NumberArray) { if (FieldName.IsEmpty()) { return; } TArray> EntriesArray; for (auto Number : NumberArray) { EntriesArray.Add(MakeShareable(new FJsonValueNumber(Number))); } JsonObj->SetArrayField(FieldName, EntriesArray); } void UVaRestJsonObject::SetNumberArrayFieldDouble(const FString& FieldName, const TArray& NumberArray) { if (FieldName.IsEmpty()) { return; } TArray> EntriesArray; for (auto Number : NumberArray) { EntriesArray.Add(MakeShareable(new FJsonValueNumber(Number))); } JsonObj->SetArrayField(FieldName, EntriesArray); } TArray UVaRestJsonObject::GetStringArrayField(const FString& FieldName) const { TArray StringArray; if (!JsonObj->HasTypedField(FieldName) || FieldName.IsEmpty()) { UE_LOG(LogVaRest, Warning, TEXT("%s: No field with name %s of type Array"), *VA_FUNC_LINE, *FieldName); return StringArray; } const TArray> JsonArrayValues = JsonObj->GetArrayField(FieldName); for (TArray>::TConstIterator It(JsonArrayValues); It; ++It) { const auto Value = (*It).Get(); if (Value->Type != EJson::String) { UE_LOG(LogVaRest, Error, TEXT("Not String element in array with field name %s"), *FieldName); } StringArray.Add((*It)->AsString()); } return StringArray; } void UVaRestJsonObject::SetStringArrayField(const FString& FieldName, const TArray& StringArray) { if (FieldName.IsEmpty()) { return; } TArray> EntriesArray; for (auto String : StringArray) { EntriesArray.Add(MakeShareable(new FJsonValueString(String))); } JsonObj->SetArrayField(FieldName, EntriesArray); } TArray UVaRestJsonObject::GetBoolArrayField(const FString& FieldName) const { TArray BoolArray; if (!JsonObj->HasTypedField(FieldName) || FieldName.IsEmpty()) { UE_LOG(LogVaRest, Warning, TEXT("%s: No field with name %s of type Array"), *VA_FUNC_LINE, *FieldName); return BoolArray; } const TArray> JsonArrayValues = JsonObj->GetArrayField(FieldName); for (TArray>::TConstIterator It(JsonArrayValues); It; ++It) { const auto Value = (*It).Get(); if (Value->Type != EJson::Boolean) { UE_LOG(LogVaRest, Error, TEXT("Not Boolean element in array with field name %s"), *FieldName); } BoolArray.Add((*It)->AsBool()); } return BoolArray; } void UVaRestJsonObject::SetBoolArrayField(const FString& FieldName, const TArray& BoolArray) { if (FieldName.IsEmpty()) { return; } TArray> EntriesArray; for (auto Boolean : BoolArray) { EntriesArray.Add(MakeShareable(new FJsonValueBoolean(Boolean))); } JsonObj->SetArrayField(FieldName, EntriesArray); } TArray UVaRestJsonObject::GetObjectArrayField(const FString& FieldName) const { TArray OutArray; if (!JsonObj->HasTypedField(FieldName) || FieldName.IsEmpty()) { UE_LOG(LogVaRest, Warning, TEXT("%s: No field with name %s of type Array"), *VA_FUNC_LINE, *FieldName); return OutArray; } TArray> ValArray = JsonObj->GetArrayField(FieldName); for (const auto& Value : ValArray) { if (Value->Type != EJson::Object) { UE_LOG(LogVaRest, Error, TEXT("Not Object element in array with field name %s"), *FieldName); } TSharedPtr NewObj = Value->AsObject(); UVaRestJsonObject* NewJson = NewObject(); NewJson->SetRootObject(NewObj); OutArray.Add(NewJson); } return OutArray; } void UVaRestJsonObject::SetObjectArrayField(const FString& FieldName, const TArray& ObjectArray) { if (FieldName.IsEmpty()) { return; } TArray> EntriesArray; for (auto Value : ObjectArray) { if (Value == nullptr) continue; EntriesArray.Add(MakeShareable(new FJsonValueObject(Value->GetRootObject()))); } JsonObj->SetArrayField(FieldName, EntriesArray); } ////////////////////////////////////////////////////////////////////////// // Deserialize int32 UVaRestJsonObject::DeserializeFromUTF8Bytes(const ANSICHAR* Bytes, int32 Size) { FJSONReader Reader; const ANSICHAR* EndByte = Bytes + Size; while (Bytes < EndByte) { TCHAR Char = FUtf8Helper::CodepointFromUtf8(Bytes, EndByte - Bytes); if (Char > 0xFFFF) { Char = UNICODE_BOGUS_CHAR_CODEPOINT; } if (!Reader.Read(Char)) { break; } } SetRootObject(Reader.State.Root); return Reader.State.Size; } int32 UVaRestJsonObject::DeserializeFromTCHARBytes(const TCHAR* Bytes, int32 Size) { FJSONReader Reader; int32 i = 0; while (i < Size) { if (!Reader.Read(Bytes[i++])) { break; } } SetRootObject(Reader.State.Root); return Reader.State.Size; } void UVaRestJsonObject::DecodeFromArchive(TUniquePtr& Reader) { FArchive& Ar = (*Reader.Get()); uint8 SymbolBytes[2]; // Read first two bytes Ar << SymbolBytes[0]; Ar << SymbolBytes[1]; bool bIsIntelByteOrder = true; if (SymbolBytes[0] == 0xff && SymbolBytes[1] == 0xfe) { // Unicode Intel byte order. Less 1 for the FFFE header, additional 1 for null terminator. bIsIntelByteOrder = true; } else if (SymbolBytes[0] == 0xfe && SymbolBytes[1] == 0xff) { // Unicode non-Intel byte order. Less 1 for the FFFE header, additional 1 for null terminator. bIsIntelByteOrder = false; } FJSONReader JsonReader; TCHAR Char; while (!Ar.AtEnd()) { Ar << SymbolBytes[0]; if (Ar.AtEnd()) { break; } Ar << SymbolBytes[1]; if (bIsIntelByteOrder) { Char = CharCast(static_cast(static_cast(SymbolBytes[0]) + static_cast(SymbolBytes[1]) * 256)); } else { Char = CharCast(static_cast(static_cast(SymbolBytes[1]) + static_cast(SymbolBytes[0]) * 256)); } if (!JsonReader.Read(Char)) { break; } } SetRootObject(JsonReader.State.Root); if (!Ar.Close()) { UE_LOG(LogVaRest, Error, TEXT("UVaRestJsonObject::DecodeFromArchive: Error! Can't close file!")); } } ////////////////////////////////////////////////////////////////////////// // Serialize bool UVaRestJsonObject::WriteToFile(const FString& Path) const { TUniquePtr FileWriter(IFileManager::Get().CreateFileWriter(*Path)); if (!FileWriter) { return false; } FArchive& Ar = *FileWriter.Get(); UCS2CHAR BOM = UNICODE_BOM; Ar.Serialize(&BOM, sizeof(UCS2CHAR)); FString Str = FString(TEXT("{")); WriteStringToArchive(Ar, *Str, Str.Len()); int32 ElementCount = 0; FJSONWriter JsonWriter; for (auto JsonObjectValuePair : JsonObj->Values) { Str = FString(TEXT("\"")); WriteStringToArchive(Ar, *Str, Str.Len()); const TCHAR* BufferPtr = *JsonObjectValuePair.Key; for (int i = 0; i < JsonObjectValuePair.Key.Len(); ++i) { Str = FString(1, &BufferPtr[i]); WriteStringToArchive(Ar, *Str, Str.Len()); } Str = FString(TEXT("\"")); WriteStringToArchive(Ar, *Str, Str.Len()); Str = FString(TEXT(":")); WriteStringToArchive(Ar, *Str, Str.Len()); ++ElementCount; JsonWriter.Write(JsonObjectValuePair.Value, FileWriter.Get(), ElementCount >= JsonObj->Values.Num()); } Str = FString(TEXT("}")); WriteStringToArchive(Ar, *Str, Str.Len()); FileWriter->Close(); return true; } bool UVaRestJsonObject::WriteToFilePath(const FString& Path, const bool bIsRelativeToProjectDir) { return WriteToFile(bIsRelativeToProjectDir ? FPaths::ProjectDir() / Path : Path); } bool UVaRestJsonObject::WriteStringToArchive(FArchive& Ar, const TCHAR* StrPtr, int64 Len) { const auto Src = StringCast(StrPtr, Len); Ar.Serialize(const_cast(Src.Get()), Src.Length() * sizeof(UCS2CHAR)); return true; }