// Modifications Copyright 2018-current Getnamo. All Rights Reserved // Copyright 2014 Vladimir Alyamkin. All Rights Reserved. #include "SIOJsonValue.h" #include "SIOJConvert.h" #include "SIOJsonObject.h" #include "ISIOJson.h" #include "Misc/Base64.h" #if PLATFORM_WINDOWS #pragma region FJsonValueBinary #endif TArray FJsonValueBinary::AsBinary(const TSharedPtr& InJsonValue) { if (FJsonValueBinary::IsBinary(InJsonValue)) { TSharedPtr BinaryValue = StaticCastSharedPtr(InJsonValue); return BinaryValue->AsBinary(); } else if (InJsonValue->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(InJsonValue->AsString(), DecodedArray); if (!bDidDecodeCorrectly) { UE_LOG(LogSIOJ, Warning, TEXT("FJsonValueBinary::AsBinary couldn't decode %s as a binary."), *InJsonValue->AsString()); } return DecodedArray; } else { TArray EmptyArray; return EmptyArray; } } bool FJsonValueBinary::IsBinary(const TSharedPtr& InJsonValue) { if (!InJsonValue.IsValid()) { return false; } //use our hackery to determine if we got a binary string bool IgnoreBool; return !InJsonValue->TryGetBool(IgnoreBool); } #if PLATFORM_WINDOWS #pragma endregion FJsonValueBinary #pragma region USIOJsonValue #endif USIOJsonValue::USIOJsonValue(const class FObjectInitializer& PCIP) : Super(PCIP) { } USIOJsonValue* USIOJsonValue::ConstructJsonValueNumber(UObject* WorldContextObject, float Number) { TSharedPtr NewVal = MakeShareable(new FJsonValueNumber(Number)); USIOJsonValue* NewValue = NewObject(); NewValue->SetRootValue(NewVal); return NewValue; } USIOJsonValue* USIOJsonValue::ConstructJsonValueString(UObject* WorldContextObject, const FString& StringValue) { TSharedPtr NewVal = MakeShareable(new FJsonValueString(StringValue)); USIOJsonValue* NewValue = NewObject(); NewValue->SetRootValue(NewVal); return NewValue; } USIOJsonValue* USIOJsonValue::ConstructJsonValueBool(UObject* WorldContextObject, bool InValue) { TSharedPtr NewVal = MakeShareable(new FJsonValueBoolean(InValue)); USIOJsonValue* NewValue = NewObject(); NewValue->SetRootValue(NewVal); return NewValue; } USIOJsonValue* USIOJsonValue::ConstructJsonValueArray(UObject* WorldContextObject, const TArray& InArray) { // Prepare data array to create new value TArray< TSharedPtr > ValueArray; for (auto InVal : InArray) { ValueArray.Add(InVal->GetRootValue()); } TSharedPtr NewVal = MakeShareable(new FJsonValueArray(ValueArray)); USIOJsonValue* NewValue = NewObject(); NewValue->SetRootValue(NewVal); return NewValue; } USIOJsonValue* USIOJsonValue::ConstructJsonValueObject(USIOJsonObject *JsonObject, UObject* WorldContextObject) { TSharedPtr NewVal = MakeShareable(new FJsonValueObject(JsonObject->GetRootObject())); USIOJsonValue* NewValue = NewObject(); NewValue->SetRootValue(NewVal); return NewValue; } USIOJsonValue* USIOJsonValue::ConstructJsonValueBinary(UObject* WorldContextObject, TArray ByteArray) { TSharedPtr NewVal = MakeShareable(new FJsonValueBinary(ByteArray)); USIOJsonValue* NewValue = NewObject(); NewValue->SetRootValue(NewVal); return NewValue; } USIOJsonValue* USIOJsonValue::ConstructJsonValue(UObject* WorldContextObject, const TSharedPtr& InValue) { TSharedPtr NewVal = InValue; USIOJsonValue* NewValue = NewObject(); NewValue->SetRootValue(NewVal); return NewValue; } USIOJsonValue* USIOJsonValue::ValueFromJsonString(UObject* WorldContextObject, const FString& StringValue) { TSharedPtr NewVal = USIOJConvert::JsonStringToJsonValue(StringValue); USIOJsonValue* NewValue = NewObject(); NewValue->SetRootValue(NewVal); return NewValue; } TSharedPtr& USIOJsonValue::GetRootValue() { return JsonVal; } void USIOJsonValue::SetRootValue(TSharedPtr& JsonValue) { JsonVal = JsonValue; } ////////////////////////////////////////////////////////////////////////// // FJsonValue API ESIOJson::Type USIOJsonValue::GetType() const { if (!JsonVal.IsValid()) { return ESIOJson::None; } switch (JsonVal->Type) { case EJson::None: return ESIOJson::None; case EJson::Null: return ESIOJson::Null; case EJson::String: if (FJsonValueBinary::IsBinary(JsonVal)) { return ESIOJson::Binary; } else { return ESIOJson::String; } case EJson::Number: return ESIOJson::Number; case EJson::Boolean: return ESIOJson::Boolean; case EJson::Array: return ESIOJson::Array; case EJson::Object: return ESIOJson::Object; default: return ESIOJson::None; } } FString USIOJsonValue::GetTypeString() const { if (!JsonVal.IsValid()) { return "None"; } switch (JsonVal->Type) { case EJson::None: return TEXT("None"); case EJson::Null: return TEXT("Null"); case EJson::String: return TEXT("String"); case EJson::Number: return TEXT("Number"); case EJson::Boolean: return TEXT("Boolean"); case EJson::Array: return TEXT("Array"); case EJson::Object: return TEXT("Object"); default: return TEXT("None"); } } bool USIOJsonValue::IsNull() const { if (!JsonVal.IsValid()) { return true; } return JsonVal->IsNull(); } float USIOJsonValue::AsNumber() const { if (!JsonVal.IsValid()) { ErrorMessage(TEXT("Number")); return 0.f; } return JsonVal->AsNumber(); } FString USIOJsonValue::AsString() const { if (!JsonVal.IsValid()) { ErrorMessage(TEXT("String")); return FString(); } //Auto-convert non-strings instead of getting directly if (JsonVal->Type != EJson::String) { return EncodeJson(); } else { return JsonVal->AsString(); } } bool USIOJsonValue::AsBool() const { if (!JsonVal.IsValid()) { ErrorMessage(TEXT("Boolean")); return false; } return JsonVal->AsBool(); } TArray USIOJsonValue::AsArray() const { TArray OutArray; if (!JsonVal.IsValid()) { ErrorMessage(TEXT("Array")); return OutArray; } TArray< TSharedPtr > ValArray = JsonVal->AsArray(); for (auto Value : ValArray) { USIOJsonValue* NewValue = NewObject(); NewValue->SetRootValue(Value); OutArray.Add(NewValue); } return OutArray; } USIOJsonObject* USIOJsonValue::AsObject() { if (!JsonVal.IsValid()) { ErrorMessage(TEXT("Object")); return nullptr; } TSharedPtr NewObj = JsonVal->AsObject(); USIOJsonObject* JsonObj = NewObject(); JsonObj->SetRootObject(NewObj); return JsonObj; } TArray USIOJsonValue::AsBinary() { if (!JsonVal.IsValid()) { ErrorMessage(TEXT("Binary")); TArray ByteArray; return ByteArray; } //binary object pretending & starts with non-json format? it's our disguise binary if (JsonVal->Type == EJson::String) { //it's a legit binary if (FJsonValueBinary::IsBinary(JsonVal)) { //Valid binary available return FJsonValueBinary::AsBinary(JsonVal); } //It's a string, decode as if hex encoded binary else { const FString& HexString = JsonVal->AsString(); TArray ByteArray; ByteArray.AddUninitialized(HexString.Len() / 2); bool DidConvert = FString::ToHexBlob(HexString, ByteArray.GetData(), ByteArray.Num()); //Empty our array if conversion failed if (!DidConvert) { ByteArray.Empty(); } return ByteArray; } } //Not a binary nor binary string, return empty array else { //Empty array TArray ByteArray; return ByteArray; } } FString USIOJsonValue::EncodeJson() const { return USIOJConvert::ToJsonString(JsonVal); } ////////////////////////////////////////////////////////////////////////// // Helpers void USIOJsonValue::ErrorMessage(const FString& InType) const { UE_LOG(LogSIOJ, Error, TEXT("Json Value of type '%s' used as a '%s'."), *GetTypeString(), *InType); } #if PLATFORM_WINDOWS #pragma endregion USIOJsonValue #endif