Home > Software engineering >  How to parse a Pandas DataFrame string in Unreal C ?
How to parse a Pandas DataFrame string in Unreal C ?

Time:12-07

I have a web service, which offers labelled 3d points which were serialized using Pandas' DataFrame's to_json method like so:

{
    "x": {
        "6579": 0.2108709365,
        "1079": -0.7737237811
    },
    "y": {
        "6579": -0.9016159773,
        "1079": -1.2094773054
    },
    "z": {
        "6579": -0.164285481,
        "1079": -1.3477079868
    },
    "label": {
        "6579": 4,
        "1079": 6
    }
}

I have an AHttpActor class, which can request and print the data to the screen:

#pragma once

#include "GameFramework/Actor.h"
#include "Runtime/Online/HTTP/Public/Http.h"
#include "CoreMinimal.h"
#include "HttpActor.generated.h"

UCLASS()
class UE4CPPEXAMPLE_API AHttpActor : public AActor
{
    GENERATED_BODY()
    
public: 
    AHttpActor(const class FObjectInitializer& ObjectInitializer);

    virtual void BeginPlay() override;

    FHttpModule* Http;

    void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
};
#include "UE4CPPExample.h"
#include "HttpActor.h"
#include "Templates/SharedPointer.h"

AHttpActor::AHttpActor(
    const class FObjectInitializer& ObjectInitializer
)
    : Super(ObjectInitializer)
{
    Http = &FHttpModule::Get();
}

void AHttpActor::BeginPlay()
{
    Super::BeginPlay();

    TSharedRef < IHttpRequest, ESPMode::ThreadSafe > Request = Http->CreateRequest();
    Request -> OnProcessRequestComplete().BindUObject(
        this,
        &AHttpActor::OnResponseReceived
    );

    Request -> SetURL("http://localhost:2144/sample?limit=1");
    Request -> SetVerb("GET");
    Request -> ProcessRequest();
}

void AHttpActor::OnResponseReceived(
    FHttpRequestPtr Request,
    FHttpResponsePtr Response,
    bool bWasSuccessful
){
    GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, Response -> GetContentAsString());
}

I am a newcomer to C and most of the examples are assuming a list of common objects in the data. So, I am having a surprisingly hard time with this:

How do I parse this JSON string to a C object and iterate over it, one point (x, y, z, label) at a time?

To make my request more clear, I want to do the following in C instead of Python:

import json
from urllib.request import urlopen

with urlopen('http://localhost:2144/sample?limit=2') as response:
    data_dictionary = json.loads(response.read())

for key in data_dictionary['x'].keys():
    x = data_dictionary['x'][key]
    y = data_dictionary['y'][key]
    z = data_dictionary['z'][key]
    label = data_dictionary['label'][key]

    print((x, y, z, label))

CodePudding user response:

I've never done Unreal C development before, but something like this should work:

#include <string>

void AHttpActor::OnResponseReceived(
    FHttpRequestPtr Request,
    FHttpResponsePtr Response,
    bool bWasSuccessful
){
    FString fcontent = Response->GetContentAsString();
    std::string content = TCHAR_TO_UTF8(*fcontent);

    nlohmann::json j = nlohmann::json::parse(content);
    for (const auto &[key, _] : j["x"].items())
    {
        std::string x = j["x"][key];
        std::string y = j["y"][key];
        std::string z = j["z"][key];
        std::string label = j["label"][key];
        // do your stuff
    }
}

CodePudding user response:

I would rather export this pandas dataframe to CSV.

content = df.to_csv( index=False, sep=' ')

Then from C do something like:

FString fcontent = Response->GetContentAsString();
std::string content = TCHAR_TO_UTF8(*fcontent);
std::istringstream ifs( content );
while ( ifs.good() ) {
    double x, y, z; 
    std::string label;
    ifs >> x >> y >> z >> label;
    ... (do something with x y z label) ...
}

Then you don't need to mess around importing and building external libraries, managing versions etc which can be a hassle sometimes.

CodePudding user response:

I tried parsing the string using JsonObjectStringToUStruct. Here is the header:

USTRUCT()
struct FLabels
{
    GENERATED_USTRUCT_BODY()

    FLabels() {}

    UPROPERTY()
    TMap<FString, FString> label;
};

USTRUCT()
struct FXs
{
    GENERATED_USTRUCT_BODY()

    FXs() {}

    UPROPERTY()
    TMap<FString, float> x;
};

USTRUCT()
struct FYs
{
    GENERATED_USTRUCT_BODY()

    FYs() {}

    UPROPERTY()
    TMap<FString, float> y;
};

USTRUCT()
struct FZs
{
    GENERATED_USTRUCT_BODY()

    FZs() {}

    UPROPERTY()
    TMap<FString, float> z;
};


USTRUCT()
struct FDataFrame
{
    GENERATED_USTRUCT_BODY()

    FDataFrame() {}

    UPROPERTY()
    FLabels labels;

    UPROPERTY()
    FXs xs;

    UPROPERTY()
    FYs ys;

    UPROPERTY()
    FZs zs;
};

Here is the implementation:

    FString fcontent = Response->GetContentAsString();
    GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, fcontent);
    FDataFrame dataframe;
    FJsonObjectConverter::JsonObjectStringToUStruct(fcontent, &dataframe, 0, 0);

    FLabels labels = dataframe.labels;
    FXs xs = dataframe.xs;
    FYs ys = dataframe.ys;
    FZs zs = dataframe.zs;

    TMap<FString, FString> label = labels.label;
    TMap<FString, float> x = xs.x;
    TMap<FString, float> y = ys.y;
    TMap<FString, float> z = zs.z;

However, dataframe does not get populated.

CodePudding user response:

Another not-working approach is to use a TJsonReader. Here is a stub implementation:

    TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(Response->GetContentAsString());

    TSharedPtr<FJsonObject> JsonObject;

    if (FJsonSerializer::Deserialize(JsonReader, JsonObject))
    {
        TSharedPtr<FJsonObject> labelField = JsonObject->GetObjectField("label");

        TMap<FString, TSharedPtr<FJsonValue>> values = labelField->Values;

    }

This works so far, but I cannot find out how to proceed with operating on the values.

  • Related