SIOJConvert.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083
  1. // Copyright 2018-current Getnamo. All Rights Reserved
  2. #include "SIOJConvert.h"
  3. //#include "Json.h"
  4. #include "UObject/TextProperty.h"
  5. #include "JsonGlobals.h"
  6. #include "Policies/CondensedJsonPrintPolicy.h"
  7. #include "Misc/FileHelper.h"
  8. #include "SIOJsonValue.h"
  9. #include "SIOJsonObject.h"
  10. #include "JsonObjectConverter.h"
  11. #include "UObject/PropertyPortFlags.h"
  12. #include "Misc/Base64.h"
  13. typedef TJsonWriterFactory< TCHAR, TCondensedJsonPrintPolicy<TCHAR> > FCondensedJsonStringWriterFactory;
  14. typedef TJsonWriter< TCHAR, TCondensedJsonPrintPolicy<TCHAR> > FCondensedJsonStringWriter;
  15. //The one key that will break
  16. #define TMAP_STRING TEXT("!__!INTERNAL_TMAP")
  17. namespace
  18. {
  19. FJsonObjectConverter::CustomExportCallback EnumOverrideExportCallback;
  20. //Begin partial copy of FJsonObjectConverter for BP enum workaround
  21. bool JsonValueToFPropertyWithContainer(const TSharedPtr<FJsonValue>& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);
  22. bool JsonAttributesToUStructWithContainer(const TMap< FString, TSharedPtr<FJsonValue> >& JsonAttributes, const UStruct* StructDefinition, void* OutStruct, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);
  23. /** Convert JSON to property, assuming either the property is not an array or the value is an individual array element */
  24. bool ConvertScalarJsonValueToFPropertyWithContainer(const TSharedPtr<FJsonValue>& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)
  25. {
  26. if (FEnumProperty* EnumProperty = CastField<FEnumProperty>(Property))
  27. {
  28. if (JsonValue->Type == EJson::String)
  29. {
  30. // see if we were passed a string for the enum
  31. const UEnum* Enum = EnumProperty->GetEnum();
  32. check(Enum);
  33. FString StrValue = JsonValue->AsString();
  34. int64 IntValue = Enum->GetValueByName(FName(*StrValue));
  35. if (IntValue == INDEX_NONE)
  36. {
  37. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable import enum %s from string value %s for property %s"), *Enum->CppType, *StrValue, *Property->GetNameCPP());
  38. return false;
  39. }
  40. EnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(OutValue, IntValue);
  41. }
  42. else
  43. {
  44. // AsNumber will log an error for completely inappropriate types (then give us a default)
  45. EnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(OutValue, (int64)JsonValue->AsNumber());
  46. }
  47. }
  48. else if (FNumericProperty *NumericProperty = CastField<FNumericProperty>(Property))
  49. {
  50. if (NumericProperty->IsEnum() && JsonValue->Type == EJson::String)
  51. {
  52. // see if we were passed a string for the enum
  53. const UEnum* Enum = NumericProperty->GetIntPropertyEnum();
  54. check(Enum); // should be assured by IsEnum()
  55. FString StrValue = JsonValue->AsString();
  56. int64 IntValue = Enum->GetValueByName(FName(*StrValue));
  57. //BEGIN WORKAROUND MODIFICATION
  58. if (IntValue == INDEX_NONE)
  59. {
  60. //Failed 'NewEnumeratorX' lookup, try via DisplayNames
  61. const FString LowerStrValue = StrValue.ToLower();
  62. //blueprints only support int8 sized enums
  63. int8 MaxEnum = (int8)Enum->GetMaxEnumValue();
  64. for (int32 i = 0; i < MaxEnum; i++)
  65. {
  66. //Case insensitive match
  67. if (LowerStrValue.Equals(Enum->GetDisplayNameTextByIndex(i).ToString().ToLower()))
  68. {
  69. IntValue = i;
  70. }
  71. }
  72. //END WORKAROUND MODIFICATION
  73. if (IntValue == INDEX_NONE)
  74. {
  75. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable import enum %s from string value %s for property %s"), *Enum->CppType, *StrValue, *Property->GetNameCPP());
  76. return false;
  77. }
  78. }
  79. NumericProperty->SetIntPropertyValue(OutValue, IntValue);
  80. }
  81. else if (NumericProperty->IsFloatingPoint())
  82. {
  83. // AsNumber will log an error for completely inappropriate types (then give us a default)
  84. NumericProperty->SetFloatingPointPropertyValue(OutValue, JsonValue->AsNumber());
  85. }
  86. else if (NumericProperty->IsInteger())
  87. {
  88. if (JsonValue->Type == EJson::String)
  89. {
  90. // parse string -> int64 ourselves so we don't lose any precision going through AsNumber (aka double)
  91. NumericProperty->SetIntPropertyValue(OutValue, FCString::Atoi64(*JsonValue->AsString()));
  92. }
  93. else
  94. {
  95. // AsNumber will log an error for completely inappropriate types (then give us a default)
  96. NumericProperty->SetIntPropertyValue(OutValue, (int64)JsonValue->AsNumber());
  97. }
  98. }
  99. else
  100. {
  101. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable to set numeric property type %s for property %s"), *Property->GetClass()->GetName(), *Property->GetNameCPP());
  102. return false;
  103. }
  104. }
  105. else if (FBoolProperty *BoolProperty = CastField<FBoolProperty>(Property))
  106. {
  107. // AsBool will log an error for completely inappropriate types (then give us a default)
  108. BoolProperty->SetPropertyValue(OutValue, JsonValue->AsBool());
  109. }
  110. else if (FStrProperty *StringProperty = CastField<FStrProperty>(Property))
  111. {
  112. // AsString will log an error for completely inappropriate types (then give us a default)
  113. StringProperty->SetPropertyValue(OutValue, JsonValue->AsString());
  114. }
  115. else if (FArrayProperty *ArrayProperty = CastField<FArrayProperty>(Property))
  116. {
  117. if (JsonValue->Type == EJson::Array)
  118. {
  119. TArray< TSharedPtr<FJsonValue> > ArrayValue = JsonValue->AsArray();
  120. int32 ArrLen = ArrayValue.Num();
  121. // make the output array size match
  122. FScriptArrayHelper Helper(ArrayProperty, OutValue);
  123. Helper.Resize(ArrLen);
  124. // set the property values
  125. for (int32 i = 0; i < ArrLen; ++i)
  126. {
  127. const TSharedPtr<FJsonValue>& ArrayValueItem = ArrayValue[i];
  128. if (ArrayValueItem.IsValid() && !ArrayValueItem->IsNull())
  129. {
  130. if (!JsonValueToFPropertyWithContainer(ArrayValueItem, ArrayProperty->Inner, Helper.GetRawPtr(i), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags))
  131. {
  132. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable to deserialize array element [%d] for property %s"), i, *Property->GetNameCPP());
  133. return false;
  134. }
  135. }
  136. }
  137. }
  138. else
  139. {
  140. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import TArray from non-array JSON key for property %s"), *Property->GetNameCPP());
  141. return false;
  142. }
  143. }
  144. else if (FMapProperty* MapProperty = CastField<FMapProperty>(Property))
  145. {
  146. if (JsonValue->Type == EJson::Object)
  147. {
  148. TSharedPtr<FJsonObject> ObjectValue = JsonValue->AsObject();
  149. FScriptMapHelper Helper(MapProperty, OutValue);
  150. check(ObjectValue);
  151. int32 MapSize = ObjectValue->Values.Num();
  152. Helper.EmptyValues(MapSize);
  153. // set the property values
  154. for (const auto& Entry : ObjectValue->Values)
  155. {
  156. if (Entry.Value.IsValid() && !Entry.Value->IsNull())
  157. {
  158. int32 NewIndex = Helper.AddDefaultValue_Invalid_NeedsRehash();
  159. TSharedPtr<FJsonValueString> TempKeyValue = MakeShared<FJsonValueString>(Entry.Key);
  160. const bool bKeySuccess = JsonValueToFPropertyWithContainer(TempKeyValue, MapProperty->KeyProp, Helper.GetKeyPtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags);
  161. const bool bValueSuccess = JsonValueToFPropertyWithContainer(Entry.Value, MapProperty->ValueProp, Helper.GetValuePtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags);
  162. if (!(bKeySuccess && bValueSuccess))
  163. {
  164. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable to deserialize map element [key: %s] for property %s"), *Entry.Key, *Property->GetNameCPP());
  165. return false;
  166. }
  167. }
  168. }
  169. Helper.Rehash();
  170. }
  171. else
  172. {
  173. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import TMap from non-object JSON key for property %s"), *Property->GetNameCPP());
  174. return false;
  175. }
  176. }
  177. else if (FSetProperty* SetProperty = CastField<FSetProperty>(Property))
  178. {
  179. if (JsonValue->Type == EJson::Array)
  180. {
  181. TArray< TSharedPtr<FJsonValue> > ArrayValue = JsonValue->AsArray();
  182. int32 ArrLen = ArrayValue.Num();
  183. FScriptSetHelper Helper(SetProperty, OutValue);
  184. // set the property values
  185. for (int32 i = 0; i < ArrLen; ++i)
  186. {
  187. const TSharedPtr<FJsonValue>& ArrayValueItem = ArrayValue[i];
  188. if (ArrayValueItem.IsValid() && !ArrayValueItem->IsNull())
  189. {
  190. int32 NewIndex = Helper.AddDefaultValue_Invalid_NeedsRehash();
  191. if (!JsonValueToFPropertyWithContainer(ArrayValueItem, SetProperty->ElementProp, Helper.GetElementPtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags))
  192. {
  193. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable to deserialize set element [%d] for property %s"), i, *Property->GetNameCPP());
  194. return false;
  195. }
  196. }
  197. }
  198. Helper.Rehash();
  199. }
  200. else
  201. {
  202. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import TSet from non-array JSON key for property %s"), *Property->GetNameCPP());
  203. return false;
  204. }
  205. }
  206. else if (FTextProperty* TextProperty = CastField<FTextProperty>(Property))
  207. {
  208. if (JsonValue->Type == EJson::String)
  209. {
  210. // assume this string is already localized, so import as invariant
  211. TextProperty->SetPropertyValue(OutValue, FText::FromString(JsonValue->AsString()));
  212. }
  213. else if (JsonValue->Type == EJson::Object)
  214. {
  215. TSharedPtr<FJsonObject> Obj = JsonValue->AsObject();
  216. check(Obj.IsValid()); // should not fail if Type == EJson::Object
  217. // import the subvalue as a culture invariant string
  218. FText Text;
  219. if (!FJsonObjectConverter::GetTextFromObject(Obj.ToSharedRef(), Text))
  220. {
  221. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import FText from JSON object with invalid keys for property %s"), *Property->GetNameCPP());
  222. return false;
  223. }
  224. TextProperty->SetPropertyValue(OutValue, Text);
  225. }
  226. else
  227. {
  228. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import FText from JSON that was neither string nor object for property %s"), *Property->GetNameCPP());
  229. return false;
  230. }
  231. }
  232. else if (FStructProperty *StructProperty = CastField<FStructProperty>(Property))
  233. {
  234. static const FName NAME_DateTime(TEXT("DateTime"));
  235. static const FName NAME_Color_Local(TEXT("Color"));
  236. static const FName NAME_LinearColor_Local(TEXT("LinearColor"));
  237. if (JsonValue->Type == EJson::Object)
  238. {
  239. TSharedPtr<FJsonObject> Obj = JsonValue->AsObject();
  240. check(Obj.IsValid()); // should not fail if Type == EJson::Object
  241. if (!JsonAttributesToUStructWithContainer(Obj->Values, StructProperty->Struct, OutValue, ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags))
  242. {
  243. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - FJsonObjectConverter::JsonObjectToUStruct failed for property %s"), *Property->GetNameCPP());
  244. return false;
  245. }
  246. }
  247. else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetFName() == NAME_LinearColor_Local)
  248. {
  249. FLinearColor& ColorOut = *(FLinearColor*)OutValue;
  250. FString ColorString = JsonValue->AsString();
  251. FColor IntermediateColor;
  252. IntermediateColor = FColor::FromHex(ColorString);
  253. ColorOut = IntermediateColor;
  254. }
  255. else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetFName() == NAME_Color)
  256. {
  257. FColor& ColorOut = *(FColor*)OutValue;
  258. FString ColorString = JsonValue->AsString();
  259. ColorOut = FColor::FromHex(ColorString);
  260. }
  261. else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetFName() == NAME_DateTime)
  262. {
  263. FString DateString = JsonValue->AsString();
  264. FDateTime& DateTimeOut = *(FDateTime*)OutValue;
  265. if (DateString == TEXT("min"))
  266. {
  267. // min representable value for our date struct. Actual date may vary by platform (this is used for sorting)
  268. DateTimeOut = FDateTime::MinValue();
  269. }
  270. else if (DateString == TEXT("max"))
  271. {
  272. // max representable value for our date struct. Actual date may vary by platform (this is used for sorting)
  273. DateTimeOut = FDateTime::MaxValue();
  274. }
  275. else if (DateString == TEXT("now"))
  276. {
  277. // this value's not really meaningful from json serialization (since we don't know timezone) but handle it anyway since we're handling the other keywords
  278. DateTimeOut = FDateTime::UtcNow();
  279. }
  280. else if (FDateTime::ParseIso8601(*DateString, DateTimeOut))
  281. {
  282. // ok
  283. }
  284. else if (FDateTime::Parse(DateString, DateTimeOut))
  285. {
  286. // ok
  287. }
  288. else
  289. {
  290. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable to import FDateTime for property %s"), *Property->GetNameCPP());
  291. return false;
  292. }
  293. }
  294. else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetCppStructOps() && StructProperty->Struct->GetCppStructOps()->HasImportTextItem())
  295. {
  296. UScriptStruct::ICppStructOps* TheCppStructOps = StructProperty->Struct->GetCppStructOps();
  297. FString ImportTextString = JsonValue->AsString();
  298. const TCHAR* ImportTextPtr = *ImportTextString;
  299. if (!TheCppStructOps->ImportTextItem(ImportTextPtr, OutValue, PPF_None, nullptr, (FOutputDevice*)GWarn))
  300. {
  301. // Fall back to trying the tagged property approach if custom ImportTextItem couldn't get it done
  302. Property->ImportText_Direct(ImportTextPtr, OutValue, nullptr, PPF_None);
  303. }
  304. }
  305. else if (JsonValue->Type == EJson::String)
  306. {
  307. FString ImportTextString = JsonValue->AsString();
  308. const TCHAR* ImportTextPtr = *ImportTextString;
  309. Property->ImportText_Direct(ImportTextPtr, OutValue, nullptr, PPF_None);
  310. }
  311. else
  312. {
  313. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import UStruct from non-object JSON key for property %s"), *Property->GetNameCPP());
  314. return false;
  315. }
  316. }
  317. else if (FObjectProperty *ObjectProperty = CastField<FObjectProperty>(Property))
  318. {
  319. if (JsonValue->Type == EJson::Object)
  320. {
  321. UObject* Outer = GetTransientPackage();
  322. if (ContainerStruct->IsChildOf(UObject::StaticClass()))
  323. {
  324. Outer = (UObject*)Container;
  325. }
  326. UClass* PropertyClass = ObjectProperty->PropertyClass;
  327. UObject* createdObj = StaticAllocateObject(PropertyClass, Outer, NAME_None, EObjectFlags::RF_NoFlags, EInternalObjectFlags::None, false);
  328. (*PropertyClass->ClassConstructor)(FObjectInitializer(createdObj, PropertyClass->ClassDefaultObject, EObjectInitializerOptions::None));
  329. ObjectProperty->SetObjectPropertyValue(OutValue, createdObj);
  330. TSharedPtr<FJsonObject> Obj = JsonValue->AsObject();
  331. check(Obj.IsValid()); // should not fail if Type == EJson::Object
  332. if (!JsonAttributesToUStructWithContainer(Obj->Values, ObjectProperty->PropertyClass, createdObj, ObjectProperty->PropertyClass, createdObj, CheckFlags & (~CPF_ParmFlags), SkipFlags))
  333. {
  334. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - FJsonObjectConverter::JsonObjectToUStruct failed for property %s"), *Property->GetNameCPP());
  335. return false;
  336. }
  337. }
  338. else if (JsonValue->Type == EJson::String)
  339. {
  340. // Default to expect a string for everything else
  341. if (Property->ImportText_Direct(*JsonValue->AsString(), OutValue, nullptr, PPF_None) == nullptr)
  342. {
  343. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable import property type %s from string value for property %s"), *Property->GetClass()->GetName(), *Property->GetNameCPP());
  344. return false;
  345. }
  346. }
  347. }
  348. else
  349. {
  350. // Default to expect a string for everything else
  351. if (Property->ImportText_Direct(*JsonValue->AsString(), OutValue, nullptr, PPF_None) == nullptr)
  352. {
  353. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable import property type %s from string value for property %s"), *Property->GetClass()->GetName(), *Property->GetNameCPP());
  354. return false;
  355. }
  356. }
  357. return true;
  358. }
  359. bool JsonValueToFPropertyWithContainer(const TSharedPtr<FJsonValue>& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)
  360. {
  361. if (!JsonValue.IsValid())
  362. {
  363. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Invalid value JSON key"));
  364. return false;
  365. }
  366. bool bArrayOrSetProperty = Property->IsA<FArrayProperty>() || Property->IsA<FSetProperty>();
  367. bool bJsonArray = JsonValue->Type == EJson::Array;
  368. if (!bJsonArray)
  369. {
  370. if (bArrayOrSetProperty)
  371. {
  372. //Begin custom workaround - support string -> binary array conversion
  373. FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property);
  374. if (ArrayProperty->Inner->IsA<FByteProperty>())
  375. {
  376. //Did we get a direct binary?
  377. TArray<uint8> ByteArray;
  378. if (FJsonValueBinary::IsBinary(JsonValue))
  379. {
  380. ByteArray = FJsonValueBinary::AsBinary(JsonValue);
  381. }
  382. //it's a string, convert use base64 to bytes
  383. else if(JsonValue->Type == EJson::String)
  384. {
  385. bool bDidDecodeCorrectly = FBase64::Decode(JsonValue->AsString(), ByteArray);
  386. if (!bDidDecodeCorrectly)
  387. {
  388. UE_LOG(LogJson, Warning, TEXT("FBase64::Decode failed on %s"), *Property->GetName());
  389. return false;
  390. }
  391. }
  392. else
  393. {
  394. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import TArray from unsupported non-array JSON key: %s"), *Property->GetName());
  395. return false;
  396. }
  397. //Memcpy raw arrays
  398. FScriptArrayHelper ArrayHelper(ArrayProperty, OutValue);
  399. ArrayHelper.EmptyAndAddUninitializedValues(ByteArray.Num());
  400. FGenericPlatformMemory::Memcpy(ArrayHelper.GetRawPtr(), ByteArray.GetData(), ByteArray.Num());
  401. return true;
  402. }
  403. //End custom workaround
  404. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import TArray from non-array JSON key"));
  405. return false;
  406. }
  407. if (Property->ArrayDim != 1)
  408. {
  409. UE_LOG(LogJson, Warning, TEXT("Ignoring excess properties when deserializing %s"), *Property->GetName());
  410. }
  411. return ConvertScalarJsonValueToFPropertyWithContainer(JsonValue, Property, OutValue, ContainerStruct, Container, CheckFlags, SkipFlags);
  412. }
  413. // In practice, the ArrayDim == 1 check ought to be redundant, since nested arrays of UPropertys are not supported
  414. if (bArrayOrSetProperty && Property->ArrayDim == 1)
  415. {
  416. // Read into TArray
  417. return ConvertScalarJsonValueToFPropertyWithContainer(JsonValue, Property, OutValue, ContainerStruct, Container, CheckFlags, SkipFlags);
  418. }
  419. // We're deserializing a JSON array
  420. const auto& ArrayValue = JsonValue->AsArray();
  421. if (Property->ArrayDim < ArrayValue.Num())
  422. {
  423. UE_LOG(LogJson, Warning, TEXT("BPEnumWA-Ignoring excess properties when deserializing %s"), *Property->GetName());
  424. }
  425. // Read into native array
  426. int ItemsToRead = FMath::Clamp(ArrayValue.Num(), 0, Property->ArrayDim);
  427. for (int Index = 0; Index != ItemsToRead; ++Index)
  428. {
  429. if (!ConvertScalarJsonValueToFPropertyWithContainer(ArrayValue[Index], Property, (char*)OutValue + Index * Property->ElementSize, ContainerStruct, Container, CheckFlags, SkipFlags))
  430. {
  431. return false;
  432. }
  433. }
  434. return true;
  435. }
  436. bool JsonAttributesToUStructWithContainer(const TMap< FString, TSharedPtr<FJsonValue> >& JsonAttributes, const UStruct* StructDefinition, void* OutStruct, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)
  437. {
  438. if (StructDefinition == FJsonObjectWrapper::StaticStruct())
  439. {
  440. // Just copy it into the object
  441. FJsonObjectWrapper* ProxyObject = (FJsonObjectWrapper *)OutStruct;
  442. ProxyObject->JsonObject = MakeShared<FJsonObject>();
  443. ProxyObject->JsonObject->Values = JsonAttributes;
  444. return true;
  445. }
  446. int32 NumUnclaimedProperties = JsonAttributes.Num();
  447. if (NumUnclaimedProperties <= 0)
  448. {
  449. return true;
  450. }
  451. // iterate over the struct properties
  452. for (TFieldIterator<FProperty> PropIt(StructDefinition); PropIt; ++PropIt)
  453. {
  454. FProperty* Property = *PropIt;
  455. // Check to see if we should ignore this property
  456. if (CheckFlags != 0 && !Property->HasAnyPropertyFlags(CheckFlags))
  457. {
  458. continue;
  459. }
  460. if (Property->HasAnyPropertyFlags(SkipFlags))
  461. {
  462. continue;
  463. }
  464. // find a json value matching this property name
  465. const TSharedPtr<FJsonValue>* JsonValue = JsonAttributes.Find(Property->GetName());
  466. if (!JsonValue)
  467. {
  468. // we allow values to not be found since this mirrors the typical UObject mantra that all the fields are optional when deserializing
  469. continue;
  470. }
  471. if (JsonValue->IsValid() && !(*JsonValue)->IsNull())
  472. {
  473. void* Value = Property->ContainerPtrToValuePtr<uint8>(OutStruct);
  474. if (!JsonValueToFPropertyWithContainer(*JsonValue, Property, Value, ContainerStruct, Container, CheckFlags, SkipFlags))
  475. {
  476. UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonObjectToUStruct - Unable to parse %s.%s from JSON"), *StructDefinition->GetName(), *Property->GetName());
  477. return false;
  478. }
  479. }
  480. if (--NumUnclaimedProperties <= 0)
  481. {
  482. // If we found all properties that were in the JsonAttributes map, there is no reason to keep looking for more.
  483. break;
  484. }
  485. }
  486. return true;
  487. }
  488. //End FJsonObjectConverter BPEnum Workaround
  489. class FJsonObjectConverterBPEnum : public FJsonObjectConverter
  490. {
  491. public:
  492. static bool JsonObjectToUStruct(const TSharedRef<FJsonObject>& JsonObject, const UStruct* StructDefinition, void* OutStruct, int64 CheckFlags, int64 SkipFlags)
  493. {
  494. return JsonAttributesToUStructWithContainer(JsonObject->Values, StructDefinition, OutStruct, StructDefinition, OutStruct, CheckFlags, SkipFlags);
  495. }
  496. };
  497. }
  498. FString FTrimmedKeyMap::ToString()
  499. {
  500. FString SubMapString;
  501. for (auto Pair : SubMap)
  502. {
  503. FString PairString = FString::Printf(TEXT("{%s:%s}"), *Pair.Key, *Pair.Value->ToString());
  504. SubMapString.Append(PairString);
  505. SubMapString.Append(",");
  506. }
  507. return FString::Printf(TEXT("{%s:%s}"), *LongKey, *SubMapString);
  508. }
  509. FString USIOJConvert::ToJsonString(const TSharedPtr<FJsonObject>& JsonObject)
  510. {
  511. FString OutputString;
  512. TSharedRef< FCondensedJsonStringWriter > Writer = FCondensedJsonStringWriterFactory::Create(&OutputString);
  513. FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
  514. return OutputString;
  515. }
  516. FString USIOJConvert::ToJsonString(const TArray<TSharedPtr<FJsonValue>>& JsonValueArray)
  517. {
  518. FString OutputString;
  519. TSharedRef< FCondensedJsonStringWriter > Writer = FCondensedJsonStringWriterFactory::Create(&OutputString);
  520. FJsonSerializer::Serialize(JsonValueArray, Writer);
  521. return OutputString;
  522. }
  523. FString USIOJConvert::ToJsonString(const TSharedPtr<FJsonValue>& JsonValue)
  524. {
  525. if (JsonValue->Type == EJson::None)
  526. {
  527. return FString();
  528. }
  529. else if (JsonValue->Type == EJson::Null)
  530. {
  531. return FString();
  532. }
  533. else if (JsonValue->Type == EJson::String)
  534. {
  535. return JsonValue->AsString();
  536. }
  537. else if (JsonValue->Type == EJson::Number)
  538. {
  539. return FString::Printf(TEXT("%f"), JsonValue->AsNumber());
  540. }
  541. else if (JsonValue->Type == EJson::Boolean)
  542. {
  543. return FString::Printf(TEXT("%d"), JsonValue->AsBool());
  544. }
  545. else if (JsonValue->Type == EJson::Array)
  546. {
  547. return ToJsonString(JsonValue->AsArray());
  548. }
  549. else if (JsonValue->Type == EJson::Object)
  550. {
  551. return ToJsonString(JsonValue->AsObject());
  552. }
  553. else
  554. {
  555. return FString();
  556. }
  557. }
  558. USIOJsonValue* USIOJConvert::ToSIOJsonValue(const TArray<TSharedPtr<FJsonValue>>& JsonValueArray)
  559. {
  560. TArray< TSharedPtr<FJsonValue> > ValueArray;
  561. for (auto InVal : JsonValueArray)
  562. {
  563. ValueArray.Add(InVal);
  564. }
  565. USIOJsonValue* ResultValue = NewObject<USIOJsonValue>();
  566. TSharedPtr<FJsonValue> NewVal = MakeShareable(new FJsonValueArray(ValueArray));
  567. ResultValue->SetRootValue(NewVal);
  568. return ResultValue;
  569. }
  570. #if PLATFORM_WINDOWS
  571. #pragma endregion ToJsonValue
  572. #endif
  573. TSharedPtr<FJsonValue> USIOJConvert::JsonStringToJsonValue(const FString& JsonString)
  574. {
  575. //Null
  576. if (JsonString.IsEmpty())
  577. {
  578. return MakeShareable(new FJsonValueNull);
  579. }
  580. //Number
  581. if (JsonString.IsNumeric())
  582. {
  583. //convert to double
  584. return MakeShareable(new FJsonValueNumber(FCString::Atod(*JsonString)));
  585. }
  586. //Object
  587. if (JsonString.StartsWith(FString(TEXT("{"))))
  588. {
  589. TSharedPtr< FJsonObject > JsonObject = ToJsonObject(JsonString);
  590. return MakeShareable(new FJsonValueObject(JsonObject));
  591. }
  592. //Array
  593. if (JsonString.StartsWith(FString(TEXT("["))))
  594. {
  595. TArray < TSharedPtr<FJsonValue>> RawJsonValueArray;
  596. TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(*JsonString);
  597. bool success = FJsonSerializer::Deserialize(Reader, RawJsonValueArray);
  598. if (success)
  599. {
  600. return MakeShareable(new FJsonValueArray(RawJsonValueArray));
  601. }
  602. }
  603. //Bool
  604. if (JsonString == FString("true") || JsonString == FString("false"))
  605. {
  606. bool BooleanValue = (JsonString == FString("true"));
  607. return MakeShareable(new FJsonValueBoolean(BooleanValue));
  608. }
  609. //String
  610. return MakeShareable(new FJsonValueString(JsonString));
  611. }
  612. TSharedPtr<FJsonValue> USIOJConvert::ToJsonValue(const TSharedPtr<FJsonObject>& JsonObject)
  613. {
  614. return MakeShareable(new FJsonValueObject(JsonObject));
  615. }
  616. TSharedPtr<FJsonValue> USIOJConvert::ToJsonValue(const FString& StringValue)
  617. {
  618. return MakeShareable(new FJsonValueString(StringValue));
  619. }
  620. TSharedPtr<FJsonValue> USIOJConvert::ToJsonValue(double NumberValue)
  621. {
  622. return MakeShareable(new FJsonValueNumber(NumberValue));
  623. }
  624. TSharedPtr<FJsonValue> USIOJConvert::ToJsonValue(bool BoolValue)
  625. {
  626. return MakeShareable(new FJsonValueBoolean(BoolValue));
  627. }
  628. TSharedPtr<FJsonValue> USIOJConvert::ToJsonValue(const TArray<uint8>& BinaryValue)
  629. {
  630. return MakeShareable(new FJsonValueBinary(BinaryValue));
  631. }
  632. TSharedPtr<FJsonValue> USIOJConvert::ToJsonValue(const TArray<TSharedPtr<FJsonValue>>& ArrayValue)
  633. {
  634. return MakeShareable(new FJsonValueArray(ArrayValue));
  635. }
  636. #if PLATFORM_WINDOWS
  637. #pragma endregion ToJsonValue
  638. #endif
  639. TArray<TSharedPtr<FJsonValue>> USIOJConvert::JsonStringToJsonArray(const FString& JsonString)
  640. {
  641. TArray < TSharedPtr<FJsonValue>> RawJsonValueArray;
  642. TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(*JsonString);
  643. FJsonSerializer::Deserialize(Reader, RawJsonValueArray);
  644. return RawJsonValueArray;
  645. }
  646. TSharedPtr<FJsonObject> USIOJConvert::ToJsonObject(const FString& JsonString)
  647. {
  648. TSharedPtr< FJsonObject > JsonObject = MakeShareable(new FJsonObject);
  649. TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(*JsonString);
  650. FJsonSerializer::Deserialize(Reader, JsonObject);
  651. return JsonObject;
  652. }
  653. TSharedPtr<FJsonObject> USIOJConvert::ToJsonObject(UStruct* StructDefinition, void* StructPtr, bool IsBlueprintStruct, bool BinaryStructCppSupport /*= false */)
  654. {
  655. TSharedRef<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
  656. if (IsBlueprintStruct || BinaryStructCppSupport)
  657. {
  658. //Handle BP enum override
  659. if (!EnumOverrideExportCallback.IsBound())
  660. {
  661. EnumOverrideExportCallback.BindLambda([](FProperty* Property, const void* Value)
  662. {
  663. if (FByteProperty* BPEnumProperty = CastField<FByteProperty>(Property))
  664. {
  665. //Override default enum behavior by fetching display name text
  666. UEnum* EnumDef = BPEnumProperty->Enum;
  667. uint8 IntValue = *(uint8*)Value;
  668. //It's an enum byte
  669. if (EnumDef)
  670. {
  671. FString StringValue = EnumDef->GetDisplayNameTextByIndex(IntValue).ToString();
  672. return (TSharedPtr<FJsonValue>)MakeShared<FJsonValueString>(StringValue);
  673. }
  674. //it's a regular byte, convert to number
  675. else
  676. {
  677. return (TSharedPtr<FJsonValue>)MakeShared<FJsonValueNumber>(IntValue);
  678. }
  679. }
  680. //byte array special case
  681. else if (FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
  682. {
  683. //is it a byte array?
  684. if (ArrayProperty->Inner->IsA<FByteProperty>())
  685. {
  686. FScriptArrayHelper ArrayHelper(ArrayProperty, Value);
  687. TArray<uint8> ByteArray(ArrayHelper.GetRawPtr(), ArrayHelper.Num());
  688. return USIOJConvert::ToJsonValue(ByteArray);
  689. }
  690. }
  691. // invalid
  692. return TSharedPtr<FJsonValue>();
  693. });
  694. }
  695. //Get the object keys
  696. FJsonObjectConverter::UStructToJsonObject(StructDefinition, StructPtr, JsonObject, 0, 0, &EnumOverrideExportCallback);
  697. //Wrap it into a value and pass it into the trimmer
  698. TSharedPtr<FJsonValue> JsonValue = MakeShareable(new FJsonValueObject(JsonObject));
  699. TrimValueKeyNames(JsonValue);
  700. //Return object with trimmed names
  701. return JsonValue->AsObject();
  702. }
  703. else
  704. {
  705. FJsonObjectConverter::UStructToJsonObject(StructDefinition, StructPtr, JsonObject, 0, 0);
  706. return JsonObject;
  707. }
  708. }
  709. TSharedPtr<FJsonObject> USIOJConvert::MakeJsonObject()
  710. {
  711. return MakeShareable(new FJsonObject);
  712. }
  713. bool USIOJConvert::JsonObjectToUStruct(TSharedPtr<FJsonObject> JsonObject, UStruct* Struct, void* StructPtr, bool IsBlueprintStruct /*= false*/, bool BinaryStructCppSupport /*= false*/)
  714. {
  715. if (IsBlueprintStruct || BinaryStructCppSupport)
  716. {
  717. //Json object we pass will have their trimmed BP names, e.g. boolKey vs boolKey_8_EDBB36654CF43866C376DE921373AF23
  718. //so we have to match them to the verbose versions, get a map of the names
  719. TSharedPtr<FTrimmedKeyMap> KeyMap = MakeShareable(new FTrimmedKeyMap);
  720. SetTrimmedKeyMapForStruct(KeyMap, Struct);
  721. //Print our keymap for debug
  722. //UE_LOG(LogTemp, Log, TEXT("Keymap: %s"), *KeyMap->ToString());
  723. //Adjust our passed in JsonObject to use the long key names
  724. TSharedPtr<FJsonValue> JsonValue = MakeShareable(new FJsonValueObject(JsonObject));
  725. ReplaceJsonValueNamesWithMap(JsonValue, KeyMap);
  726. /*Todo: add support for enums by pretty name and not by NewEnumeratorX
  727. Will require re-writing FJsonObjectConverter::JsonObjectToUStruct to lookup by display name in numeric case
  728. of https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/Runtime/JsonUtilities/Private/JsonObjectConverter.cpp#L377,
  729. or getting engine pull request merge.
  730. */
  731. //Use custom blueprint JsonObjectToUStruct to fix BPEnums
  732. return FJsonObjectConverterBPEnum::JsonObjectToUStruct(JsonObject.ToSharedRef(), Struct, StructPtr, 0, 0);
  733. }
  734. else
  735. {
  736. return FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), Struct, StructPtr, 0, 0);
  737. }
  738. }
  739. bool USIOJConvert::JsonFileToUStruct(const FString& FilePath, UStruct* Struct, void* StructPtr, bool IsBlueprintStruct /*= false*/)
  740. {
  741. //Read bytes from file
  742. TArray<uint8> OutBytes;
  743. if (!FFileHelper::LoadFileToArray(OutBytes, *FilePath))
  744. {
  745. return false;
  746. }
  747. //Convert to json string
  748. FString JsonString;
  749. FFileHelper::BufferToString(JsonString, OutBytes.GetData(), OutBytes.Num());
  750. //Read into struct
  751. return JsonObjectToUStruct(ToJsonObject(JsonString), Struct, StructPtr, IsBlueprintStruct);
  752. }
  753. bool USIOJConvert::ToJsonFile(const FString& FilePath, UStruct* Struct, void* StructPtr, bool IsBlueprintStruct /*= false*/)
  754. {
  755. //Get json object with trimmed values
  756. TSharedPtr<FJsonObject> JsonObject = ToJsonObject(Struct, StructPtr, IsBlueprintStruct);
  757. TSharedPtr<FJsonValue> TrimmedValue = MakeShareable(new FJsonValueObject(JsonObject));
  758. TrimValueKeyNames(TrimmedValue);
  759. //Convert to string
  760. FString JsonString = ToJsonString(TrimmedValue);
  761. FTCHARToUTF8 Utf8String(*JsonString);
  762. TArray<uint8> Bytes;
  763. Bytes.Append((uint8*)Utf8String.Get(), Utf8String.Length());
  764. //flush to disk
  765. return FFileHelper::SaveArrayToFile(Bytes, *FilePath);
  766. }
  767. void USIOJConvert::TrimValueKeyNames(const TSharedPtr<FJsonValue>& JsonValue)
  768. {
  769. //Array?
  770. if (JsonValue->Type == EJson::Array)
  771. {
  772. auto Array = JsonValue->AsArray();
  773. for (auto SubValue : Array)
  774. {
  775. TrimValueKeyNames(SubValue);
  776. }
  777. }
  778. //Object?
  779. else if (JsonValue->Type == EJson::Object)
  780. {
  781. auto JsonObject = JsonValue->AsObject();
  782. for (auto Pair : JsonObject->Values)
  783. {
  784. const FString& Key = Pair.Key;
  785. FString TrimmedKey;
  786. bool DidNeedTrimming = TrimKey(Key, TrimmedKey);
  787. //keep attempting sub keys even if we have a valid string
  788. auto SubValue = Pair.Value;
  789. TrimValueKeyNames(SubValue);
  790. if (DidNeedTrimming)
  791. {
  792. //Replace field names with the trimmed key
  793. JsonObject->SetField(TrimmedKey, SubValue);
  794. JsonObject->RemoveField(Key);
  795. }
  796. }
  797. }
  798. else
  799. {
  800. //UE_LOG(LogTemp, Warning, TEXT("TrimValueKeyNames:: uncaught type is: %d"), (int)JsonValue->Type);
  801. }
  802. }
  803. bool USIOJConvert::TrimKey(const FString& InLongKey, FString& OutTrimmedKey)
  804. {
  805. //Look for the position of the 2nd '_'
  806. int32 LastIndex = InLongKey.Find(TEXT("_"), ESearchCase::IgnoreCase, ESearchDir::FromEnd);
  807. LastIndex = InLongKey.Find(TEXT("_"), ESearchCase::IgnoreCase, ESearchDir::FromEnd, LastIndex);
  808. if (LastIndex >= 0)
  809. {
  810. OutTrimmedKey = InLongKey.Mid(0, LastIndex);;
  811. return true;
  812. }
  813. else
  814. {
  815. return false;
  816. }
  817. }
  818. void USIOJConvert::SetTrimmedKeyMapForStruct(TSharedPtr<FTrimmedKeyMap>& InMap, UStruct* Struct)
  819. {
  820. //Get the child fields
  821. FField* FieldPtr = Struct->ChildProperties;
  822. //If it hasn't been set, the long key is the json standardized long name
  823. if (InMap->LongKey.IsEmpty())
  824. {
  825. InMap->LongKey = FJsonObjectConverter::StandardizeCase(Struct->GetName());
  826. }
  827. //For each child field...
  828. while (FieldPtr != nullptr)
  829. {
  830. //Map our trimmed name to our full name
  831. const FString& LowerKey = FJsonObjectConverter::StandardizeCase(FieldPtr->GetName());
  832. FString TrimmedKey;
  833. bool DidTrim = TrimKey(LowerKey, TrimmedKey);
  834. //Set the key map
  835. TSharedPtr<FTrimmedKeyMap> SubMap = MakeShareable(new FTrimmedKeyMap);
  836. SubMap->LongKey = LowerKey;
  837. //No-trim case, trim = long
  838. if (!DidTrim)
  839. {
  840. TrimmedKey = SubMap->LongKey;
  841. }
  842. //Did we get a substructure?
  843. FStructProperty* SubStruct = CastField<FStructProperty>(FieldPtr);
  844. FArrayProperty* ArrayProp = CastField<FArrayProperty>(FieldPtr);
  845. FMapProperty* MapProperty = CastField<FMapProperty>(FieldPtr);
  846. if (SubStruct != nullptr)
  847. {
  848. //We did, embed the sub-map
  849. SetTrimmedKeyMapForStruct(SubMap, SubStruct->Struct);
  850. }
  851. //Did we get a sub-array?
  852. else if (ArrayProp != nullptr)
  853. {
  854. //set the inner map for the inner property
  855. //UE_LOG(LogTemp, Log, TEXT("found array: %s"), *ArrayProp->GetName());
  856. SetTrimmedKeyMapForProp(SubMap, ArrayProp->Inner);
  857. }
  858. else if (MapProperty != nullptr)
  859. {
  860. //UE_LOG(LogTemp, Log, TEXT("I'm a tmap: %s"), *MapProperty->GetName());
  861. SetTrimmedKeyMapForProp(SubMap, MapProperty);
  862. }
  863. //Debug types
  864. /*
  865. UProperty* ObjectProp = Cast<UProperty>(FieldPtr);
  866. if (ObjectProp)
  867. {
  868. UE_LOG(LogTemp, Log, TEXT("found map: %s, %s, type: %s, %s"),
  869. *ObjectProp->GetName(),
  870. *ObjectProp->GetNameCPP(),
  871. *ObjectProp->GetClass()->GetFName().ToString(),
  872. *ObjectProp->GetCPPType());
  873. }*/
  874. InMap->SubMap.Add(TrimmedKey, SubMap);
  875. //UE_LOG(LogTemp, Log, TEXT("long: %s, trim: %s, is struct: %d"), *SubMap->LongKey, *TrimmedKey, SubStruct != NULL);
  876. FieldPtr = FieldPtr->Next;
  877. }
  878. //UE_LOG(LogTemp, Log, TEXT("Final map: %d"), InMap->SubMap.Num());
  879. }
  880. void USIOJConvert::SetTrimmedKeyMapForProp(TSharedPtr<FTrimmedKeyMap>& InMap, FProperty* InnerProperty)
  881. {
  882. //UE_LOG(LogTemp, Log, TEXT("got prop: %s"), *InnerProperty->GetName());
  883. FStructProperty* SubStruct = CastField<FStructProperty>(InnerProperty);
  884. FArrayProperty* ArrayProp = CastField<FArrayProperty>(InnerProperty);
  885. FMapProperty* MapProperty = CastField<FMapProperty>(InnerProperty);
  886. if (SubStruct != nullptr)
  887. {
  888. //We did, embed the sub-map
  889. SetTrimmedKeyMapForStruct(InMap, SubStruct->Struct);
  890. }
  891. //Did we get a sub-array?
  892. else if (ArrayProp != nullptr)
  893. {
  894. SetTrimmedKeyMapForProp(InMap, ArrayProp->Inner);
  895. }
  896. else if (MapProperty != nullptr)
  897. {
  898. //Make a special submap with special TMAP identifier key
  899. TSharedPtr<FTrimmedKeyMap> SubMap = MakeShareable(new FTrimmedKeyMap);
  900. SubMap->LongKey = TMAP_STRING;
  901. InMap->SubMap.Add(SubMap->LongKey, SubMap);
  902. //Take the value property and set it as it's unique child
  903. SetTrimmedKeyMapForProp(SubMap, MapProperty->ValueProp);
  904. //Each child in the JSON object map will use the same structure (it's a UE4 limitation of maps anyway
  905. }
  906. }
  907. void USIOJConvert::ReplaceJsonValueNamesWithMap(TSharedPtr<FJsonValue>& JsonValue, TSharedPtr<FTrimmedKeyMap> KeyMap)
  908. {
  909. if (JsonValue->Type == EJson::Object)
  910. {
  911. //Go through each key in the object
  912. auto Object = JsonValue->AsObject();
  913. auto SubMap = KeyMap->SubMap;
  914. auto AllValues = Object->Values;
  915. FString PreviewPreValue = USIOJConvert::ToJsonString(Object);
  916. //UE_LOG(LogTemp, Log, TEXT("Rep::PreObject: <%s>"), *PreviewPreValue);
  917. for (auto Pair : AllValues)
  918. {
  919. if (SubMap.Contains(TMAP_STRING))
  920. {
  921. FString TMapString = FString(TMAP_STRING);
  922. //If we found a tmap, replace each sub key with list of keys
  923. ReplaceJsonValueNamesWithMap(Pair.Value, SubMap[TMapString]);
  924. }
  925. else if (SubMap.Num() > 0 && SubMap.Contains(Pair.Key))
  926. {
  927. //Get the long key for entry
  928. const FString& LongKey = SubMap[Pair.Key]->LongKey;
  929. //loop nested structures
  930. ReplaceJsonValueNamesWithMap(Pair.Value, SubMap[Pair.Key]);
  931. if (Pair.Key != LongKey)
  932. {
  933. //finally set the field and remove the old field
  934. Object->SetField(LongKey, Pair.Value);
  935. Object->RemoveField(Pair.Key);
  936. }
  937. }
  938. }
  939. FString PreviewPostValue = USIOJConvert::ToJsonString(Object);
  940. //UE_LOG(LogTemp, Log, TEXT("Rep::PostObject: <%s>"), *PreviewPostValue);
  941. }
  942. else if (JsonValue->Type == EJson::Array)
  943. {
  944. auto Array = JsonValue->AsArray();
  945. for (auto Item : Array)
  946. {
  947. //UE_LOG(LogTemp, Log, TEXT("%s"), *Item->AsString());
  948. ReplaceJsonValueNamesWithMap(Item, KeyMap);
  949. }
  950. }
  951. }