PythonにおいてのJSONファイルの取扱いあれこれ

# 1, Pythonのファイル出力

Pythonで作成した出力結果をまとめるのに便利なJSONファイルの紹介です．
後ほど説明しますが，Pythonのリストや辞書形式をそのまま保存できるため，
入出力用の変換がほとんど必要ありません．
また，入出力用のimport jsonが入っているので，別ライブラリのインストールは不要です．

# 2, リスト(list)と辞書(dict)について

まず，リストと辞書のことを知っていないと辛いので，書いておきます．
「知ってるよ！」という人は飛ばしてください．

## 2.1 リスト

リストは，C++で言うところの配列だと思ってください（自分の理解）．
Pythonでは，[]で初期化するとその変数はリストの扱いになります．
下の例では，変数aをリストとして初期化しています．
for文の中では，リストの末尾にiの値を追加しています．
各要素へのアクセスは，[]内にリストの何番目かを指定することでできます．

```python
a = [] #リストとして初期化

# 0~9までの値を格納
for i in range(10):
    a.append(i)

print(a)
#出力：[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

#要素の参照
print(a[0]) #出力：0
print(a[1]) #出力：1
print(a[2]) #出力：2
```

## 2.2 辞書

辞書とは，C++でいうところのstd::mapのようなもので，リストの要素番号の代わりに，キーという格納する要素とペアになる値により各要素を管理します．
ややこしいですが，{}で初期化するとその変数は辞書の扱いになります．
下の例では，変数bを辞書として初期化しています．

```py3
b = {} #辞書として初期化

# jをキーとして，j*10の値を格納
for j in range(10):
    b[j] = j * 10

print(b)
#出力：{0: 0, 1: 10, 2: 20, 3: 30, 4: 40, 5: 50, 6: 60, 7: 70, 8: 80, 9: 90}

print(b[0]) #0
print(b[1]) #10
print(b[2]) #20
```

キーに文字列を使用することもできます．
さらに，辞書の中に辞書が作れたりします．
ただし，複数の同じキーは使えないので注意が必要です．

```py3
ys = {}

ys["honoka"] = {"B": 78, "W": 58, "H": 82}
ys["eri"]    = {"B": 88, "W": 60, "H": 84}
ys["kotori"] = {"B": 80, "W": 58, "H": 80}
ys["umi"]    = {"B": 76, "W": 58, "H": 80}
ys["rin"]    = {"B": 75, "W": 59, "H": 80}
ys["maki"]   = {"B": 78, "W": 56, "H": 83}
ys["nozomi"] = {"B": 90, "W": 60, "H": 82}
ys["hanayo"] = {"B": 82, "W": 60, "H": 83}
ys["niko"]   = {"B": 74, "W": 57, "H": 79}

print(ys)
#出力：{'nozomi': {'H': 82, 'B': 90, 'W': 60}, 'umi': {'H': 80, 'B': 76, 'W': 58}, 'niko': {'H': 79, 'B': 74, 'W': 57}, 'kotori': {'H': 80, 'B': 80, 'W': 58}, 'maki': {'H': 83, 'B': 78, 'W': 56}, 'eri': {'H': 84, 'B': 88, 'W': 60}, 'hanayo': {'H': 83, 'B': 82, 'W': 60}, 'rin': {'H': 80, 'B': 75, 'W': 59}, 'honoka': {'H': 82, 'B': 78, 'W': 58}}

print(ys["rin"]["W"])
#出力：59
```

上記の例で注目してもらいたいのは，出力結果が格納順になっていないところです．
これは，Pythonの辞書では格納した順番が保持されないためです．キーでアクセスするから順番関係ないよねって考えです．
そこで，格納順番を保持したい場合は，初期化に少し工夫をします．

