OSS

[나름해석]JSMN-kyu.version1.2

J-Kyu 2018. 4. 2. 23:27

https://github.com/J-Kyu/jsmn
Original creator of jsmn: zserge

(※설명과 해석은 어디까지나 글쓴이의 주관적 사고로 부터 나왔습니다※)



<JSMN>



목적

jsmn은 C에서 JSON의 minmalistic 버전이다. 주어진 block token들을 {,[,",\,:등을 기준으로 data들을 분리하고 전달하는데 그 목적이 있다.



jsmn.h


-jsmntype_t


typedef enum {

        JSMN_UNDEFINED = 0,

        JSMN_OBJECT = 1,

        JSMN_ARRAY = 2,

        JSMN_STRING = 3,

        JSMN_PRIMITIVE = 4

    } jsmntype_t

jsmntype_t는 token의 type을 지정해주는 enum 변수이다


-jsmerr

enum jsmnerr {

        /* Not enough tokens were provided */

        JSMN_ERROR_NOMEM = -1,

        /* Invalid character inside JSON string */

        JSMN_ERROR_INVAL = -2,

        /* The string is not a full JSON packet, more bytes expected */

        JSMN_ERROR_PART = -3

    };

jsmerr는 block token을 parsing 할 때, 각 token에 대한 오류를 나타내고 있다


-jsmntok_t

typedef struct {

        jsmntype_t type;

        int start;

        int end;

        int size;

#ifdef JSMN_PARENT_LINKS

        int parent;

#endif

    } jsmntok_t;

jsmntok_t는 각 token에 대하여 token의 data의 위치와 정보를 지니고 있는 구조체이다


-jsmn_parser

 */

    typedef struct {

        unsigned int pos; /* offset in the JSON string */

        unsigned int toknext; /* next token to allocate */

        int toksuper; /* superior token node, e.g parent object or array */

    } jsmn_parser;

jsmn_parser는 jsmn_pase()함수가 token block을 pasing할 때, 사용 가능한 token들에 대한 정보과 현재 token의 위치를 알려주는 구조체이다


jsmn.c

static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,jsmntok_t *tokens, size_t num_tokens) 

-새로운 token에 들어 갈 때, 초기 값을 주어주면서 token의 기본 정보(jsmntok_t)에 대해서 할당해주는 함수


static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, int start, int end) {

-token에 대하여 type을 정해주고, 시작과  그리고 size의 값을 선언해준다


static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) {

-jsmn_parse()함수에서 *js에 대하여 token이 primitive type인지 아닌지 검사하는 함수이다

-return: token에 대해서 모든 것이 primitive인 경우 return 0를 하며, error 인 경우 해당 error code(음수)를 return 한다


static int jsmn_parse_string(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens)

-jsmn_parse()함수에서 *js에 대하여 token의 element가 string type인지 검사하는 함수 

" \" "와  "\"를 기준으로 string을 확인한다

-return: token에 대하여 string인지를 검사할 때, token들의 정보가 정확하다면, return 0,이며, error인 경우 해당 error code(음수)가 return된다


int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens) 

-*js에 해당하는 token들을 parse하는데 주 목적이 있다. Parsing 할때, token의 개수를 return한다. 그라고 *js의 element의 요소에 따라 token을 만들어 주며, 각 token에 대한 정보를 각 변수의 주소 값을 참조하여 값들을 부여한다

return: array of token의 개수를 return하는데, 그 기준이 바로 jsmn_string과 jsmn_primitive를 따지면서 return하게 될 count의 value를 증가 시킨다 

static int jsmn_init(jsmn_parser *parser)

-jsmn_parser의 type의 변수를 새롭게 선언해주는 것 함수아다


simple.c(주석 처리를 통해서 코드 설명)


/*

 * A small example of jsmn parsing when JSON structure is known and number of

 * tokens is predictable.

 */


static const char *JSON_STRING =

"{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n  "

"\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}";


/*

 JSON_STRING은 여러개의 string을 갖고 있는 포인터 변수이다.

 그리고 사용자는 jsmn을 통해서 JSON_STRING을 분석하고 정렬하는데

 그 목적이 있다

 */


static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {

    if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start &&

        strncmp(json + tok->start, s, tok->end - tok->start) == 0) {

        return 0;

    }

    return -1;

}

/*

 *해당 token의 정보를 지닌 tok가 json(JSON_STRING)에 접근하여서

 *얻은 string과 *s(String 변수)가 동일한지 검사하는 함수

 */





