YANGを使いこなす! (4) XMLだけじゃなくてJSONもつかいたい

# 今回やること
[YANGを使いこなす! Index](https://qiita.com/corestate55/items/70defe12f20f1e282fc0)

YANG チュートリアルは、ここまでデータ表現に XMLを使っていました。が、もちろん他のデータ形式を使いたいという話があるでしょう。JSON については pyang でデータ変換がサポートされています。ここでは、これまで XML を使ったオペレーションを JSON でやってみます。

これは [XmlJson · mbj4668/pyang Wiki](https://github.com/mbj4668/pyang/wiki/XmlJson) の内容になります。

# XML で書いたデータを JSON に変換する

## XML → JSON 変換

はじめに、XML → JSON の変換を見ていきます。XML の変換と言えば XSL ですね。ということで pyang で YANG から XSL を出力します。(YANG モデルにしたがって書かれた XML データ(インスタンス)を JSON に変換するための XSL; そのままだと整形されてないので中身を確認したい場合は `xmllint` します。)

```
corestate55@ytut:~/yang-tutorial/data$  pyang -f jsonxsl turing-machine.yang | xmllint --format - > tm-json.xsl
```

生成した XSL ファイルを使って XML データインスタンスを JSON に変換します。(あとで json 変換いじるので、ここではあえて `.orig.json` にしてあります。)

```
corestate55@ytut:~/yang-tutorial/data$ xsltproc -o turing-machine-rpc.orig.json tm-json.xsl turing-machine-rpc.xml
corestate55@ytut:~/yang-tutorial/data$ xsltproc -o turing-machine-config.orig.json tm-json.xsl turing-machine-config.xml 
```

## JSON Data Validation
JSON データインスタンスに対して validation をするには JSON schema が必要になります。[GitHub \- cmoberg/pyang\-json\-schema\-plugin: Pyang JSON Schema output plugin](https://github.com/cmoberg/pyang-json-schema-plugin) をつかうと YANG から JSON schema への変換もできます。インストールしましょう。

```
corestate55@ytut:~$ git clone https://github.com/cmoberg/pyang-json-schema-plugin.git
corestate55@ytut:~$ cd pyang-json-schema-plugin/
corestate55@ytut:~/pyang-json-schema-plugin$ python3 setup.py build
corestate55@ytut:~/pyang-json-schema-plugin$ sudo python3 setup.py install
```

JSON schema を出してみます。

```
corestate55@ytut:~/yang-tutorial/data$ pyang -f jsonschema turing-machine.yang -o tm-json.jsonschema
```

Schema ができたので、これをつかって XML から変換した JSON データインスタンスを validate できます。JSON schema を使った json validation tool はいろいろあります。(参考: [Implementations | JSON Schema](http://json-schema.org/implementations.html#validators))

* [ajv-cli - npm](https://www.npmjs.com/package/ajv-cli)
* [jsonlint-cli - npm](https://www.npmjs.com/package/jsonlint-cli)
* [JSON Schema Lint :: JSON Schema Validator](https://jsonschemalint.com/#/version/draft-04/markup/json)
    * オンラインのチェックツール

ツールのインストール

```
corestate55@ytut:~$ sudo apt install npm
corestate55@ytut:~$ sudo npm install -g ajv-cli
corestate55@ytut:~$ sudo npm install -g jsonlint-cli
```

JSON data validation

```
corestate55@ytut:~/yang-tutorial/data$ ajv validate -s  tm-json.jsonschema -d turing-machine-config.orig.json
turing-machine-config.orig.json valid
corestate55@ytut:~/yang-tutorial/data$ jsonlint-cli --validate tm-json.jsonschema turing-machine-config.json
corestate55@ytut:~/yang-tutorial/data$
```

## XML → JSON 変換のチューニング

いま Turing Machine クライアントは YANG ファイルから生成した gRPC API (protocol buffer) をつかって作ってあるのですが(これは次回で説明予定)、残念ながら yang-protobuf ツール (goyang) が出力する protobuf の JSON tag と pyang が出力する XSL の変換ルールがマッチしません……。なのでちょっと XSL ファイルの中身を変更します。[^ref_notice]

[^ref_notice]: あくまでもいま使うデータ分に対する雑な変更です。本当は、XSL や json schema を変更する場合、それによって扱うデータ全体の整合性がとれるかどうかを考えないといけないですね。

* pyang が出力した XSL file はテンプレート(`jsonxsl-templates.xsl`)を読み込んでいるので、これをコピーしてローカルに持ってきます。また、include するファイルをローカルの XSL template に変更します。([差分/tm-json.xsl.diff](https://gist.github.com/corestate55/c931596d287da2b1524d66eb83f106f8#file-tm-json-xsl-diff))
* あとは template で出力する値を変えていきます。([差分/jsonxsl-templates.xsl.diff](https://gist.github.com/corestate55/c931596d287da2b1524d66eb83f106f8#file-jsonxsl-templates-xsl-diff))
    * Namespace (`nsid`) をつかって json key 作るようになっていますがこの辺は変えちゃいます。
    * "hoge-fuga" 形式のワードを "hoge_fuga" になるように translate します。 

この XSL をつかって改めて XML データインスタンスを JSON に変換します。(変更前 XSLT でつくった JSON データとの差分を確認してみてください。)

```
corestate55@ytut:~/yang-tutorial/data$ cp /usr/local/share/yang/xslt/jsonxsl-templates.xsl .
corestate55@ytut:~/yang-tutorial/data$ patch < jsonxsl-templates.xsl.diff
patching file jsonxsl-templates.xsl
corestate55@ytut:~/yang-tutorial/data$ patch < tm-json.xsl.diff
patching file tm-json.xsl
corestate55@ytut:~/yang-tutorial/data$ xsltproc -o turing-machine-rpc.json tm-json.xsl turing-machine-rpc.xml
corestate55@ytut:~/yang-tutorial/data$ xsltproc -o turing-machine-config.json tm-json.xsl turing-machine-config.xml
corestate55@ytut:~/yang-tutorial/data$ diff -u turing-machine-rpc.orig.json turing-machine-rpc.json
...
corestate55@ytut:~/yang-tutorial/data$ diff -u turing-machine-config.orig.json turing-machine-config.json
...
```

Validation につかう JSON schema も同じように中身を変更してやらないと行けないですがここでは省略…

## JSON で操作してみる

Turing Machine クライアントは `-j` オプションで JSON data を扱うようになります。これまで試していた一連の操作を改めて試してみましょう。

```
corestate55@ytut:~/yang-tutorial$ ./tmclient -j
command> init data/turing-machine-rpc.json
command> config data/turing-machine-config.json
command> run
{
  "xmlns":  "urn:ietf:params:xml:ns:netconf:notification:1.0",
  "event_time": "2018-04-30 04:23:54.421365005 +0000 UTC  m=+41.792914416",
  "halted": {
    "state": 4
  }
}
command> get
...
```

# JSON で書いたデータを XML に変換する

逆 (JSON → XML) もできます。最初に、JSON を XML に変換するための driver file を作ります。

```
corestate55@ytut:~/yang-tutorial/data$ pyang -f jtox -o tm-json.jtox turing-machine.yang
```

次に、json2xml に変換したいデータインスタンスとドライバファイルを食わせると XML に変換されます。(そのままだと整形されないので xmllint で整形しています。)

```
corestate55@ytut:~/yang-tutorial/data$ json2xml tm-json.jtox turing-machine-config.orig.json | xmllint --format - | head
<?xml version="1.0" encoding="utf-8"?>
<nc:data  xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"  xmlns:tm="http://example.net/turing-machine">
  <tm:turing-machine>
    <tm:transition-function>
      <tm:delta>
        <tm:label>left summand</tm:label>
        <tm:input>
          <tm:state>0</tm:state>
          <tm:symbol>1</tm:symbol>
        </tm:input>
corestate55@ytut:~/yang-tutorial/data$
```

XML/JSON の変換に伴って名前空間の取り扱いがどうなるかは今回あまりフォローしていません。そこは注意が必要かもしれません。