📗

APIから受け取ったJSONデータをUnityで辞書型に変換する方法

2023/10/04に公開

前書き

作っているもののオプション設定を作ろうと思い、たどり着いた解決方法です。
参照の記事をもとにChatGPTで解決することができました。

https://note.com/sirodon_256/n/nd92fc6c26cc8

やりたいこと

  • APIでロールプレイの設定の情報取得できるように

本題

APIの処理

api.py
from fastapi import FastAPI
import subprocess
import os
import character_roleplay

app = FastAPI()

@app.get("/roll_model")
def roll_model():
    return character_roleplay.create_character_dialogue()

if __name__ == "__main__":
    name, ex = os.path.splitext(os.path.basename(__file__))
    subprocess.run(f"uvicorn {name}:app --reload")

ロールプレイ用の名前とファイルパスを返すコード

character_roleplay.py
import glob
import json

def create_character_dialogue():
    data = []
    
    file_paths = glob.glob('historys/roll/*.json')
    
    for file_path in file_paths:
        with open(file_path, encoding="UTF-8") as f:
            character_data = json.load(f)

        character_info = {
            "name":character_data['character_name'],
            "path":file_path.replace('\\', '/')
        }
        
        data.append(character_info)
        
    return {'data': data}
ディレクトリ構造
ディレクトリ
├─.venv
├─historys
│  └─roll
│    └─ローラモデルファイル
├─app.py
├─character_roleplay.py
└─onnxruntime.dll

ロールモデルのJSON

JSONの中身
{
    "character_name":"AI",
    "system_message" : "日常的な会話が得意です。ユーザーとお話がしたいです。",
    "initial_messages":[
	    {
	        "speaker": "ユーザー",
	        "text": "こんにちは。"
	    },
	    {
	        "speaker": "システム",
	        "text": "こんにちは、ユーザーさん。"
	    }
    ]
}

Unityの処理

受け取る想定のJSON

getjson
{
    "data":[
        {
            "name":"AI",
            "path":"historys/roll/assistant_history.json"
        },
        {
            "name":"平成ギャル",
            "path":"historys/roll/gal_history.json"
        },
        {
            "name":"ずんだもん",
            "path":"historys/roll/zundamon_history.json"
        }
    ]
}

JSONデータの構造に合ったC#クラスを作成します。

JSONデータにはdataキーがあり、その値はオブジェクトの配列です。
各オブジェクトにはnamepathというキーがあります。
以下のクラスを作成します。

[System.Serializable]
public class MyData
{
    public string name;
    public string path;
}

[System.Serializable]
public class DataContainer
{
    public List<MyData> data;
}

次に、JSON文字列を辞書型に変換するコード。

public class JSONParser : MonoBehaviour
{
    private string serverURL = "http://127.0.0.1:8000/"; // サーバーのURL
    
    void Start()
    {
        StartCoroutine(FetchDataFromAPI());
    }
    
    // JSON文字列を辞書型に変換するメソッド
    public Dictionary<string, string> ParseJSON(string jsonString)
    {
        // JSON文字列をDataContainerクラスにデシリアライズ
        DataContainer container = JsonUtility.FromJson<DataContainer>(jsonString);

        // 辞書型に変換
        Dictionary<string, string> dictionary = new Dictionary<string, string>();
        foreach (var data in container.data)
        {
            dictionary[data.name] = data.path;
        }

        return dictionary;
    }
    
    // APIからデータを取得する関数
    private IEnumerator FetchDataFromAPI()
    {
        using (UnityWebRequest webRequest = UnityWebRequest.Get(serverURL + "roll_model"))
        {
            yield return webRequest.SendWebRequest();

            if (webRequest.result == UnityWebRequest.Result.Success)
            {
                string jsonText = webRequest.downloadHandler.text;
                Debug.Log(jsonText);

                Dictionary<string, string> dataDictionary = ParseJSON(jsonText);

                // 辞書型データを使用
                foreach (var kvp in dataDictionary)
                {
                    Debug.Log($"Name: {kvp.Key}, Path: {kvp.Value}");
                }
            }
            else
            {
                Debug.LogError("APIリクエストが失敗しました: " + webRequest.error);
            }
        }
    }
}

このコードでは、JSON文字列をDataContainerクラスにデシリアライズし、その後、Dictionary<string, string>に変換しています。最終的に、dataDictionaryとして辞書型データを取得し、使用することができます。

全体のコード
JSONParser.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;

[System.Serializable]
public class MyData
{
    public string name;
    public string path;
}

[System.Serializable]
public class DataContainer
{
    public List<MyData> data;
}

public class JSONParser : MonoBehaviour
{
    private string serverURL = "http://127.0.0.1:8000/"; // サーバーのURL
    
    void Start()
    {
        StartCoroutine(FetchDataFromAPI());
    }
    
    // JSON文字列を辞書型に変換するメソッド
    public Dictionary<string, string> ParseJSON(string jsonString)
    {
        // JSON文字列をDataContainerクラスにデシリアライズ
        DataContainer container = JsonUtility.FromJson<DataContainer>(jsonString);

        // 辞書型に変換
        Dictionary<string, string> dictionary = new Dictionary<string, string>();
        foreach (var data in container.data)
        {
            dictionary[data.name] = data.path;
        }

        return dictionary;
    }
    
    // APIからデータを取得する関数
    private IEnumerator FetchDataFromAPI()
    {
        using (UnityWebRequest webRequest = UnityWebRequest.Get(serverURL + "roll_model"))
        {
            yield return webRequest.SendWebRequest();

            if (webRequest.result == UnityWebRequest.Result.Success)
            {
                string jsonText = webRequest.downloadHandler.text;
                Debug.Log(jsonText);

                Dictionary<string, string> dataDictionary = ParseJSON(jsonText);

                // 辞書型データを使用
                foreach (var kvp in dataDictionary)
                {
                    Debug.Log($"Name: {kvp.Key}, Path: {kvp.Value}");
                }
            }
            else
            {
                Debug.LogError("APIリクエストが失敗しました: " + webRequest.error);
            }
        }
    }
}

余談

はじめは以下のようなJSONで受け取ろうとしていたのですがJsonUtilityではパースできないことが分かったので、dataキーをつけることにしました。

{
	{
		"name":"AI",
		"path":"historys/roll/assistant_history.json"
	},
	{
		"name":"平成ギャル",
		"path":"historys/roll/gal_history.json"
	},
	{
		"name":"ずんだもん",
		"path":"historys/roll/zundamon_history.json"
	}
}

参照

https://zenn.dev/tech_memorandum/articles/25e1ec1e83e929

Discussion