vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

Pythonで CSV -> Json を作る(そしてそれをUnityで読み込み)

はじめに

前回に引き続き必要になったのでメモっておきます。

Pythonを使ってCSVとして保存されたファイルを読み込み、Jsonファイルとして出力。
それをUnityで読み込みます。

Pythonを使うことになった理由は単に興味があったからですw

※2017.9.20修正
Pythonのファイル名、データを格納するための Parts クラスでのアトリビュートの宣言方法を修正しました。

CSVを読み込む

まずはCSVを読み込んで、 Parts というクラス(酷い名前なのは気にしない方向で。。。)に格納します。

parts.csv

PartsNo,PartsName,Description,
No.1,Parts1,一つ目のパーツです,
No.2,Parts2,二つ目のパーツです,

parts.py

 # -*- coding: utf-8 -*-
# coding: utf-8
class Parts:
    def __init__(self, parts_no: str, parts_name: str, description: str):
        self.parts_no = parts_no
        self.parts_name = parts_name
        self.description = description

main.py

 # -*- coding: utf-8 -*-
import csv
from parts import Parts


def main():
  # CSVファイルを読み込む.
  with open(f'parts.csv', encoding='utf-8') as csvFile:
    for index, row in enumerate(csv.reader(csvFile)):
        # タイトル行を無視する.
        if index <= 0:
            continue

        # 読み込んだ内容に[]が含まれているためそれを取り除く.
        replace_row = remove_symbols(str(row))
        split_rows = str.split(replace_row, ',')

        # コンマ区切りでSplitした内容をクラスに格納.
        new_parts = Parts(split_rows[0], split_rows[1], split_rows[2])


def remove_symbols(original: str):
    # 読み込んだ内容に[]、半角スペースが含まれているためそれを取り除く.
    remove_left_bracket = str.replace(original, '[', '')
    remove_right_bracket = str.replace(remove_left_bracket, ']', '')
    remove_space = str.replace(remove_right_bracket, ' ', '')
    return remove_space

main()

  • 「with」を使用することで、C#の「using」と同様ファイルの使用が終わったら自動で開放してくれるようになります。
  • 読み込んだ内容は「[‘PartsNo’, ‘PartsName’, ‘Description’, ‘’]」のように [ と ] が含まれるため、それを取り除く必要があります
    (もう少し上手いやり方がありそうな気はしています)。
  • 今回は読み込んだ内容をわざわざクラスに入れる必要はあまりないのですが、形式や文字数に制限がある場合などはクラス格納 -> チェックとした方が良さそうです。

Jsonファイルとして出力

クラスのデータをDictionaryデータとして受け取る

