CORDEA blog

Android applications engineer

nim で json を扱う話

この記事は Nim Advent Calendar 2016 18 日目の記事です。

さて、ネタが被ったような気がしないでもないのですが
nim で json 扱うのが楽ですよって話をします。

json の扱いの簡単さはとても大切ですね。

今回例として使用した jsonjson.org の JSON Example の一番上のやつです。
http://json.org/example.html
この json を example.json として保存し、使用しています。

Version

Nim Compiler Version 0.15.2 (2016-12-18) [MacOSX: amd64]

json module を使う

json module を使えば json を簡単に扱うことができます

json -> JsonNode
import json

when isMainModule:
    let
        jsonStr = "example.json".readFile()
        jsonObj = parseJson jsonStr
    echo jsonObj["glossary"]["title"].str
  • Result
example glossary
JsonNode -> json
import json

when isMainModule:
    let
        jsonStr = "example.json".readFile()
        jsonObj = parseJson jsonStr
    echo $jsonObj
  • Result
{"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossL...

簡単ですが、用途によってはどこか物足りません。
やっぱり object に詰めて欲しいですよね。

そこで出てくるのが marshal module です。

marshal module を使う

marshal module を使えば json <-> object が実現できます
準備として example.nim として以下を用意しました

type
    Base = object of RootObj

    Example* = object of Base
        glossary*: Glossary

    Glossary* = object of Base
        title*: string
        GlossDiv*: GlossDiv

    GlossDiv* = object of Base
        title*: string
        GlossList*: GlossList

    GlossList* = object of Base
        GlossEntry*: GlossEntry

    GlossEntry* = object of Base
        ID*: string
        SortAs*: string
        GlossTerm*: string
        Acronym*: string
        Abbrev*: string
        GlossDef*: GlossDef
        GlossSee*: string

    GlossDef* = object of Base
        para*: string
        GlossSeeAlso*: seq[string]
json -> object
import example, marshal

when isMainModule:
    let
        jsonStr = "example.json".readFile()
        ex = to[Example] jsonStr
    echo ex.glossary.title
  • Result
example glossary
object -> json
import example, marshal

when isMainModule:
    let
        ex = Example()
    echo $$ex
  • Result
{"glossary": {"title": null, "GlossDiv": {"title": null, "GlossL...

自分で object に詰める

さきほどの marshal module はとてもシンプルなもので
例えば json の key と object の key を変えたい場合などに対応することはできません。(できなかったはず...)

そのため、json の key に proc などがあるとつらい感じになります。
そういう場合は、自分で object に詰めてしまうのが楽だと思います。

今回の json は string と array しか出てこないので実装はこの程度でした

import json, sequtils

proc `[]`(t: JsonNode, key: string): JsonNode =
    result = if t.hasKey(key): json.`[]`(t, key) else: nil

proc to[T: object](node: JsonNode, data: var T) =
    for k, v in data.fieldPairs:
        when v is string:
            v = node[k].getStr
        elif v is seq:
            if node[k] == nil:
                v = @[]
            else:
                v = node[k].elems.map(proc(x: JsonNode): string = x.str)
        else:
            node[k].to v
  • Usage
import example, json, sequtils

...

when isMainModule:
    let
        jsonStr = "example.json".readFile()
        jsonObj = parseJson jsonStr
    var ex: Example
    jsonObj.to ex
    echo ex

実装も簡単ですし、依存も減るし、諸々の仕様に柔軟に対応できるので
状況次第では自分で実装してしまうのも悪くないかなと思っています。

それでは。