嵌入式中JSON数据格式实现—JSMN
admin 于 2017年08月11日 发表在 嵌入式开发笔记
本节主要针对嵌入式设备,讲解两个基于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++; } ...... }