jsmn study notes-JSON parser with minimal resource occupation and fastest parsing speed

1. Introduction

1.1 jsmn

Based on the well-known JSON format implementation method of C language, namely: jsmn and cJSON .
Of these two protocols, jsmn is especially suitable for environments with extremely limited storage space in single-chip microcomputers. A JSON parser that occupies a very small resource is claimed to be the fastest in the world; cJSON is suitable for environments with sufficient space and large data processing.

jsmn mainly has the following characteristics:

  • No library dependencies;
  • The syntax is compatible with C89, and the code is highly portable;
  • No dynamic memory allocation
  • Minimal code footprint
  • There are only two APIs, extremely concise

1.2 JSON

JSON (JavaScript Object Notation, JS Object Notation) is a lightweight data exchange format. It is based on a subset of ECMAScript (the js specification developed by the European Computer Association), and uses a text format completely independent of the programming language to store and represent data.

1.2.1 JSON syntax rules

In the JS language, everything is an object. Therefore, any supported type can be represented by JSON, such as string, number, object, array, etc. But objects and arrays are two special and commonly used types:

● Objects are represented as key-value pairs
● Data is separated by commas
● Curly brackets hold objects
● Square brackets hold arrays

1.2.2 JSON key/value pairs

JSON key-value pairs are a way to save JS objects. The key name in the key/value pair combination is written in front and wrapped in double quotes "", separated by a colon:, and then followed by the value:

{"firstName": "Json"}

Two, transplant

Project address: https://github.com/zserge/jsmn

Add jsmn.h to the project

2.1 Including jsmn header file

When using, include the header file, because the function definition of jsmn is also in the header file, so when you add it for the first time, you can add it directly:

/* USER CODE BEGIN Includes */
#include "jsmn.h"
#include <stdio.h>	//用于printf打印
#include <string.h> //用于字符串处理

/* USER CODE END Includes */

After it has been used, when you continue to use it in other files, you need to add it like this, and the order is not interchangeable :

/* USER CODE BEGIN 0 */
#define JSMN_HEADER
#include "jsmn.h"	

/* USER CODE END 0 */

Otherwise it will cause function redefinition:

、 、 API

3.1 jsmn_init

Initialize the parser

3.2 jsmn_parse

Parse the data and get the token

3.3 token structure

jsmn abstracts each json data segment into a token:

  • Type of data item
  • The starting position of the data item data segment in the original json data
  • The end position of the data item data segment in the original json data
typedef struct {
    
    
	jsmntype_t type; // Token type
	int start;       // Token start position
	int end;         // Token end position
	int size;        // Number of child (nested) tokens
} jsmntok_t;

json raw data:
{"name":"mculover666", "admin":false, "uid":1000}
print out each token after parsing:

printf("[type][start][end][size]\n");
for(i = 0;i < r; i++)
{
    
    
	printf("[%4d][%5d][%3d][%4d]\n", t[i].type, t[i].start, t[i].end, t[i].size);
}

The results are as follows:


There are 7 tokens parsed from this json data:
① Token of Object type: {\"name\":\"mculover666\",\"admin\":false,\"uid\":1000}
② Token of String type: "name"、"mculover666"、"admin"、"uid"
③ Token of Primitive type:数字1000,布尔值false

3.4 token type

typedef enum {
    
    
	JSMN_UNDEFINED = 0,
	JSMN_OBJECT = 1,
	JSMN_ARRAY = 2,
	JSMN_STRING = 3,
	JSMN_PRIMITIVE = 4
} jsmntype_t;

Four, example 1

json raw data:
{"user":"johndoe", "admin":false, "uid":1000, "groups": ["users", "wheel", "audio", "video"]}

#include "jsmn.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

// 2.编写在原始json数据中的字符串比较函数
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;
}