```py3
import collections as cl #collectionsをインポート

#ys = {}
#{}の代わりに，collections.OrderedDict()で初期化する
ys = cl.OrderedDict()

ys["honoka"] = cl.OrderedDict({"B": 78, "W": 58, "H": 82})
ys["eri"]    = cl.OrderedDict({"B": 88, "W": 60, "H": 84})
ys["kotori"] = cl.OrderedDict({"B": 80, "W": 58, "H": 80})
ys["umi"]    = cl.OrderedDict({"B": 76, "W": 58, "H": 80})
ys["rin"]    = cl.OrderedDict({"B": 75, "W": 59, "H": 80})
ys["maki"]   = cl.OrderedDict({"B": 78, "W": 56, "H": 83})
ys["nozomi"] = cl.OrderedDict({"B": 90, "W": 60, "H": 82})
ys["hanayo"] = cl.OrderedDict({"B": 82, "W": 60, "H": 83})
ys["niko"]   = cl.OrderedDict({"B": 74, "W": 57, "H": 79})

print(ys)
#出力：OrderedDict([('honoka', OrderedDict([('B', 78), ('H', 82), ('W', 58)])), ('eri', OrderedDict([('B', 88), ('H', 84), ('W', 60)])), ('kotori', OrderedDict([('B', 80), ('H', 80), ('W', 58)])), ('umi', OrderedDict([('B', 76), ('H', 80), ('W', 58)])), ('rin', OrderedDict([('B', 75), ('H', 80), ('W', 59)])), ('maki', OrderedDict([('B', 78), ('H', 83), ('W', 56)])), ('nozomi', OrderedDict([('B', 90), ('H', 82), ('W', 60)])), ('hanayo', OrderedDict([('B', 82), ('H', 83), ('W', 60)])), ('niko', OrderedDict([('B', 74), ('H', 79), ('W', 57)]))])
```

上記のcollections.OrderedDict()は，順序付き辞書を作成する関数です．
これで初期化することにより，入力順序を保った辞書が出来上がります．

# 3 JSONの形式

さて，肝心のJSON形式はどうなっているかというと，上記の辞書形式そのままの形式になっています．
ファイルの中身は，

```json
{(キー1)：(要素1),(キー2)：(要素2)，…}
```

の順で並んでいる形式なので，辞書≒JSON形式と言えます．
Python内でJSONファイルを扱う場合は，JSONファイル用のライブラリをインポートする必要があります．

```py3
import json
```


# 4 サンプル

以下はサンプルです．
以下のJSONファイルを使って説明していきます．


```json:myu_s.json
{
    "honoka": {
        "BWH": [
            78,
            58,
            82
        ],
        "height": 157
    },
    "eri": {
        "BWH": [
            88,
            60,
            84
        ],
        "height": 162
    },
    "kotori": {
        "BWH": [
            80,
            58,
            80
        ],
        "height": 159
    },
    "umi": {
        "BWH": [
            76,
            58,
            80
        ],
        "height": 159
    },
    "rin": {
        "BWH": [
            75,
            59,
            80
        ],
        "height": 155
    },
    "maki": {
        "BWH": [
            78,
            56,
            83
        ],
        "height": 161
    },
    "nozomi": {
        "BWH": [
            90,
            60,
            82
        ],
        "height": 159
    },
    "hanayo": {
        "BWH": [
            82,
            60,
            83
        ],
        "height": 156
    },
    "niko": {
        "BWH": [
            74,
            57,
            79
        ],
        "height": 154
    }
}
```


## 4.1 ファイル読み込み

```py3
import json

def main():
    f = open("myu_s.json", 'r')
    
    #ココ重要！！
    json_data = json.load(f) #JSON形式で読み込む

    name_list = ["honoka","eri","kotori","umi","rin","maki","nozomi","hanayo","niko"]
    for name in name_list:
        print("{0:6s} 身長：{1}cm BWH: ".format(name,json_data[name]["height"]),end="\t")
        for i in range(len(json_data[name]["BWH"])):
            print("{}".format(json_data[name]["BWH"][i]),end="\t")
        print()

if __name__=='__main__':
    main()
```

### 出力結果

```py3
honoka 身長：157cm BWH: 	78	58	82	
eri    身長：162cm BWH: 	88	60	84	
kotori 身長：159cm BWH: 	80	58	80	
umi    身長：159cm BWH: 	76	58	80	
rin    身長：155cm BWH: 	75	59	80	
maki   身長：161cm BWH: 	78	56	83	
nozomi 身長：159cm BWH: 	90	60	82	
hanayo 身長：156cm BWH: 	82	60	83	
niko   身長：154cm BWH: 	74	57	79		
```


## 4.2 ファイル内の表示

さて，ファイルの読み込みでは各要素を読み込んで自分で表示を整形しました．
しかし，読み込んだJSONファイルの中身をサラッと確認したいときなどにいちいち，
上のように表示するのは面倒です（処理では必要ですが）．

そんな時のために，整形して表示してくれるjson.dumps関数があります．

```py3
import json

def main():
    f = open("myu_s.json", 'r')
    json_data = json.load(f)

    #ココ重要！！
    # インデントありで表示
    print("{}".format(json.dumps(json_data,indent=4)))

if __name__=='__main__':
    main()
```

### 出力結果

