nimのJSON操作

# 概要
NimのJSON処理をコツコツと
[json](https://nim-lang.org/docs/json.html)モジュールのメソッドを一通り試します

## 以下メモ

### JSONオブジェクトを構築

```nim
import json
import os,strutils,tables

block:
  # %* をつけてJsonオブジェクトを構築
  let jsonObj = %* { "key1": "value1", "key2": 1 }
  echo "# キーを指定して取得"
  echo "key1=>" & $jsonObj["key1"]
  echo "key2=>" & $jsonObj["key2"]
  echo "# 型を指定して取得"
  echo "key1=>" & jsonObj["key1"].getStr()
  echo "key2=>" & $jsonObj["key2"].getInt()
  echo "# 存在しないキーを指定"
  #echo "keyx=>" & $jsonObj["keyx"]
  echo "Error: unhandled exception: key not found: keyx [KeyError]"
  echo "# デフォルト値を指定して取得(これもエラー)"
  # echo "keyx=>" & $jsonObj["keyx"].getStr("")
  echo "Error: unhandled exception: key not found: keyx [KeyError]"
```

```shell-session
(stdout)
# キーを指定して取得
key1=>"value1"
key2=>1
# 型を指定して取得
key1=>value1
key2=>1
# 存在しないキーを指定
Error: unhandled exception: key not found: keyx [KeyError]
# デフォルト値を指定して取得(これもエラー)
Error: unhandled exception: key not found: keyx [KeyError]
```

### キーの存在チェック

```nim
import json
import os,strutils,tables

block:
  # %* をつけてJsonオブジェクトを構築
  let jsonObj = %* { "key1": "value1", "key2": 1 }
  echo "# キーの存在チェック"
  echo "hasKey[key1]=>" & $jsonObj.hasKey("key1")
  echo "hasKey[key3]=>" & $jsonObj.hasKey("key3")
  echo "hasKey[key1]=>" & $jsonObj.contains("key1")
  echo "hasKey[key3]=>" & $jsonObj.contains("key3")
```

```shell-session
(stdout)
# キーの存在チェック
hasKey[key1]=>true
hasKey[key3]=>false
hasKey[key1]=>true
hasKey[key3]=>false
```

### JSONのPretty表示

```nim
import json
import os,strutils,tables

block:
  let jsonObj = %* { "key1": "value1", "key2": 1 }
  echo jsonObj.pretty()
```

```shell-session
(stdout)
{
  "key1": "value1",
  "key2": 1
}
```

### JSONをファイルへ保存

```nim
import json
import os,strutils,tables

block:
  let jsonObj = %* { "key1": "value1", "key2": 1 }
  let f = open("sample.json", FileMode.fmWrite)
  f.write(jsonObj.pretty())
  f.close()
  echo "file exists=>" & $"sample.json".existsFile
```

```shell-session
(stdout)
file exists=>true
```

### JSONファイルの読み込み

```nim
import json
import os,strutils,tables

block:
  let jsonObj1 = %* { "key1": "value1", "key2": 1 }
  # さっき書き込んだファイルを読み込む
  let jsonObj2 = parseFile("sample.json")
  echo "compare json=>" , jsonObj1 == jsonObj2
  echo jsonObj2.pretty
```

```shell-session
(stdout)
compare json=>true
{
  "key1": "value1",
  "key2": 1
}
```

### JSON文字列からの構築

```nim
import json
import os,strutils,tables

block:
  let jsonObj1 = %* { "key1": "value1", "key2": 1 }
  # さっき書き込んだファイルを読み込む
  let jsonStr = parseFile("sample.json").pretty
  let jsonObj2 = parseJson(jsonStr)
  echo "compare json=>" , jsonObj1 == jsonObj2
```

```shell-session
(stdout)
compare json=>true
```

### JSONオブジェクトに属性を追加

```nim
import json
import os,strutils,tables

block:
  # %* をつけてJsonオブジェクトを構築
  let jsonObj = %* { "key1": "value1", "key2": 1 }
  # 文字列を追加
  jsonObj["key3"] = newJString("value3")
  echo "key3=>" & $jsonObj["key3"]
  # %* で追加
  jsonObj["key3"] = %* "value3"
  echo "key3=>" & $jsonObj["key3"]
  # 数値(int)を追加
  jsonObj["key3"] = newJInt(999)
  echo "key3=>" & $jsonObj["key3"]
  # %* で追加
  jsonObj["key3"] = %* 999
  echo "key3=>" & $jsonObj["key3"]
  # 配列を追加(newJArrayの引数は空なのでこれはできない))
  # jsonObj["key3"] = newJArray([1,2,3,4])
  # echo "key3=>" & $jsonObj["key3"]
  # %* で追加
  jsonObj["key3"] = %* [1,2,3,4]
  echo "key3=>" & $jsonObj["key3"]
```

```shell-session
(stdout)
key3=>"value3"
key3=>"value3"
key3=>999
key3=>999
key3=>[1,2,3,4]
```

### JSONのキー一覧を取得

```nim
import json
import os,strutils,tables

block:
  # %* をつけてJsonオブジェクトを構築
  let jsonObj = %* { "key1": "value1", "key2": 1 }
  let fields: OrderedTable[string,JsonNode] = jsonObj.getFields()
  var keys: seq[string] = @[]
  for key in fields.keys:
    keys.add key
  echo "keys=>" & keys
```

```shell-session
(stdout)
@["keys=>", "key1", "key2"]
```

### 属性の型を確認する

```nim
import json
import os,strutils,tables

block:
  # %* をつけてJsonオブジェクトを構築
  let jsonObj = %* { 
    "key1": "value1", 
    "key2": 1, 
    "key3": true, 
    "key4": 3.141592, 
    "key5": nil, 
    "key6": [11,2,3], 
    "key7": {"name": "object"}
  }
  # JsonNodeKindは省略可能
  echo jsonObj["key1"].kind == JsonNodeKind.JString
  echo jsonObj["key2"].kind == JsonNodeKind.JInt
  echo jsonObj["key3"].kind == JsonNodeKind.JBool
  echo jsonObj["key4"].kind == JsonNodeKind.JFloat
  echo jsonObj["key5"].kind == JsonNodeKind.JNull
  echo jsonObj["key6"].kind == JsonNodeKind.JArray
  echo jsonObj["key7"].kind == JsonNodeKind.JObject
```

```shell-session
(stdout)
true
true
true
true
true
true
true
```

### JSONのオブジェクト階層を探索しながら値を取得

```nim
import json
import os,strutils,tables

block:
  # %* をつけてJsonオブジェクトを構築
  let jsonObj = %* { 
    "parent": { 
      "child1": "value1",
      "child2": {
        "child3": [1,2,3]
      }
    }
  }
  # {}演算子を使って、親・子のキーを並べて指定する
  echo "parent.child1=>" & $jsonObj{"parent","child1"}
  echo "parent.child2.child3=>" & $jsonObj{"parent","child2","child3"}
```

```shell-session
(stdout)
parent.child1=>"value1"
parent.child2.child3=>[1,2,3]
```

### 属性の削除

```nim
import json
import os,strutils,tables

block:
  # %* をつけてJsonオブジェクトを構築
  let jsonObj = %* { "key1": "value1", "key2": 1 }
  # deleteで削除
  jsonObj.delete("key1")
  echo jsonObj.pretty
  echo "# もう一度deleteで削除->キーが無いのでエラー"
  echo """jsonObj.delete("key1")"""
  echo "Error: unhandled exception: key not in object [IndexError]"
```

```shell-session
(stdout)
{
  "key2": 1
}
# もう一度deleteで削除->キーが無いのでエラー
jsonObj.delete("key1")
Error: unhandled exception: key not in object [IndexError]
```

### JSONエスケープ処理

```nim
import json
import os,strutils,tables

block:
  echo "escapeJson=>" & escapeJson("test1\ntest2\n")
```

```shell-session
(stdout)
escapeJson=>"test1\ntest2\n"
```

### 整形しないでJson文字列を作成(toUgly)

```nim
import json
import os,strutils,tables

block:
  let jsonObj = %* { 
    "parent": { 
      "child1": "value1",
      "child2": {
        "child3": [1,2,3]
      }
    }
  }
  var result = ""
  toUgly(result,jsonObj)
  echo "toUgly=>" & result
```

```shell-session
(stdout)
toUgly=>{"parent":{"child1":"value1","child2":{"child3":[1,2,3]}}}
```

### イテレータ(items) 対象は配列オブジェクトのみ

```nim
import json
import os,strutils,tables

block:
  # %* をつけてJsonオブジェクトを構築
  let jsonObj = %* [ 
    {"key1": "value1"}, 
    {"key2": 1}, 
    {"key3": true}, 
    {"key4": 3.141592}, 
    {"key5": nil}, 
    {"key6": [1,2,3]}, 
    {"key7": {"name": "object"}}
  ]
  for item in jsonObj.items:
    echo "==================="
    echo $item
```

```shell-session
(stdout)
===================
{"key1":"value1"}
===================
{"key2":1}
===================
{"key3":true}
===================
{"key4":3.141592}
===================
{"key5":null}
===================
{"key6":[1,2,3]}
===================
{"key7":{"name":"object"}}
```

### イテレータ(mitems) 対象はミュータブルなJson配列オブジェクトのみ。

```nim
import json
import os,strutils,tables

block:
  # %* をつけてJsonオブジェクトを構築
  var jsonObj = %* [ 
    {"key1": "value1"}, 
    {"key2": 1}, 
    {"key3": true}, 
    {"key4": 3.141592}, 
    {"key5": nil}, 
    {"key6": [1,2,3]}, 
    {"key7": {"name": "object"}}
  ]
  for item in jsonObj.mitems:
    # 中身を変更できる
    item["message"] = %* "hello"
    echo "==================="
    echo $item
```

```shell-session
(stdout)
===================
{"key1":"value1","message":"hello"}
===================
{"key2":1,"message":"hello"}
===================
{"key3":true,"message":"hello"}
===================
{"key4":3.141592,"message":"hello"}
===================
{"key5":null,"message":"hello"}
===================
{"key6":[1,2,3],"message":"hello"}
===================
{"key7":{"name":"object"},"message":"hello"}
```

### イテレータ(pairs) 対象はオブジェクトのみ。

```nim
import json
import os,strutils,tables

block:
  # %* をつけてJsonオブジェクトを構築
  let jsonObj = %* { 
    "key1": "value1", 
    "key2": 1, 
    "key3": true, 
    "key4": 3.141592, 
    "key5": nil, 
    "key6": [1,2,3], 
    "key7": {"name": "object"}
  }
  for item in jsonObj.pairs:
    echo "==================="
    echo "key=>" & $item.key
    echo "val=>" & $item.val
```

```shell-session
(stdout)
===================
key=>key1
val=>"value1"
===================
key=>key2
val=>1
===================
key=>key3
val=>true
===================
key=>key4
val=>3.141592
===================
key=>key5
val=>null
===================
key=>key6
val=>[1,2,3]
===================
key=>key7
val=>{"name":"object"}
```

### イテレータ(mpairs) 対象はミュータブルなJsonオブジェクト

```nim
import json
import os,strutils,tables

block:
  # %* をつけてJsonオブジェクトを構築
  var jsonObj = %* { 
    "key1": "value1", 
    "key2": 1, 
    "key3": true, 
    "key4": 3.141592, 
    "key5": nil, 
    "key6": [1,2,3], 
    "key7": {"name": "object"}
  }
  # mpairsで列挙
  for item in jsonObj.mpairs:
    echo "==================="
    echo "key=>" & $item.key
    echo "val=>" & $item.val
    jsonObj[item.key] = %* "message"
  echo "書き換え結果=>" & jsonObj.pretty    
```

```shell-session
(stdout)
===================
key=>key1
val=>"value1"
===================
key=>key2
val=>1
===================
key=>key3
val=>true
===================
key=>key4
val=>3.141592
===================
key=>key5
val=>null
===================
key=>key6
val=>[1,2,3]
===================
key=>key7
val=>{"name":"object"}
書き換え結果=>{
  "key1": "message",
  "key2": "message",
  "key3": "message",
  "key4": "message",
  "key5": "message",
  "key6": "message",
  "key7": "message"
}
```

### JSONオブジェクトからオブジェクトに変換

```nim
import json
import os,strutils,tables

block:
  let jsonNode = parseJson("""
  {
    "person": {
      "name": "Nimmer",
      "age": 21
    },
    "list": [1, 2, 3, 4]
  }
  """)
  type
    Person = object
      name: string
      age: int
    Data = object
      person: Person
      list: seq[int]
  var data = to(jsonNode, Data)
  echo "data.person.name=>" & data.person.name
  echo "data.person.age=>" & $data.person.age
  echo "data.list=>" & $data.list
```

```shell-session
(stdout)
data.person.name=>Nimmer
data.person.age=>21
data.list=>@[1, 2, 3, 4]
```

### JSON配列内の要素を配列オブジェクトに変換

```nim
import json
import os,strutils,tables

block:
  let jsonNode = parseJson("""[
  {
    "person": {
      "name": "Nimmer",
      "age": 21
    },
    "list": [1, 2, 3, 4]
  }]
  """)
  type
    Person = object
      name: string
      age: int
    Data = object
      person: Person
      list: seq[int]
  var data = to(jsonNode, seq[Data])
  echo "data[0].person.name=>" & data[0].person.name
  echo "data[0].person.age=>" & $data[0].person.age
  echo "data[0].list=>" & $data[0].list
```

```shell-session
(stdout)
data[0].person.name=>Nimmer
data[0].person.age=>21
data[0].list=>@[1, 2, 3, 4]
```