Parse nested json to TMap in Unreal Engine 4

I have a nested json with the following structure within UE4:


{
  "boundary_points__per__cell": {
    "0": 
      {
        "x": 16.451330042628662,
        "y": 2.0813326861577965
      },
      {
        "x": 16.755262971791506,
        "y": 2.0406535171257136
      }
    ],
    "1": 
      {
        "x": -1.6378002918100634,
        "y": 4.9689057223409412
      },
      {
        "x": 0.9452331911724825,
        "y": 6.1469903490163311
      }
    ]
  }
}

I tried using proposed techniques from other threads that suggest using the JsonObjectStringToUStruct. In particular I have the following structs to parse the json logic:


USTRUCT()
struct FBoundaryPoint
{
  GENERATED_USTRUCT_BODY()
    UPROPERTY()
  int32 x;
  UPROPERTY()
  int32 y;
  
};

USTRUCT()
struct FBoundaryPoints
{
  GENERATED_USTRUCT_BODY()
    UPROPERTY()
  TArray<FBoundaryPoint> BoundaryPoints;
  
};

USTRUCT()
struct FVoronoiStruct
{
  GENERATED_USTRUCT_BODY()
    UPROPERTY()
  TMap<FString, FBoundaryPoints> boundary_points__per__cell;
};


However, Iam unable to find a solution since, the parsed objects return only the keys of the dictionary (“0”. “1”) without parsing correctly the array of elements.

Manually doing it not an option?



TSharedPtr<FJsonObject> FileAsJson;

TArray<TSharedPtr<FJsonValue> > Points = FileAsJson->GetArrayField(TEXT("boundary_points__per__cell")));
for(int32 i = 0; i < Points.Num() ++i)
{
    TSharedPtr<FJsonObject>& PointsObject = Points*->AsObject();
    FVector2D Coords;
    if (PointsObject->HasTypedField<EJson::Number>(TEXT("x")))
    {
        Coords.X = PointsObject->TryGetNumberField(TEXT("x"));
    }
    if (PointsObject->HasTypedField<EJson::Number>(TEXT("y")))
    {
        Coords.Y = PointsObject->TryGetNumberField(TEXT("y"));
    }
    etc etc
}


Yes unfortunately I had to switch to a manual parsing and everything worked as expected. Thanks

If anyone is struggling with nested Jsons and stumbles on this thread, I’ll point out that the above solution is not 100% correct because “boundary_points__per__cell” is not referring to an array. It is referring to another dict.

So using the following will give you an error!

TArray<TSharedPtr > Points = FileAsJson->GetArrayField(TEXT(“boundary_points__per__cell”)));

What helped me here was using the Values member variable in the FJsonObject class.

You can use this to directly get a TMap with the keys “0”, “1”, “2”, etc in this example.

I struggled with this so hope it saves you some trouble!

TMap<FString, FString> URequestManagerBase::DeSerializeJsonContent(const FString& JsonContent)
{
	TMap<FString, FString> ReturnMap;

	TSharedPtr<FJsonObject> JsonObject;
	TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonContent);

	if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
	{
		for (const TPair<FString, TSharedPtr<FJsonValue>>& Pair : JsonObject->Values)
		{
			CheckJsonType(Pair, &ReturnMap);
		}
	}
	return ReturnMap;
}
void URequestManagerBase::CheckJsonType(TPair<FString, TSharedPtr<FJsonValue>> Json, TMap<FString, FString>* Map)
{
	if (Json.Value->Type == EJson::Object || Json.Value->Type == EJson::Array)
	{
		if (Json.Value->Type == EJson::Object)
		{
			for (const TPair<FString, TSharedPtr<FJsonValue>>& Pair : Json.Value->AsObject()->Values)
			{
				CheckJsonType(Pair, Map);
			}
		}
		else if (Json.Value->Type == EJson::Array)
		{
			int32 Index = 1;
			for (const auto& ArrayValue : Json.Value->AsArray())
			{
				Map->Add(Json.Key + "_" + FString::FromInt(Index), ArrayValue->AsString());
				Index++;
			}
		}
	}
	else 
	{
		Map->Add(Json.Key, Json.Value->AsString());
	}
}