jsonもUbuntuも知らない状態からazure-iot-sdk-cのparsonを使ってjson解析できるようになるまで

azure-iot-sdk-cのparsonを使ってjson解析しなきゃならなくなった人へ贈ります。

結論を書くと、janssonを弄ってみるとparsonの理解が捗ると思います。
json理解している人はjansson弄らなくてもparsonが分かると思いますが、json理解できていない人にはjanssonがお勧めです。

## 私のレベルについて

Linuxは分かりません。
C言語の勉強をfedora core(6だったかな？)を使ってしましたが、それも10年くらい昔の話し。
現在はもっぱらWindowsでVisual Studio。
Linuxのコマンドなんて殆ど記憶に残っていません(^_^;)

jsonもこの時初めて知りました。
ついでにgitもついでに知りました。

## 本文を執筆するのに使った環境について

本書執筆のためにVM(VMware)でUbuntuを動かして、Tera Termからアクセスしています。

環境は以下の通り。

    Windows 7 Professional 64bit
    VMware(R) Workstation 12 Player 12.1.1 build-3770994
    Ubuntu 16.04 LTS
    gcc --version (Ubuntu 5.3.1-14ubuntu2) 5.3.1 20160413
    Tera Term Version 4.93

## parsonについて

parsonはC言語で書かれたjsonのパーサ (parser)です。

*特徴は以下の通りです。(gitのReadMe.md曰く)*

- Full JSON support<br />
    いろいろな形式のjsonを扱えます。
- Lightweight (only 2 files)<br />
  2ファイルしかないので軽量です。
- Simple API<br />
  シンプルです。(本当に？？？)
- Addressing json values with dot notation <br />
  ドット(.)を使ってオブジェクトや配列にアクセスできます
- C89 compatible<br />
  移植性を考慮してC89で記載されています。<br />
  azure-iot-sdk-cはC99で記載されているので、こちらの方がよりいろいろな環境で使えるはず。。
- Test suites<br />
  テストスイートが揃っています。

*私が思っている特徴もついでに記載します*

- サンプルがないので使い方が分かりづらい<br />
サンプルがありません。関数の説明がparson.hにコメントであるだけです。
- UTF-8に対応<br />
parson.hとparson.cを眺めているとUTF-8(BOMあり、なし)に対応しているのが分かります。
そのため、日本語もOK(のはず)です。

*あと多分、以下の特徴も持っています*

- 動的にメモリ確保してjsonを扱っている。


## jsonについて

jsonについての詳細は以下のページを参照しましょう。

