2020年12月30日水曜日

C言語でJSON解析をするためにライブラリJanssonを使用する

event_note18:01 editBy ケイ forumNo comments

概要

C言語でJSONの解析を行うライブラリです。
参考サイトはいろいろありますが、今一つわかりにくいため
Linux環境で使うための手順を示します。

 

準備


まず以下のリンクよりソースファイルをダウンロードします。
http://www.digip.org/jansson/releases/jansson-2.13.1.tar.gz
 
このファイルをLinux上の適当なディレクトリに配置します。
以下のコマンドで展開します。

tar zxvf jansson-2.13.1.tar.gz


展開したディレクトリ内にて
以下のコマンドを実行して jansson をビルドします。

./configure --prefix=/usr --disable-static && make

 

root ユーザーになって以下を実行します。

sudo make install

これでライブラリが使用できるようになります

使用するには以下のようにヘッダをリンクし

#include <jansson.h>


ライブラリのリンクはコンパイル時に以下のように指定します。

-ljansson


使用例

それでは以下のJSONファイルを読み込んでみます。

このサンプルでは全部の要素ではなく、読みたい項目のみを抜粋して取得します。

 

ファイル:json.txt

{

    "hostname": "127.0.0.1",

    "sendPort": 80,

    "Description":  "Sequential pattern",

    "DependOnSequentialEvent":  {

        "environmentType":  "PHU",

        "valPercent":   83.700,

        "isScript": true,

        "Location": {

            "state1":   98,

            "state2":   44493,

            "state3":   298

        },

        "pict": "ES-JJ0938",

        "serialNo": 31984,

        "currentEvent": true,

        "alive":    "yes",

        "picturesNumber":   2

    },

    "eventState":   "active",

    "eventType":    "DependOnSequentialEvent",

    "cdID": 1,

    "netPonds": 1,

    "dateTime": "2020-10-24 19:13:49"

}

 

main.c

#include <stdio.h>

#include <unistd.h>

#include <string.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <jansson.h>

 

int parseJson(const char *buffer, size_t buflen)

{

    // JSONオブジェクトを入れる変数

    json_error_t error;

    json_t* root;

    json_t* obj;

    const char *str;

    double dValue;

    int iValue;

 

    // JSON全体バッファを読み込む

    root = json_loadb(buffer, buflen, 0, &error);

    if ( root == NULL )

    {

        printf("[ERR]json load FAILED\n");

        return 1;

    }

 

    printf("------------------------------------\n");

    obj = json_object_get(root, "dateTime");

    if(json_is_string(obj))

    {

        str = json_string_value(obj);

        printf("dateTime: %s\n", str);

    }

 

    obj = json_object_get(root, "eventType");

    if(json_is_string(obj))

    {

        str = json_string_value(obj);

        printf("eventType: %s\n", str);

    }

 

    obj = json_object_get(root, "DependOnSequentialEvent");

    if(json_is_object(obj))

    {

        json_t* obj2;

 

        obj2 = json_object_get(obj, "valPercent");

        if(json_is_real(obj2))

        {

            dValue = json_real_value(obj2);

            printf("valPercent(double): %f\n", dValue);

        }

        else if(json_is_integer(obj2))

        {

            iValue = json_integer_value(obj2);

            printf("valPercent(int): %d\n", iValue);

        }

 

        obj2 = json_object_get(obj, "alive");

        if(json_is_string(obj2))

        {

            str = json_string_value(obj2);

            printf("alive: %s\n", str);

        }

 

        obj2 = json_object_get(obj, "isScript");

        if(json_is_true(obj2))

        {

            printf("isScript: true\n");

        }

        if(json_is_false(obj2))

        {

            printf("isScript: false\n");

        }

    }

    printf("------------------------------------\n");

}

 