dumps関数のindentで指定した分のスペースで区切って表示してくれます．
indentを何も指定しないと，１行で表示されるので注意してください．
また，通常の辞書形式で読み込まれるので，順序が変わります．

```py3
{
    "umi": {
        "height": 159,
        "BWH": [
            76,
            58,
            80
        ]
    },
    "hanayo": {
        "height": 156,
        "BWH": [
            82,
            60,
            83
        ]
    },
    "honoka": {
        "height": 157,
        "BWH": [
            78,
            58,
            82
        ]
    },
    "rin": {
        "height": 155,
        "BWH": [
            75,
            59,
            80
        ]
    },
    "maki": {
        "height": 161,
        "BWH": [
            78,
            56,
            83
        ]
    },
    "nozomi": {
        "height": 159,
        "BWH": [
            90,
            60,
            82
        ]
    },
    "niko": {
        "height": 154,
        "BWH": [
            74,
            57,
            79
        ]
    },
    "eri": {
        "height": 162,
        "BWH": [{
    "umi": {
        "height": 159,
        "BWH": [
            76,
            58,
            80
        ]
    },
    "hanayo": {
        "height": 156,
        "BWH": [
            82,
            60,
            83
        ]
    },
    "honoka": {
        "height": 157,
        "BWH": [
            78,
            58,
            82
        ]
    },
    "rin": {
        "height": 155,
        "BWH": [
            75,
            59,
            80
        ]
    },
    "maki": {
        "height": 161,
        "BWH": [
            78,
            56,
            83
        ]
    },
    "nozomi": {
        "height": 159,
        "BWH": [
            90,
            60,
            82
        ]
    },
    "niko": {
        "height": 154,
        "BWH": [
            74,
            57,
            79
        ]
    },
    "eri": {
        "height": 162,
        "BWH": [
            88,
            60,
            84
        ]
    },
    "kotori": {
        "height": 159,
        "BWH": [
            80,
            58,
            80
        ]
    }
}
            88,
            60,
            84
        ]
    },
    "kotori": {
        "height": 159,
        "BWH": [
            80,
            58,
            80
        ]
    }
}
```

## 4.3 ファイルの書き込み

最後は，ファイルの書き込みについてです．
ファイルに書き込む際は，万が一，他のプログラムからファイルを読み込むことになると順番が入れ替わってしまうことによる不具合が起きる可能性があるので，collections.OrderedDictで順序を指定した辞書を作成し，書き込みます．

今回は，わかりやすいように名前リスト，身長リスト，スリーサイズリストの３つのリストに分かれているデータを１つのJSON構造にして，書き込みを行っています．

ファイルに書き込む際は，json.dump関数を使用します．表示の時はdump「s」
です．違いますので注意してください．
引数の１つ目に，書き込む辞書を入れ，２つ目に書き込む先のファイルを指定します．

```py3
import json
import collections as cl

def main():
    name_list = ["honoka", "eri", "kotori", "umi", "rin", "maki", "nozomi", "hanayo", "niko"]
    height = [157,162,159,159,155,161,159,156,154]
    BWH = [[78, 58, 82],[88, 60, 84],[80, 58, 80],[76, 58, 80],
           [75, 59, 80],[78, 56, 83],[90, 60, 82],[82, 60, 83],[74, 57, 79]]

    ys = cl.OrderedDict()
    for i in range(len(name_list)):
        data = cl.OrderedDict()
        data["BWH"] = BWH[i]
        data["height"] = height[i]

        ys[name_list[i]] = data

    #print("{}".format(json.dumps(ys,indent=4)))

    fw = open('myu_s.json','w')
    #ココ重要！！
    # json.dump関数でファイルに書き込む
    json.dump(ys,fw,indent=4)

if __name__=='__main__':
    main()
```

### 出力結果

※myu_s.jsonと同じ結果なので割愛します．


# 5 まとめ

以上がJSONファイルの取扱い方です．ほぼ，独学なので間違っているところがあるかもですが，ご了承ください．
間違い，新発見などがありましたら順次修正・追加していきます．

# 6 参考にしたサイト

[1] [Python入門-リスト・タプル・辞書](http://www.tohoho-web.com/python/list.html) 
[2] [[python] JSONファイルのフォーマットを整えてDumpする](http://qiita.com/Hyperion13fleet/items/7129623ab32bdcc6e203)
[3] [【Python入門】JSON形式データの扱い方](http://qiita.com/Morio/items/7538a939cc441367070d)
[4] [pythonの辞書で要素の追加順を保存する](http://ridedefy1.hatenablog.jp/entry/2012/10/17/011014)