階層構造をもつJSONのデータをCSVファイルの1行として保存するプログラムのメモ. ネットでは配列と同じような構造のJSONデータをCSVファイルに変換する例はよく見るけど, 入れ子になってるデータでもCSVファイルに保存したい.
- 階層構造をもつJSONのデータをCSVファイルの1行として保存する
- 保存したCSVファイルから逆にJSONに戻せるようにする
- 構造は未知としてライブラリ化する
作戦は,クラスオブジェクトをJSON化して,JSONのkeyはCSVのヘッダに格納して,valueは対応する列のセルに格納する. 階層構造を保つ場合は,key1:key2のように階層構造の親子関係を「:」で接続して表現する.
想定するJSONの階層構造例
想定するJSONはこれ.中括弧が3重になっていることから分かるように3階層になっている.
{"a":"aho","b":{"a":1,"b":2,"c":{"c":2,"d":3}}}
これをこんな感じのCSVに変換したい.ここではオブジェクト1個分だからCSVファイルではヘッダ1行とデータ1行.
#a,b:a,b:b,b:c:c,b:c:d aho,1,2,2,3
使い方
// JSONデータ(入力方法は省略) // {"a":"aho","b":{"a":1,"b":2,"c":{"c":2,"d":3}}} string json_txt = .... ; // JSON -> CSV // JSONからCSVのヘッダとデータ1行に変換 string csv_header = JsonCsv.JsonToCsvHeader(json_txt); string csv_row = JsonCsv.JsonToCsvRow(json_txt); Console.WriteLine(csv_header+csv_row); // CSV -> JSON // CSVヘッダからDictionary<string, object>に変換(key情報のみ格納) var dict = JsonCsv.CsvHeaderToDict(csv_header+'\n'); // Dictionary<string, object>からJSONに変換(途中結果の確認用) string json_dict = JsonCsv.DictToJson(dict); // key情報とvalue情報をあわせてJSONに変換 string json_out = JsonCsv.CsvRowToJson(dict, csv_row+'\n'); Console.WriteLine(json_dict); Console.WriteLine(json_out);
{"a":"aho","b":{"a":1,"b":2,"c":{"c":2,"d":3}}}
JSONからCSV
JSONからCSVヘッダ
階層構造を処理するためにDictionary<string, object>を用いて, objectにDictionary<string, object>を代入することでツリー構造を作る. ツリー構造を巡回するために再帰関数extractHeaderをJsonToCsvHeaderから呼び出す仕組み.
public static string JsonToCsvHeader(string json_txt){ Dictionary<string, object> dic = Json.Deserialize(json_txt) as Dictionary<string, object>; string csv_txt = "#"; csv_txt += extractHeader("", (Dictionary<string, object>)dic); return csv_txt + '\n'; }
// #a,b:a,b:b,b:b:c:c,b:c:d static string extractHeader(string parent, Dictionary<string, object> dic){ string csv_txt = ""; bool is_first = true; foreach(KeyValuePair<string, object> pair in dic) { //最初以外はカンマで区切る if(!is_first){ csv_txt += ","; } is_first = false; Type type = pair.Value.GetType(); if( type == typeof(Dictionary<string, object>) ) { csv_txt += extractHeader(parent + pair.Key + ':', (Dictionary<string, object>)pair.Value); } else{ csv_txt += (parent + pair.Key); Debug.Log(parent + pair.Key); } } return csv_txt; }
JSONからCSVデータ
public static string JsonToCsvRow(string json_txt){ Dictionary<string, object> dic = Json.Deserialize(json_txt) as Dictionary<string, object>; string csv_txt = extractRow((Dictionary<string, object>)dic); return csv_txt + '\n'; }
static string extractRow(Dictionary<string, object> dic){ string csv_txt = ""; bool is_first = true; foreach(KeyValuePair<string, object> pair in dic) { //最初以外はカンマで区切る if(!is_first){ csv_txt += ","; } is_first = false; Type type = pair.Value.GetType(); if( type == typeof(Dictionary<string, object>) ) { csv_txt += extractRow((Dictionary<string, object>)pair.Value); } else{ csv_txt += pair.Value; } } return csv_txt; }
CSVからJSON
CSVのヘッダをDictionaryに格納
ここでもDictionaryを使ってツリー構造を構築する. CSVのヘッダからDictionary<string, object>に一旦変換する. addDictItem関数は再帰処理の関数.
public static Dictionary<string, object> CsvHeaderToDict(string csv_txt) { var rows = csv_txt.Split('\n'); string[] header = rows[0].Split(','); header[0] = header[0].Trim('#'); Dictionary<string, object> dict = new Dictionary<string, object>(); for(int i = 0; i<header.Length; i++) { var keys = header[i].Split(':'); addDictItem(keys, 0, ref dict); } return dict; }
static void addDictItem(string [] keys, int idx, ref Dictionary<string, object> dict){ //2つ以上のkey if(keys.Length > idx+1) { //追加 if(dict.ContainsKey(keys[idx])){ var child = (Dictionary<string, object>) dict[ keys[idx] ]; addDictItem(keys, idx+1, ref child); } //新規 else{ var child = new Dictionary<string, object>(); dict[ keys[idx] ]= child; addDictItem(keys, idx+1, ref child); } } //1つのkey else{ dict[ keys[idx] ]=""; } }
CSVのデータとDictionaryからJSONに変換
一旦Dictionaryが出来てしまえば簡単. CSVのデータ1行(csv_row)とヘッダ情報を格納したDictionaryとでJSON形式のデータを復元する.
public static string CsvRowToJson(Dictionary<string, object> dic, string csv_row) { csv_row = csv_row.Trim(); var cols = csv_row.Split(','); int i_col = 0; string json_txt = "{"; bool is_first = true; foreach(KeyValuePair<string, object> pair in dic) { //最初以外はカンマで区切る if(!is_first){ json_txt += ","; } is_first = false; //子ノードを持っているとき Type type = pair.Value.GetType(); if( type == typeof(Dictionary<string, object>) ) { string pattern = "[^,]*,"; Regex regex = new Regex(pattern); int max_count = i_col; string row_next = regex.Replace(csv_row, "", max_count); json_txt += "\"" + pair.Key + "\":" + CsvRowToJson((Dictionary<string, object>)pair.Value, row_next); } //子ノードが無いとき else{ json_txt += "\"" + pair.Key + "\":\"" + cols[i_col] + "\""; } i_col++; } return json_txt + '}'; }