int main(){

    char block[65535];

    char *file = "json.txt";

    int fd = open(file, O_RDONLY);

    if (fd < 0) {

        // open 失敗

        fprintf(stderr, "open(%s) errror:%d\n", file, fd);

        return -1;

    }

 

    int size = read(fd, block, sizeof(block));

    if (size < 0) {

        fprintf(stderr, "read() errror:%d\n", fd);

        close(fd);

        return -1;

    }

    close(fd);

 

    parseJson((const char*)block, size);

}

 

makefile

EXEC = jsontest

OBJS = main.o \

 

all : clean $(EXEC)

 

clean :

        -rm -f $(EXEC) *.elf *.gdb *.o *~

 

$(EXEC): $(OBJS)

        gcc  -g -O0 -o $@ $(OBJS) -lm -ljansson

 

%.o: %.c

        gcc -c -g3 -O0 -o $@ $<

 

 

実行結果

$ ./jsontest

------------------------------------

dateTime: 2020-10-24 19:13:49

eventType: DependOnSequentialEvent

valPercent(double): 83.700000

alive: yes

isScript: true

------------------------------------

 

解説

まずJSON全体をオブジェクトに取得します(json_loadb関数)

ここではバッファ指定のjson_loadb関数を使用しています。JSON形式でなければエラーとなります。これで全体のオブジェクトを取得します。

 各要素は名前指定で対象オブジェクトに対してのオブジェクトとして取得します(json_object_get関数)

オブジェクト=json_object_get(対象オブジェクト, 文字列);

  

取得したオブジェクトを判断します(is関数)

例えば数値で取得する場合は

json_is_integer(オブジェクト)

true/falseが得られます。この判断は必須ではありませんが、この判断が間違っていると次の取得でエラーとなります。

 

取得します。

例えば上記の数値の場合であれば

json_integer_value(オブジェクト)

で値が得られます。

 

Bool(true/false)を取得する場合は

json_is_true関数, json_is_false関数で判断するのが良いでしょう。

 

下の階層を取得する場合はjson_object_get関数で取得したオブジェクトが下の階層となります。ソースを見ればわかると思います。

 

また、各階層内で取得する際は、文字列指定なので、上から取得しなければならないわけではありません。ソース上でも敢えて取得順はバラバラにしています。

 

注意点

実数(double)を取得するのはjson_real_value関数を使用しますが、この関数では整数表記は取得できません。

事前に、json_is_integer(整数判断)json_is_real(実数判断)をするのが望ましいと思います。

 

使用例(配列)

次は配列(Array)を取得する例です。
以下のJSONファイルはLocationが配列となっています。

 

ファイル:json.txt

{

    "hostname": "127.0.0.1",

    "sendPort": 80,

    "Description":  "Sequential pattern",

    "DependOnSequentialEvent":  {

        "environmentType":  "PHU",

        "valPercent":   83.700,

        "isScript": true,

        "Location": [

        {

            "state1":   98,

            "state2":   44493,

            "state3":   298

        },

        {

            "state1":   198,

            "state2":   144493,

            "state3":   1298

        },

        {

            "state1":   298,

            "state2":   244493,

            "state3":   2298

        }

        ],

        "pict": "ES-JJ0938",

        "serialNo": 31984,

        "currentEvent": true,

        "alive":    "yes",

        "picturesNumber":   2

    },

    "eventState":   "active",

    "eventType":    "DependOnSequentialEvent",

    "cdID": 1,

    "netPonds": 1,

    "dateTime": "2020-10-24 19:13:49"

}

 

main.c

#include <stdio.h>

#include <unistd.h>

#include <string.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <jansson.h>

 

int parseJson(const char *buffer, size_t buflen)

