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
いろいろな形式のjsonを扱えます。 - Lightweight (only 2 files)
2ファイルしかないので軽量です。 - Simple API
シンプルです。(本当に???) - Addressing json values with dot notation
ドット(.)を使ってオブジェクトや配列にアクセスできます - C89 compatible
移植性を考慮してC89で記載されています。
azure-iot-sdk-cはC99で記載されているので、こちらの方がよりいろいろな環境で使えるはず。。 - Test suites
テストスイートが揃っています。
私が思っている特徴もついでに記載します
- サンプルがないので使い方が分かりづらい
サンプルがありません。関数の説明がparson.hにコメントであるだけです。 - UTF-8に対応
parson.hとparson.cを眺めているとUTF-8(BOMあり、なし)に対応しているのが分かります。
そのため、日本語もOK(のはず)です。
あと多分、以下の特徴も持っています
- 動的にメモリ確保してjsonを扱っている。
jsonについて
jsonについての詳細は以下のページを参照しましょう。
たぶん分からない(‥;)ので、使ってみましょう↓↓
さっそく使ってみましょう!
早速parsonを使ってみましょう!
parsonの入手
parsonはgitから入手します。
ホームディレクトリで以下を実行します。
git clone https://github.com/kgabis/parson.git
うまくいけばparsonディレクトリができているはずです。
ダメならたぶんネットワーク関連なのでproxy等を見直しましょう。
入手できたら以下の通りになっているはずです。
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の中身は以下の通りです。
{
"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"
]
}
とりあえず邪魔なファイルを削除します。
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を部分的に削除して再利用するため、以下の通りに書き換えます。
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を以下の通りに書き換えます。(面影がない(^_^;))
#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して実行します。
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)がちょっとだけ理解できるようになったプログラムを以下に記載します。
#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));
}
}
出力
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ページ
janssonもjsonパーサーです。しかもC言語。
janssonはサンプルがあり(examples/simple_parse.c)、関数の説明ページまで完備されていてとても分かり安いです。
最初はそちらに置き換えようと頑張っていたのですが、どうしても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()を忘れずに!