本节主要针对嵌入式设备,讲解两个基于C语言比较有名的JSON格式实现的方法,即:jsmn 和 cJSON 。

这两个协议,jsmn特别适用于单片机中存储空间极其有限的环境;cJSON适合空间比较充足,需要大型数据处理的环境。

首先讲解jsmn格式协议。第一次知道jsmn是在TI CC3200芯片例程中看到的,也就是说,CC3200芯片实现JSON格式解析,是采用了JSMN实现方法。

1. 多平台JSON

在JSON官网(http://www.json.org/json-zh.html)可以看到基于众多编程语言的JSON格式实现方法,如下:            

2. 源码下载 

下载JSMN源码(https://github.com/zserge/jsmn ),其实有用的也就两个文件:jsmn.h 和 jsmn. c 。众所周知,一般工程都是这样去分割的,方便添加和删改。

3. 解析原理

JSMN文件代码都是标准的C语言,解析起来并不复杂。其核心思想很简单,就是利用" , "先进行分割,将拆分后的不同片段通过start、end、size参数进行存储,接着根据“字段”获取对应的值。

7. 工程示例(点击下载附件

(1)jsmn.h 中 struct 类型  jsmntok_t ,内容如下: 

/**
 * JSON token description.
 * @paramtypetype (object, array, string etc.)
 * @paramstartstart position in JSON data string
 * @paramendend position in JSON data string
 */
 typedef struct
 {
    jsmntype_t type;
    int start;
    int end;
    int size;
  #ifdef JSMN_PARENT_LINKS
    int parent;
  #endif
   }jsmntok_t;

(2)jsmn.c 中解析函数  jsmn_parse,部分代码如下: 

/**
 * Parse JSON string and fill tokens.
 */
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
               jsmntok_t *tokens, unsigned int num_tokens)
{
    jsmnerr_t r;
    int i;
    jsmntok_t *token;
    int count = 0;

    for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
    {
        char c;
        jsmntype_t type;

        c = js[parser->pos];   //获取首个字符
        switch (c)
        {
        case '{':
        case '[':
            count++;
            if (tokens == NULL)  //判断分配的空间不足
            {
                break;
            }
            token = jsmn_alloc_token(parser, tokens, num_tokens);
            if (token == NULL)
                return JSMN_ERROR_NOMEM;
            if (parser->toksuper != -1)
            {
                tokens[parser->toksuper].size++;
#ifdef JSMN_PARENT_LINKS
                token->parent = parser->toksuper;
#endif
            }
            token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
            token->start = parser->pos;
            parser->toksuper = parser->toknext - 1;
            break;
        case '}':
        case ']':
            if (tokens == NULL)
                break;
            type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
        ......
        case ',':
            if (tokens != NULL &&
                    tokens[parser->toksuper].type != JSMN_ARRAY &&
                    tokens[parser->toksuper].type != JSMN_OBJECT)
            {
#ifdef JSMN_PARENT_LINKS
                parser->toksuper = tokens[parser->toksuper].parent;
#else
                for (i = parser->toknext - 1; i >= 0; i--)
                {
                    if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT)
                    {
                        if (tokens[i].start != -1 && tokens[i].end == -1)
                        {
                            parser->toksuper = i;
                            break;
                        }
                    }
                }
#endif
            }
            break;
#ifdef JSMN_STRICT
        /* In strict mode primitives are: numbers and booleans */
        ......
        case 'n' :
            /* And they must not be keys of the object */
            if (tokens != NULL)
            {
                jsmntok_t *t = &tokens[parser->toksuper];
                if (t->type == JSMN_OBJECT ||
                        (t->type == JSMN_STRING && t->size != 0))
                {
                    return JSMN_ERROR_INVAL;
                }
            }
#else
        /* In non-strict mode every unquoted value is a primitive */
        default:
#endif
            r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
            if (r < 0) return r;
            count++;
            if (parser->toksuper != -1 && tokens != NULL)
                tokens[parser->toksuper].size++;
            break;

#ifdef JSMN_STRICT
        /* Unexpected char in strict mode */
        default:
            return JSMN_ERROR_INVAL;
#endif
        }
    }

    for (i = parser->toknext - 1; i >= 0; i--)
    {
        /* Unmatched opened object or array */
        if (tokens[i].start != -1 && tokens[i].end == -1)
        {
            return JSMN_ERROR_PART;
        }
    }

    return count;
}

(3)实现JSON格式数据解析,主代码如下:

//1.原始json格式的string序列
static const char *JSON_STRING = 
    "{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n  " 
    "\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}";

//2.创建JSON解析器p,存储JSON中数据位置信息
jsmn_parser p;

//3.假定最多有128个符号,创建其描述类型
jsmntok_t t[128]; 

//4.在一组符号上创建JSON解释器,初始化
jsmn_init(&p);

//5.运行JSON解释器,将一个JSON数据字符串转换为一个符号数组,每个数组描述一个JSON对象
r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t, sizeof(t)/sizeof(t[0]));
    
//6.获取JSON中的对象数据
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\n", t[i+1].end-t[i+1].start, JSON_STRING + t[i+1].start);
        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\n", t[i+1].end-t[i+1].start, JSON_STRING + t[i+1].start);
        i++;
    }
    ......
}

注意:本站所有文章除特别说明外,均为原创,转载请务必以超链接方式并注明作者出处

标签:JSON,JSMN