{

    // JSONオブジェクトを入れる変数

    json_error_t error;

    json_t* root;

    json_t* obj;

    const char *str;

    double dValue;

    int iValue;

 

    // JSON全体バッファを読み込む

    root = json_loadb(buffer, buflen, 0, &error);

    if ( root == NULL )

    {

        printf("[ERR]json load FAILED\n");

        return 1;

    }

 

    printf("------------------------------------\n");

    obj = json_object_get(root, "dateTime");

    if(json_is_string(obj))

    {

        str = json_string_value(obj);

        printf("dateTime: %s\n", str);

    }

 

    obj = json_object_get(root, "eventType");

    if(json_is_string(obj))

    {

        str = json_string_value(obj);

        printf("eventType: %s\n", str);

    }

 

    obj = json_object_get(root, "DependOnSequentialEvent");

    if(json_is_object(obj))

    {

        json_t* obj2;

 

        obj2 = json_object_get(obj, "valPercent");

        if(json_is_real(obj2))

        {

            dValue = json_real_value(obj2);

            printf("valPercent(double): %f\n", dValue);

        }

        else if(json_is_integer(obj2))

        {

            iValue = json_integer_value(obj2);

            printf("valPercent(int): %d\n", iValue);

        }

 

        obj2 = json_object_get(obj, "alive");

        if(json_is_string(obj2))

        {

            str = json_string_value(obj2);

            printf("alive: %s\n", str);

        }

 

        obj2 = json_object_get(obj, "isScript");

        if(json_is_true(obj2))

        {

            printf("isScript: true\n");

        }

        if(json_is_false(obj2))

        {

            printf("isScript: false\n");

        }

 

        obj2 = json_object_get(obj, "Location");

        if(json_is_array(obj2))

        {

            int i;

            int arraysize = json_array_size(obj2);

            printf("Location arraysize: %d\n", arraysize);

 

            for(i=0; i<arraysize; i++){

                json_t* obj3 = json_array_get(obj2, i);

                if(json_is_object(obj3)){

                    json_t* obj4;

                    obj4 = json_object_get(obj3, "state1");

                    if(json_is_integer(obj4)){

                        iValue = json_integer_value(obj4);

                        printf("[%d] state1: %d\n", i, iValue);

                    }

                    obj4 = json_object_get(obj3, "state2");

                    if(json_is_integer(obj4)){

                        iValue = json_integer_value(obj4);

                        printf("[%d] state2: %d\n", i, iValue);

                    }

                    obj4 = json_object_get(obj3, "state3");

                    if(json_is_integer(obj4)){

                        iValue = json_integer_value(obj4);

                        printf("[%d] state3: %d\n", i, iValue);

                    }

                }

            }

        }

    }

    printf("------------------------------------\n");

}

 

int main(){

    char block[65535];

    char *file = "json.txt";

    int fd = open(file, O_RDONLY);

    if (fd < 0) {

        // open 失敗

        fprintf(stderr, "open(%s) errror:%d\n", file, fd);

        return -1;

    }

 

    int size = read(fd, block, sizeof(block));

    if (size < 0) {

        fprintf(stderr, "read() errror:%d\n", fd);

        close(fd);

        return -1;

    }

    close(fd);

 

    parseJson((const char*)block, size);

}

 

makefile

EXEC = jsontest

OBJS = main.o \

 

all : clean $(EXEC)

 

clean :

        -rm -f $(EXEC) *.elf *.gdb *.o *~

 

$(EXEC): $(OBJS)

        gcc  -g -O0 -o $@ $(OBJS) -lm -ljansson

 

%.o: %.c

        gcc -c -g3 -O0 -o $@ $<

 


実行結果

$ ./jsontest

------------------------------------

dateTime: 2020-10-24 19:13:49

eventType: DependOnSequentialEvent

valPercent(double): 83.700000

alive: yes

isScript: true

Location arraysize: 3

[0] state1: 98

[0] state2: 44493

[0] state3: 298

[1] state1: 198

[1] state2: 144493

[1] state3: 1298

[2] state1: 298

[2] state2: 244493

[2] state3: 2298

------------------------------------

  

解説

ソースでLocationを取得している箇所が該当しますが、以下のように処理しています。
まず、取得したオブジェクトがArrayであるかを判断します。(json_is_array関数
次に要素数を取得します(json_array_size関数
要素数nがわかればインデックス(0n-1)がわかるので要素数分forでループします。
json_array_get(対象オブジェクト, インデックス)
で各要素が取得できます。あとの処理は同様です。