このままクラスをJsonに変換できると良いのですが(C#Javaのように)、
Pythonでは難しいようです。

そのため、クラスとして格納したデータを受け取って、Dictionaryとして返すようにします。

parts_dictionary_factory.py

 # -*- coding: utf-8 -*-
from parts import Parts


class PartsDictionaryFactory:

    def create(self, target_parts: Parts):
        if type(target_parts) != Parts:
            return

        return {"PartsNo": target_parts.parts_no, "PartsName": target_parts.parts_name,
                "Description": target_parts.description}

main.py

 # -*- coding: utf-8 -*-
import csv
from parts import Parts
from parts_dictionary_factory import PartsDictionaryFactory


def main():
    parts_dictionary = []
    dictionary_creator = PartsDictionaryFactory()

    with open(f'parts.csv', encoding='utf-8') as csvFile:
        for index, row in enumerate(csv.reader(csvFile)):
            if index <= 0:
                continue
            replace_row = remove_brackets(str(row))
            split_rows = str.split(replace_row, ',')
            new_parts = Parts(split_rows[0], split_rows[1], split_rows[2])

            # Dictionaryのデータに変換したものをDictionaryに追加.
            parts_dictionary.append(dictionary_creator.create(new_parts))

~省略~

Jsonファイルの出力

あとはこれまで取得・変換した文字列をJsonに置き換えて、 ファイルとして出力するだけです。

main.py

# -*- coding: utf-8 -*-
import json
import csv
from parts import Parts
from parts_dictionary_factory import PartsDictionaryFactory


def main():
    parts_dictionary = []
    dictionary_creator = PartsDictionaryFactory()

    with open(f'parts.csv', encoding='utf-8') as csvFile:

        for index, row in enumerate(csv.reader(csvFile)):
            if index <= 0:
                continue
            replace_row = remove_symbols(str(row))
            split_rows = str.split(replace_row, ',')
            new_parts = Parts(split_rows[0], split_rows[1], split_rows[2])
            parts_dictionary.append(dictionary_creator.create(new_parts))

    meta_parts = {"ModelName": "Model1", "PartsList": parts_dictionary}

    # 文字列をJson形式に変換.
    json_text = json.dumps(meta_parts)

    # ファイル出力.
    with open(f'parts.json', 'w') as file_object:
        file_object.write(json_text)
        file_object.flush()


def remove_symbols(original: str):
    remove_left_bracket = str.replace(original, '[', '')
    remove_right_bracket = str.replace(remove_left_bracket, ']', '')
    remove_space = str.replace(remove_right_bracket, ' ', '')
    return remove_space


main()

結果として下記のようなファイルが作成されます。

parts.json

{
  "ModelName": "Model1",
  "PartsList": [
    {
      "PartsNo": "'No.1'",
      "PartsName": "'Parts1'",
      "Description": "'\u4e00\u3064\u76ee\u306e\u30d1\u30fc\u30c4\u3067\u3059'"
    },
    {
      "PartsNo": "'No.2'",
      "PartsName": "'Parts2'",
      "Description": "'\u4e8c\u3064\u76ee\u306e\u30d1\u30fc\u30c4\u3067\u3059'"
    }
  ]
}
  • 実際は改行されずに出力されますが、見やすさのために編集しています。

UnityでJsonファイルを読み込む

UnityでJsonのデータをクラスに格納するのは以前もやりましたが、
今回もJsonUtilityを使用します。

まずはJsonのデータを格納するクラスの準備から。

今回はJsonの中に配列(PythonでDictionaryとして出力したもの)が含まれており、
これを格納する Parts というクラスと、
Parts のリスト、及びメタ情報(ModelName)を格納する MetaParts というクラスを作成します。
(名前の酷さはry)

Parts.cs

using System;

[Serializable]
public class Parts
{
    public string PartsNo;
    public string PartsName;
    public string Description;
}

MetaParts.cs

using System;
using System.Collections.Generic;

[Serializable]
public class MetaParts
{
    public string ModelName;
    public List PartsList;
}
  • Jsonのデータを格納するクラスには [Serializable] をつけておく必要があり、
    これがないとうまくデータを格納できませんでした。

ファイルの読み込みとJsonデータからの変換

あとはJsonファイルを読み込み、クラスに格納します。
JsonファイルはUnityEditorで動かす場合は Assets フォルダ直下、Windowsで出力した場合は プロジェクト名_Data フォルダの直下に置きます。

FileLoader.cs

using System.IO;
using System.Text;
using UnityEngine;

public class FileLoader
{
    private readonly string filePath;
    public FileLoader()
    {
        filePath = Application.dataPath + @"/parts.json";
    }
    public MetaParts LoadFromJson()
    {
        if (File.Exists(filePath))
        {
      // ファイルの読み込み.
            using (var reader = new StreamReader(filePath, Encoding.UTF8))
            {
        // ファイルの内容は一括で読み込む.
                var readText = reader.ReadToEnd();
                if (readText.Length > 0)
                {
          // Jsonからの変換に関するエラーチェックは省略.
                    return JsonUtility.FromJson(readText);
                }
            }
        }
        return new MetaParts();
    }
}

あとはこのクラスを読んであげればOKです。

終わりに

Pythonを使うことで、さらっと処理が書けるのは良いと感じます。

ただ、C#でやるように常に型を意識して書きたいのならちょっと厳しいかもしれませんね。

その辺り、Pythonならではの便利なやり方というものがあると思うので、
それを身につけられたらな、と思います。

参照

Python

Unity