LoginSignup
7
6

More than 5 years have passed since last update.

Pythonでjsonをフラットマップに変換する

Last updated at Posted at 2018-01-27

1. はじめに

jsonのobjectarray等の入れ子の任意データに一回でアクセスしたい要件があったため、キーでアクセス可能なマップ(Dict)に変換する処理を作成しました。

(仕様):必要に応じて変更してください。

  • 入れ子はキーの階層構造で示す。
  • 階層の区切りは/とする。
  • arrayの要素数はCOUNTで示す。

2. ソースコード

flatMap.py
# -*- coding: utf-8 -*-
import json
import datetime

# ★ポイント1
SEPARATOR = '/'
COUNT = 'COUNT'

# ★ポイント2
def convert_flat_map(keyStr, target, map):

    # ★ポイント3
    if isinstance(target, dict):
        for k in target.keys():
            convert_flat_map(keyStr + SEPARATOR + k, target[k], map)

    # ★ポイント4
    elif isinstance(target, list):
        map[keyStr + SEPARATOR + COUNT] = str(len(target))
        for i in range(len(target)):
            one = target[i]
            convert_flat_map(keyStr + SEPARATOR + str(i), one, map)

    # ★ポイント5
    # 参考(デバッグ)として分岐を分けているだけ。
    # 全て map[keyStr] = target で構わない。
    elif isinstance(target, int):
        map[keyStr] = target

    elif isinstance(target, float):
        map[keyStr] = target

    elif isinstance(target, datetime.date):
        map[keyStr] = target

    elif isinstance(target, unicode):
        map[keyStr] = target

    elif isinstance(target, str):
        map[keyStr] = target

    elif isinstance(target, bool):
        map[keyStr] = target

    elif target is None:
        map[keyStr] = target

    else:
        print('<other>' + str(target))

# test case 1 : http://json-schema.org/example/address.json
def test01():
    # ★ポイント6
    f = open('address.json', 'r')
    jsonData = json.load(f)
    map = {}
    convert_flat_map('', jsonData, map)
    for k in sorted(map.keys()):
        print(k + ' = ' + str(map[k]))

# test case 2 : http://json-schema.org/example/calendar.json
def test02():
    f2 = open('calendar.json', 'r')
    jsonData2 = json.load(f2)
    map2 = {}
    convert_flat_map('', jsonData2, map2)
    sorted(map2)
    for k in sorted(map2.keys()):
        print(k + ' = ' + map2[k])

if __name__=='__main__':

    # test
    test01()
    print("------------------------------")
    test02()

★ポイント1
入れ子の区切り文字とarrayの要素数を示す文字列を定義します。各自の要件に応じて変更してください。

★ポイント2
今回のポイントです。入れ子やarrayはその内部をさらに取り出すため、再帰処理で呼び出せるように関数を定義しました。

  • 第1引数 : マップ(Dict)のキーとなる文字列
  • 第2引数 : 解析対象のjsonデータ(Dict)
  • 第3引数 : 結果を格納するマップ(Dict)

★ポイント3
jsonデータのobjectはpythonではDictになるので、格納されている要素に対してconvert_flat_map()関数を再帰的に呼び出します。

★ポイント4
jsonデータのarrayはpythonではlistになるので、格納されている要素に対してconvert_flat_map()関数を再帰的に呼び出します。
また、listの場合は★ポイント1で定義した文字列で要素数も結果のマップ(Dict)に格納します。

★ポイント5
jsonデータでobjectarray以外は入れ子の終端となるので、結果のマップ(Dict)に格納します。
jsonのデータについては https://www.json.org/json-ja.html を参照ください。

(注意)
jsonのデータ型で条件分岐を分けていますがデバッグのためです。必要に応じて★ポイント5の条件分岐は纏めてください。

★ポイント6
動作確認用の処理です。呼び出す際はconvert_flat_map()の第1引数に空文字を指定します。
なお、ここで第1引数に文字列を指定した場合、キーのプレフィックスとして付与されます。

3. 動作確認

address.json
{
    "$schema": "http://json-schema.org/draft-06/schema#",
    "description": "An Address following the convention of http://microformats.org/wiki/hcard",
    "type": "object",
    "properties": {
        "post-office-box": { "type": "string" },
        "extended-address": { "type": "string" },
        "street-address": { "type": "string" },
        "locality":{ "type": "string" },
        "region": { "type": "string" },
        "postal-code": { "type": "string" },
        "country-name": { "type": "string"}
    },
    "required": ["locality", "region", "country-name"],
    "dependencies": {
        "post-office-box": ["street-address"],
        "extended-address": ["street-address"]
    }
}
address.jsonの変換結果
/$schema = http://json-schema.org/draft-06/schema#
/dependencies/extended-address/0 = street-address
/dependencies/extended-address/COUNT = 1
/dependencies/post-office-box/0 = street-address
/dependencies/post-office-box/COUNT = 1
/description = An Address following the convention of http://microformats.org/wiki/hcard
/properties/country-name/type = string
/properties/extended-address/type = string
/properties/locality/type = string
/properties/post-office-box/type = string
/properties/postal-code/type = string
/properties/region/type = string
/properties/street-address/type = string
/required/0 = locality
/required/1 = region
/required/2 = country-name
/required/COUNT = 3
/type = object
calendar.json
{
    "$schema": "http://json-schema.org/draft-06/schema#",
    "description": "A representation of an event",
    "type": "object",
    "required": [ "dtstart", "summary" ],
    "properties": {
        "dtstart": {
            "format": "date-time",
            "type": "string",
            "description": "Event starting time"
        },
        "dtend": {
            "format": "date-time",
            "type": "string",
            "description": "Event ending time"
        },
        "summary": { "type": "string" },
        "location": { "type": "string" },
        "url": { "type": "string", "format": "uri" },
        "duration": {
            "format": "time",
            "type": "string",
            "description": "Event duration"
        },
        "rdate": {
            "format": "date-time",
            "type": "string",
            "description": "Recurrence date"
        },
        "rrule": {
            "type": "string",
            "description": "Recurrence rule"
        },
        "category": { "type": "string" },
        "description": { "type": "string" },
        "geo": { "$ref": "http://json-schema.org/geo" }
    }
}
calendar.jsonの変換結果
/$schema = http://json-schema.org/draft-06/schema#
/description = A representation of an event
/properties/category/type = string
/properties/description/type = string
/properties/dtend/description = Event ending time
/properties/dtend/format = date-time
/properties/dtend/type = string
/properties/dtstart/description = Event starting time
/properties/dtstart/format = date-time
/properties/dtstart/type = string
/properties/duration/description = Event duration
/properties/duration/format = time
/properties/duration/type = string
/properties/geo/$ref = http://json-schema.org/geo
/properties/location/type = string
/properties/rdate/description = Recurrence date
/properties/rdate/format = date-time
/properties/rdate/type = string
/properties/rrule/description = Recurrence rule
/properties/rrule/type = string
/properties/summary/type = string
/properties/url/format = uri
/properties/url/type = string
/required/0 = dtstart
/required/1 = summary
/required/COUNT = 2
/type = object

4. さいごに

今回はjsonをフラットマップ(Dict)に変換する処理について説明しました。この処理が必要かどうかは要件によって異なりますが、1つのキーを利用して1回で任意のデータにアクセスできるのは個人的には便利かと思います。

7
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6