int main() {
    
    
  int i;
  int r;
  // 3.创建JSON解析器p,存储JSON中数据位置信息
  jsmn_parser p;
  // 4.假定最多有128个符号,创建其描述类型
  jsmntok_t t[128]; /* We expect no more than 128 tokens */

  // 5.在一组符号上创建JSON解释器,初始化
  jsmn_init(&p);
  // 6.运行JSON解释器,将一个JSON数据字符串转换为一个符号数组,每个数组描述一个JSON对象
  r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t,
                 sizeof(t) / sizeof(t[0]));
  if (r < 0) {
    
    
    printf("Failed to parse JSON: %d\n", r);
    return 1;
  }

  /* Assume the top-level element is an object */
  if (r < 1 || t[0].type != JSMN_OBJECT) {
    
    
    printf("Object expected\n");
    return 1;
  }

  // 7.获取JSON中的对象数据
  /* Loop over all keys of the root object */
  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++;
    } else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) {
    
    
      /* We may want to do strtol() here to get numeric value */
      printf("- UID: %.*s\n", t[i + 1].end - t[i + 1].start,
             JSON_STRING + t[i + 1].start);
      i++;
    } else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) {
    
    
      int j;
      printf("- Groups:\n");
      if (t[i + 1].type != JSMN_ARRAY) {
    
    
        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);
      }
      i += t[i + 1].size + 1;
    } else {
    
    
      printf("Unexpected key: %.*s\n", t[i].end - t[i].start,
             JSON_STRING + t[i].start);
    }
  }
  return EXIT_SUCCESS;
}

Five, example 2

json raw data:
{"proto":"static", "ipaddr":"192.168.2.67", "netmask":"255.255.255.0", "gateway":"192.168.2.1", "dns":"192.168.2.1"}
{"proto":"dhcp"}

jsmn_parser parser;
jsmntok_t jsonTokens[128];
int numTokens;

jsmn_init(&parser);

numTokens = jsmn_parse(&parser, recvMsgBuff, strlen(recvMsgBuff), jsonTokens, 128);
if(numTokens < 0)
{
    
    
    printf("Failed to parse JSON (%d)!\n", numTokens);
}
else
{
    
    
    int i;
    for(i = 1; i < numTokens; i++) 
    {
    
    
        if(jsoneq(recvMsgBuff, &jsonTokens[i], "proto") == 0)       
        {
    
     
            i++;
            if(jsoneq(recvMsgBuff, &jsonTokens[i], "static") == 0)          // 静态IP        
            {
    
    
                printf("proto: static\n");
                i++;
                if(jsoneq(recvMsgBuff, &jsonTokens[i], "ipaddr") == 0)
                {
    
    
                    char ipaddr[20] = {
    
    0};
                    memcpy(ipaddr, recvMsgBuff + jsonTokens[i + 1].start, jsonTokens[i + 1].end - jsonTokens[i + 1].start);
                    printf("ipaddr: %s\n", ipaddr);
                    i += 2;
                }
                if(jsoneq(recvMsgBuff, &jsonTokens[i], "netmask") == 0)
                {
    
    
                    char netmask[20] = {
    
    0};
                    memcpy(netmask, recvMsgBuff + jsonTokens[i + 1].start, jsonTokens[i + 1].end - jsonTokens[i + 1].start);
                    printf("netmask: %s\n", netmask);
                    i += 2;
                }
                if(jsoneq(recvMsgBuff, &jsonTokens[i], "gateway") == 0)
                {
    
    
                    char gateway[20] = {
    
    0};
                    memcpy(gateway, recvMsgBuff + jsonTokens[i + 1].start, jsonTokens[i + 1].end - jsonTokens[i + 1].start);
                    printf("gateway: %s\n", gateway);
                    i += 2;
                }
                if(jsoneq(recvMsgBuff, &jsonTokens[i], "dns") == 0)
                {
    
    
                    char dns[20] = {
    
    0};
                    memcpy(dns, recvMsgBuff + jsonTokens[i + 1].start, jsonTokens[i + 1].end - jsonTokens[i + 1].start);
                    printf("dns: %s\n", dns);
                }
            }
            else if(jsoneq(recvMsgBuff, &jsonTokens[i], "dhcp") == 0)       // 动态IP       
            {
    
    
                printf("proto: dhcp\n");
            }
        }
        else
        {
    
    
            printf("Unexpected key: %.*s\n", jsonTokens[i].end - jsonTokens[i].start, recvMsgBuff + jsonTokens[i].start);
        }
    }
}

Written by Leung on September 17, 2020

• Reference: jsmn | A JSON data format implementation in the embedded JSON parser with minimal resource usage and the fastest parsing speed—
    JSMN

Guess you like

Origin blog.csdn.net/qq_36347513/article/details/108640754