// Modifications Copyright 2018-current Getnamo. All Rights Reserved // Copyright 2014 Vladimir Alyamkin. All Rights Reserved. #include "SIOJsonObject.h" #include "SIOJsonValue.h" #include "ISIOJson.h" #include "Misc/Base64.h" #include "Policies/CondensedJsonPrintPolicy.h" #include "Serialization/JsonWriter.h" #include "Serialization/JsonSerializer.h" typedef TJsonWriterFactory< TCHAR, TCondensedJsonPrintPolicy > FCondensedJsonStringWriterFactory; typedef TJsonWriter< TCHAR, TCondensedJsonPrintPolicy > FCondensedJsonStringWriter; USIOJsonObject::USIOJsonObject(const class FObjectInitializer& PCIP) : Super(PCIP) { Reset(); } USIOJsonObject* USIOJsonObject::ConstructJsonObject(UObject* WorldContextObject) { return NewObject(); } void USIOJsonObject::Reset() { if (JsonObj.IsValid()) { JsonObj.Reset(); } JsonObj = MakeShareable(new FJsonObject()); } TSharedPtr& USIOJsonObject::GetRootObject() { return JsonObj; } void USIOJsonObject::SetRootObject(const TSharedPtr& JsonObject) { JsonObj = JsonObject; } ////////////////////////////////////////////////////////////////////////// // Serialization FString USIOJsonObject::EncodeJson() const { if (!JsonObj.IsValid()) { return TEXT(""); } FString OutputString; TSharedRef< FCondensedJsonStringWriter > Writer = FCondensedJsonStringWriterFactory::Create(&OutputString); FJsonSerializer::Serialize(JsonObj.ToSharedRef(), Writer); return OutputString; } FString USIOJsonObject::EncodeJsonToSingleString() const { FString OutputString = EncodeJson(); // Remove line terminators (void)OutputString.Replace(LINE_TERMINATOR, TEXT("")); // Remove tabs (void)OutputString.Replace(LINE_TERMINATOR, TEXT("\t")); return OutputString; } bool USIOJsonObject::DecodeJson(const FString& JsonString) { TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(*JsonString); if (FJsonSerializer::Deserialize(Reader, JsonObj) && JsonObj.IsValid()) { return true; } // If we've failed to deserialize the string, we should clear our internal data Reset(); UE_LOG(LogSIOJ, Error, TEXT("Json decoding failed for: %s"), *JsonString); return false; } ////////////////////////////////////////////////////////////////////////// // FJsonObject API TArray USIOJsonObject::GetFieldNames() { TArray Result; if (!JsonObj.IsValid()) { return Result; } JsonObj->Values.GetKeys(Result); return Result; } bool USIOJsonObject::HasField(const FString& FieldName) const { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return false; } return JsonObj->HasField(FieldName); } void USIOJsonObject::RemoveField(const FString& FieldName) { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return; } JsonObj->RemoveField(FieldName); } USIOJsonValue* USIOJsonObject::GetField(const FString& FieldName) const { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return nullptr; } TSharedPtr NewVal = JsonObj->TryGetField(FieldName); if (NewVal.IsValid()) { USIOJsonValue* NewValue = NewObject(); NewValue->SetRootValue(NewVal); return NewValue; } return nullptr; } void USIOJsonObject::SetField(const FString& FieldName, USIOJsonValue* JsonValue) { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return; } JsonObj->SetField(FieldName, JsonValue->GetRootValue()); } ////////////////////////////////////////////////////////////////////////// // FJsonObject API Helpers (easy to use with simple Json objects) bool USIOJsonObject::TryGetNumberField(const FString& FieldName, float& OutNumber) const { return JsonObj.IsValid() && JsonObj->TryGetNumberField(FieldName, OutNumber); } float USIOJsonObject::GetNumberField(const FString& FieldName) const { if (!JsonObj.IsValid() || !JsonObj->HasTypedField(FieldName)) { UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Number"), *FieldName); return 0.0f; } return JsonObj->GetNumberField(FieldName); } void USIOJsonObject::SetNumberField(const FString& FieldName, float Number) { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return; } JsonObj->SetNumberField(FieldName, Number); } bool USIOJsonObject::TryGetStringField(const FString& FieldName, FString& OutString) const { return JsonObj.IsValid() && JsonObj->TryGetStringField(FieldName, OutString); } FString USIOJsonObject::GetStringField(const FString& FieldName) const { if (!JsonObj.IsValid() || !JsonObj->HasTypedField(FieldName)) { UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type String"), *FieldName); return TEXT(""); } return JsonObj->GetStringField(FieldName); } void USIOJsonObject::SetStringField(const FString& FieldName, const FString& StringValue) { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return; } JsonObj->SetStringField(FieldName, StringValue); } bool USIOJsonObject::TryGetBoolField(const FString& FieldName, bool& OutBool) const { return JsonObj.IsValid() && JsonObj->TryGetBoolField(FieldName, OutBool); } bool USIOJsonObject::GetBoolField(const FString& FieldName) const { if (!JsonObj.IsValid() || !JsonObj->HasTypedField(FieldName)) { UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Boolean"), *FieldName); return false; } return JsonObj->GetBoolField(FieldName); } void USIOJsonObject::SetBoolField(const FString& FieldName, bool InValue) { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return; } JsonObj->SetBoolField(FieldName, InValue); } TArray USIOJsonObject::GetArrayField(const FString& FieldName) { if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Array"), *FieldName); } TArray OutArray; if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return OutArray; } TArray< TSharedPtr > ValArray = JsonObj->GetArrayField(FieldName); for (auto Value : ValArray) { USIOJsonValue* NewValue = NewObject(); NewValue->SetRootValue(Value); OutArray.Add(NewValue); } return OutArray; } void USIOJsonObject::SetArrayField(const FString& FieldName, const TArray& InArray) { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return; } TArray< TSharedPtr > ValArray; // Process input array and COPY original values for (auto InVal : InArray) { TSharedPtr JsonVal = InVal->GetRootValue(); switch (InVal->GetType()) { case ESIOJson::None: break; case ESIOJson::Null: ValArray.Add(MakeShareable(new FJsonValueNull())); break; case ESIOJson::String: ValArray.Add(MakeShareable(new FJsonValueString(JsonVal->AsString()))); break; case ESIOJson::Number: ValArray.Add(MakeShareable(new FJsonValueNumber(JsonVal->AsNumber()))); break; case ESIOJson::Boolean: ValArray.Add(MakeShareable(new FJsonValueBoolean(JsonVal->AsBool()))); break; case ESIOJson::Array: ValArray.Add(MakeShareable(new FJsonValueArray(JsonVal->AsArray()))); break; case ESIOJson::Object: ValArray.Add(MakeShareable(new FJsonValueObject(JsonVal->AsObject()))); break; default: break; } } JsonObj->SetArrayField(FieldName, ValArray); } void USIOJsonObject::MergeJsonObject(USIOJsonObject* InJsonObject, bool Overwrite) { TArray Keys = InJsonObject->GetFieldNames(); for (auto Key : Keys) { if (Overwrite == false && HasField(Key)) { continue; } SetField(Key, InJsonObject->GetField(Key)); } } bool USIOJsonObject::TryGetObjectField(const FString& FieldName, USIOJsonObject*& OutObject) const { if (!JsonObj.IsValid()) { return false; } const TSharedPtr* JsonObjField; bool bSuccess = JsonObj->TryGetObjectField(FieldName, JsonObjField); if (bSuccess) { OutObject = NewObject(); OutObject->SetRootObject(*JsonObjField); } return bSuccess; } USIOJsonObject* USIOJsonObject::GetObjectField(const FString& FieldName) const { if (!JsonObj.IsValid() || !JsonObj->HasTypedField(FieldName)) { UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Object"), *FieldName); return nullptr; } TSharedPtr JsonObjField = JsonObj->GetObjectField(FieldName); USIOJsonObject* OutRestJsonObj = NewObject(); OutRestJsonObj->SetRootObject(JsonObjField); return OutRestJsonObj; } void USIOJsonObject::SetObjectField(const FString& FieldName, USIOJsonObject* JsonObject) { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return; } JsonObj->SetObjectField(FieldName, JsonObject->GetRootObject()); } void USIOJsonObject::GetBinaryField(const FString& FieldName, TArray& OutBinary) const { if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type String"), *FieldName); } TSharedPtr JsonValue = JsonObj->TryGetField(FieldName); if (!JsonValue) { UE_LOG(LogSIOJ, Warning, TEXT("JsonValue is null for %s, aborting parse."), *FieldName); return; } if (FJsonValueBinary::IsBinary(JsonValue)) { OutBinary = FJsonValueBinary::AsBinary(JsonValue); } else if (JsonValue->Type == EJson::String) { //If we got a string that isn't detected as a binary via socket.io protocol hack //then we need to decode this string as base 64 TArray DecodedArray; bool bDidDecodeCorrectly = FBase64::Decode(JsonValue->AsString(), DecodedArray); if (!bDidDecodeCorrectly) { UE_LOG(LogSIOJ, Warning, TEXT("USIOJsonObject::GetBinaryField couldn't decode %s as a binary."), *JsonValue->AsString()); } OutBinary = DecodedArray; } else { TArray EmptyArray; OutBinary = EmptyArray; } } void USIOJsonObject::SetBinaryField(const FString& FieldName, const TArray& Bytes) { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return; } TSharedPtr JsonValue = MakeShareable(new FJsonValueBinary(Bytes)); JsonObj->SetField(FieldName, JsonValue); } ////////////////////////////////////////////////////////////////////////// // Array fields helpers (uniform arrays) TArray USIOJsonObject::GetNumberArrayField(const FString& FieldName) { if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Array"), *FieldName); } TArray NumberArray; if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return NumberArray; } TArray > JsonArrayValues = JsonObj->GetArrayField(FieldName); for (TArray >::TConstIterator It(JsonArrayValues); It; ++It) { auto Value = (*It).Get(); if (Value->Type != EJson::Number) { UE_LOG(LogSIOJ, Error, TEXT("Not Number element in array with field name %s"), *FieldName); } NumberArray.Add((*It)->AsNumber()); } return NumberArray; } void USIOJsonObject::SetNumberArrayField(const FString& FieldName, const TArray& NumberArray) { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return; } TArray< TSharedPtr > EntriesArray; for (auto Number : NumberArray) { EntriesArray.Add(MakeShareable(new FJsonValueNumber(Number))); } JsonObj->SetArrayField(FieldName, EntriesArray); } TArray USIOJsonObject::GetStringArrayField(const FString& FieldName) { if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Array"), *FieldName); } TArray StringArray; if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return StringArray; } TArray > JsonArrayValues = JsonObj->GetArrayField(FieldName); for (TArray >::TConstIterator It(JsonArrayValues); It; ++It) { auto Value = (*It).Get(); if (Value->Type != EJson::String) { UE_LOG(LogSIOJ, Error, TEXT("Not String element in array with field name %s"), *FieldName); } StringArray.Add((*It)->AsString()); } return StringArray; } void USIOJsonObject::SetStringArrayField(const FString& FieldName, const TArray& StringArray) { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return; } TArray< TSharedPtr > EntriesArray; for (auto String : StringArray) { EntriesArray.Add(MakeShareable(new FJsonValueString(String))); } JsonObj->SetArrayField(FieldName, EntriesArray); } TArray USIOJsonObject::GetBoolArrayField(const FString& FieldName) { if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Array"), *FieldName); } TArray BoolArray; if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return BoolArray; } TArray > JsonArrayValues = JsonObj->GetArrayField(FieldName); for (TArray >::TConstIterator It(JsonArrayValues); It; ++It) { auto Value = (*It).Get(); if (Value->Type != EJson::Boolean) { UE_LOG(LogSIOJ, Error, TEXT("Not Boolean element in array with field name %s"), *FieldName); } BoolArray.Add((*It)->AsBool()); } return BoolArray; } void USIOJsonObject::SetBoolArrayField(const FString& FieldName, const TArray& BoolArray) { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return; } TArray< TSharedPtr > EntriesArray; for (auto Boolean : BoolArray) { EntriesArray.Add(MakeShareable(new FJsonValueBoolean(Boolean))); } JsonObj->SetArrayField(FieldName, EntriesArray); } TArray USIOJsonObject::GetObjectArrayField(const FString& FieldName) { if (!JsonObj->HasTypedField(FieldName)) { UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Array"), *FieldName); } TArray OutArray; if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return OutArray; } TArray< TSharedPtr > ValArray = JsonObj->GetArrayField(FieldName); for (auto Value : ValArray) { if (Value->Type != EJson::Object) { UE_LOG(LogSIOJ, Error, TEXT("Not Object element in array with field name %s"), *FieldName); } TSharedPtr NewObj = Value->AsObject(); USIOJsonObject* NewJson = NewObject(); NewJson->SetRootObject(NewObj); OutArray.Add(NewJson); } return OutArray; } void USIOJsonObject::SetObjectArrayField(const FString& FieldName, const TArray& ObjectArray) { if (!JsonObj.IsValid() || FieldName.IsEmpty()) { return; } TArray< TSharedPtr > EntriesArray; for (auto Value : ObjectArray) { EntriesArray.Add(MakeShareable(new FJsonValueObject(Value->GetRootObject()))); } JsonObj->SetArrayField(FieldName, EntriesArray); }