int main() {

    int i;

    int r;

    jsmn_parser p;      //parsing할 때, token을 참조하는 기준의 정보르 갖는 구조체

    jsmntok_t t[128];   //가장 많이 지닐 수 있는 token의 max를 지정해 주었다

    

    jsmn_init(&p);      //paring 할 때 필요한 p의 초기 값을 선언해준다

    r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t, sizeof(t)/sizeof(t[0]));

    /*JSON_STRING을 parising하기 위해서 JSON_STRINGd에 포함 된 array of token의 수를 r  변수에 저장한다

     *이때, t는 token 정보를 지니는 행열의 변수이며, sizeof(t)/sizeof(t[0])는 max 값인 128을 뜻한다

     */

    if (r < 0) {

        printf("Failed to parse JSON: %d\n", r);

        return 1;

        /* r값이 음수로 나오는 경우 JSON_STRING을 parsing 할때,

         *token의 data값 중 error 여겨지는 값이 존할 때, 이 문구가 print되고

         *프로그램이 종료된다

         */

    }

    

    /* Assume the top-level element is an object */

    if (r < 1 || t[0].type != JSMN_OBJECT) {

        printf("Object expected\n");

        return 1;

        /*t[0]는 JSON_STRING의 전체 정보를 갖고있는데

         *그 정보의 type이 object가 아니거나

         *r이 1보다 작은 경우는 error는 아니지만 어떠한 token도 발견되지 않을 때,

         *만들어지는 이 문구가 print되고 프로그램이 종료 된다

         */

    }

    

    printf("\n\n%d",r);     //r은 JSON_STRING의 token이 counting된 int값을 print하는 확인 구문이다

    /* Loop over all keys of the root object */

    

    printf("\n\nt[0]의 element: %.*s\n\n\n", t[0].end-t[0].start,

           JSON_STRING + t[0].start);       //t[0]의 전체 token을 출력한다

    

    for (i = 1; i < r; i++) {

        if (jsoneq(JSON_STRING, &t[i], "user") == 0) {

            /* We may use strndup() to fetch string value */

            printf("- User: %.*s-->", t[i+1].end-t[i+1].start,

                   JSON_STRING + t[i+1].start);

            printf("%d+1번째\n",i); //해당 token의 몇번째 정보를 출력되었는 지 알기 위해서 만든 구문

            i++;

        else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) {

            /* We may additionally check if the value is either "true" or "false" */

            printf("- Admin: %.*s-->", t[i+1].end-t[i+1].start,

                   JSON_STRING + t[i+1].start);

            printf("%d+1번째\n",i); //해당 token의 몇번째 정보를 출력되었는 지 알기 위해서 만든 구문

            i++;

        else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) {

            /* We may want to do strtol() here to get numeric value */

            printf("- UID: %.*s-->", t[i+1].end-t[i+1].start,

                   JSON_STRING + t[i+1].start);

            printf("%d+1번째\n",i); //해당 token의 몇번째 정보를 출력되었는 지 알기 위해서 만든 구문

            i++;

        else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) {

            int j;

            printf("- Groups:\n");

            if (t[i+1].type != JSMN_ARRAY) {       /*JSON_STRING의 해당 token값이 group 이기 때문에 token의 type이 array아닌 경우

                                                    continue를 통해서 아래 string을 무시하고 내려간다*/

                continue/* We expect groups to be an array of strings */

            }

            for (j = 0; j < t[i+1].size; j++) {

                jsmntok_t *g = &t[i+j+2];

                printf("  * %.*s\n", g->end - g->start, JSON_STRING + g->start);

            }

            printf("-->%d+1번째\tsize_of_t[8]_:%d\n",i,t[8].size); //해당 token의 몇번째 정보를 출력되었는 지 알기 위해서 만든 구문

            i += t[i+1].size + 1;

            printf("증가 후,_%d\n",i); //i += t[i+1].size + 1;의 증가 값을 확인하기위해서 출려하는 구문

        else {

            printf("Unexpected key: %.*s\n", t[i].end-t[i].start,

                   JSON_STRING + t[i].start);

        }

    }

    

    EXIT_SUCCESS;

}

~



결과값)

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

변경후)




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



2. 추가 설명

-simple.c는 이미 정해저 있는, JSON_STRING을  jsmn_parsing을 통해서 token by token으로  parsing하여  분류한다. (물론 중간에 error도 검사한다). 각 type별로 parsing한뒤, 출력을 해당 type에 의해서 출력한다


-JSON_STRING을 parsing을 통해서 각각 paring한 값을  출력한다. 마치 python에서 dictionary를 사용하여서 출력하는 값들과 유사하다.


token은 구분은  JSMN_STRING에서 \" \"를 기준으로 하나의 token으로 count하며, :으로 parent와 child 관계를 알려준다. 물론 object도 ([ ])하나의 token이 되며, object안에도 token이기 때문에, token속에 token이 된다.

(JSON_STRING도 하나의 Object이기 때문에 하나의 token이 된다)



Original coded by zserge

Modified and explained by Kyu