[jsonの紹介](http://www.json.org/json-ja.html)

たぶん分からない(‥;)ので、使ってみましょう↓↓

## さっそく使ってみましょう！

早速parsonを使ってみましょう！

### parsonの入手

parsonはgitから入手します。

ホームディレクトリで以下を実行します。

``` sh

git clone https://github.com/kgabis/parson.git

```

うまくいけばparsonディレクトリができているはずです。
ダメならたぶんネットワーク関連なのでproxy等を見直しましょう。

入手できたら以下の通りになっているはずです。

``` sh

ubuntu:~$ cd parson/
ubuntu:~/parson$ ls
Makefile  README.md  package.json  parson.c  parson.h  tests  tests.c

```

### parsonを使ってプログラムを作ってみましょう。

parsonを使ってプログラムを作ってみましょう。

- 解析対象は良いサンプルが見つからなかったので *package.json* を使います。
- Makefileはよく分からないので要らないところを消して再利用します。
- jsonファイルのファイル名はargv[1]で受け取ります。


package.jsonの中身は以下の通りです。

``` json:package.json

{
  "name": "parson",
  "version": "0.0.0",
  "repo": "kgabis/parson",
  "description": "Small json parser and reader",
  "keywords": [ "json", "parser" ],
  "license": "MIT",
  "src": [
    "parson.c",
    "parson.h"
  ]
}

```

とりあえず邪魔なファイルを削除します。

``` sh

ubuntu:~/parson$ ls
Makefile  README.md  package.json  parson.c  parson.h  tests  tests.c

ubuntu:~/parson$ rm tests
ubuntu:~/parson$ rm README.md

```

Makefileを部分的に削除して再利用するため、以下の通りに書き換えます。

``` Makefile:Makefile

CC = gcc
CFLAGS = -O0 -g -Wall -Wextra -std=c89 -pedantic-errors

all: test 

.PHONY: test 
test: tests.c parson.c
	$(CC) $(CFLAGS) -o $@ tests.c parson.c

clean:
	rm -f test *.o

```

tests.cを以下の通りに書き換えます。(面影がない(^_^;))

``` c:tests.c

#include <stdio.h>
#include "parson.h"

/*------------------------------------------------------------------------------------*/
/* main                                                                               */
/*------------------------------------------------------------------------------------*/
int main(int argc, char **argv)
{
    if(argc != 2){
        printf("Please input json file\r\n");
        return 1;
    } else {
        JSON_Value *root_value;
        root_value = json_parse_file(argv[1]);
        if(root_value == NULL){
            return 1;
        }
        printf("%s\r\n", json_serialize_to_string(root_value));
        json_value_free(root_value);
    }

    return 0;
}

```

#### makeして実行します。

``` sh

ubuntu:~/parson$ make
ubuntu:~/parson$ ./test package.json
{"name":"parson","version":"0.0.0","repo":"kgabis\/parson","description":"Small json parser and reader","keywords":["json","parser"],"license":"MIT","src":["parson.c","parson.h"]}

```

上記のtests.cが最も簡単なparsonのサンプルだと思います。

jsonファイルを開いて、解析かけて、printfで出力しています。
parsonが解析するときにスペースや改行を消すので、凄く狭まった出力が出ます。


## parsonを理解するために作ったプログラム

とりあえずparsonを使ったプログラムが書けましたが、これだとさっぱり分かりません。
私がparson(とjson)がちょっとだけ理解できるようになったプログラムを以下に記載します。

``` c:tests.c

#include <stdio.h>
#include "parson.h"

/*------------------------------------------------------------------------------------*/
/* 固定値を設定                                                                        */
/*------------------------------------------------------------------------------------*/
/* print_json_xxxxで使用 */
#define PRINT_TYPE

/*------------------------------------------------------------------------------------*/
/* 関数                                                                               */
/*------------------------------------------------------------------------------------*/
/* Json print */
void print_json(JSON_Value *value);
void print_json_aux(JSON_Value *value);
void print_json_object(JSON_Value *value);
void print_json_string(JSON_Value *value);
void print_json_null(JSON_Value *value);
void print_json_number(JSON_Value *value);
void print_json_array(JSON_Value *value);

/*------------------------------------------------------------------------------------*/
/* main                                                                               */
/*------------------------------------------------------------------------------------*/
int main(int argc, char **argv)
{
    if(argc != 2){
        printf("Please input json file\r\n");
        return 1;
    } else {
        JSON_Value *root_value;
        root_value = json_parse_file(argv[1]);
        if(root_value == NULL){
            return 1;
        }
        print_json(root_value);
        json_value_free(root_value);
    }
    return 0;
}

/*------------------------------------------------------------------------------------*/
/* #define PRINT_TYPEされていれば                                                      */
/* "JSON String: %s\r\n" の様なフォーマットで表示し、無ければ"%s\r\n"で表示               */
/*                                                                                    */
/* ■使い方                                                                             */
/*  JSON_Value *root_value;                                                           */
/*  root_value = json_parse_file(argv[1]);                                            */
/*   !! NULLチェックを入れてね！                                                        */
/*  print_json(root_value);                                                           */
/*------------------------------------------------------------------------------------*/
void print_json(JSON_Value *value)
{
    print_json_aux(value);
}

void print_json_aux(JSON_Value *value)
{
    switch(json_value_get_type(value)){
        case JSONNull:
            print_json_null(value);
            break;
        case JSONString:
            print_json_string(value);
            break;            
        case JSONNumber:
            print_json_number(value);
            break;
        case JSONObject:
            print_json_object(value);
            break;
        case JSONArray:
            print_json_array(value);
            break;
        case JSONError:
            fprintf(stderr, "Error JSON type \r\n"); 
            break;
        default:
            fprintf(stderr, "unrecognized JSON type %d\r\n", json_type(value));
    }
}

void print_json_null(JSON_Value *value)
{
    (void)value;
    printf("JSON NULL\r\n");
}

void print_json_string(JSON_Value *value)
{
#ifdef PRINT_TYPE
    printf("JSON String: %s\r\n", json_value_get_string(value));
#else
    printf("%s\r\n", json_value_get_string(value));
#endif
}

void print_json_number(JSON_Value *value)
{
#ifdef PRINT_TYPE
    printf("JSON Number: %f\r\n", json_value_get_number(value));
#else
    printf("%f\r\n", json_value_get_number(value));
#endif
}

void print_json_object(JSON_Value *value)
{
    size_t size;
    int i;
    JSON_Object *obj;

    obj = json_value_get_object(value);
    size = json_object_get_count(obj);
    printf("JSON Object of %ld \r\n", size);
    for(i = 0; i < (int)size; i++){
#ifdef PRINT_TYPE
        printf("JSON Object: %s\r\n",json_object_get_name(obj, i));
#else
        printf("%s\r\n",json_object_get_name(obj, i));
#endif
        print_json_aux(json_object_get_value_at(obj,i));
    }
}

void print_json_array(JSON_Value *value)
{
    size_t size;
    int i;
    JSON_Array *ary;

    ary = json_value_get_array(value);
    size = json_array_get_count(ary);
    printf("JSON Array of %ld \r\n", size);
    for(i = 0; i < (int)size; i++){
        print_json_aux(json_array_get_value(ary, i));
    }

}

```

出力

``` sh

ubuntu:~/parson$ ./test package.json 
JSON Object of 7 
JSON Object: name
JSON String: parson
JSON Object: version
JSON String: 0.0.0
JSON Object: repo
JSON String: kgabis/parson
JSON Object: description
JSON String: Small json parser and reader
JSON Object: keywords
JSON Array of 2 
JSON String: json
JSON String: parser
JSON Object: license
JSON String: MIT
JSON Object: src
JSON Array of 2 
JSON String: parson.c
JSON String: parson.h

```

### 凄く長いtests.cができた経緯とparsonの理解

凄く長いプログラム(しかも再帰！)ができました。
これを作るのに1週間ほどかかりました(^_^;)

parsonの使い方が分からずGoogle先生で検索しまくっていたところ、janssonというものに辿りつき、ヒントを見つけられなかったら一生作れなかったでしょう(-_-;)

janssonは以下にあります。
[jansson downloadページ](http://www.digip.org/jansson/)

janssonもjsonパーサーです。しかもC言語。
janssonはサンプルがあり(examples/simple_parse.c)、[関数の説明ページ](https://jansson.readthedocs.io/en/2.10/)まで完備されていてとても分かり安いです。
最初はそちらに置き換えようと頑張っていたのですが、どうしてもAzureIoTSDK標準のパーサーが良いとのことで結局parsonに戻ってきました。

すると、突然parsonが理解できるようになりました。
たぶんですが、janssonのサンプルで色々なjsonファイルを表示させてみたのが良かったのでしょう。
jsonがそれなりに理解できるようになって、parsonで何をすれば良いのかが理解できたのだと思います。

## 結論

というわけで、json初心者の方はjanssonを弄ってみると突然わかるようになるかもしれません。
> jsonが理解できていて、C言語がよく分かる人はいきなり書けるんですかねー？


## ちなみに

上のサンプルは表示しかしていませんが、parsonは値の追加・変更・削除も可能です。
元のjsonが無い状態からjson形式を作る事もできます。

parson.hを見ると色々な関数がありますので、試行錯誤してみましょう。
printが作れればだいたい分かるはずです。

**分かりづらいところだけ補足。**

- set, dotsetは追加・変更に対応しています。
あれば変更、無ければ追加です。

- jsonをいちから作るならjson_value_init_objectを使いましょう。

json_value_free()を忘れずに！