json格式解析和libjson的用法介绍(关于cjson的使用方法)

时间:2021-08-30 08:25:53

在阅读本文之前,请先阅读下《Rss Reader实例开发之系统设计》一文。

Rss Reader实例开发中,进行网络数据交换时主要使用到了两种数据格式:JSON与XML。本文主要介绍JSON格式的简单概念及JSON在Rss Reader中的应用,XML格式的使用将在下一篇文章做介绍。

JSON简介:

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,可以把JSON的结构理解成无序的、可嵌套的key-value键值对集合,这些key-value键值对是以结构体或数组的形式来组织的。同一级的key-value键值对之间是用一个“,”(逗号)隔开,每个key-value键值对是由一个key后面紧接一个“:”(冒号),冒号后面是这个key对应的value。Key是一个word,由大小写字母、下划线及数字组成,可以由双引号封闭,也可以不加双引号;而value的取值集为:Number、Boolean(true或false)、null、String、Object及Array,如图一:

json格式解析和libjson的用法介绍(关于cjson的使用方法)

(图一)

1、Number:数值,包括整形数与浮点数,如:123、0.83、-2.7e10。其结构如图二:

json格式解析和libjson的用法介绍(关于cjson的使用方法)

(图二)

2、String:字符串,是以双引号封闭起来的一串字符,使用反斜杠来转义,如:\\、\n等,JSON中字符串的概念与C/C++或者JAVA语言里的字符串概念差不多,如:”abc”。其结构如图三:

json格式解析和libjson的用法介绍(关于cjson的使用方法)

(图三)

3、Object:对象,也可理解成一个结构体,是以一对大括号封闭起来的无序的key-value键值对集合,例如:{name:"Susan", age:27, birthday:{year:1984, month:2, day:11}};也可以写成:{"name":"Susan", "age":27, "birthday":{"year":1984, "month":2, "day":11}};其结构如图四:

json格式解析和libjson的用法介绍(关于cjson的使用方法)

(图四)

4、Array:数组,JSON的数组是一个以中括号封闭起来的value的集合,即数组内的各个成员的数据类型可以不一样,这一点就跟C/JAVA的数组概念不同了。每个value之间是由一个“,”(逗号)隔开,例如:[123, abc, false, {name:mj}];其结构如图五:

json格式解析和libjson的用法介绍(关于cjson的使用方法)

 

(图五)


关于JSON的详细说明与教程请自行到网络上搜索,有很多。

下面我们就来动手写一个例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
{
 
  result:true,
 
 
 
  root:{
 
    version:"201007091640",
 
    channels:[
 
    {
 
      name:"新闻中心",
 
      subchnls:[
 
      {
 
        title:"焦点新闻",
 
        link:"http://www.mobanpai.com/news/channel/1/news.rss",
 
        desc:"家事、国事、天下事"
 
      },
 
      {
 
        title:"新闻频道",
 
        link:"http://www.mobanpai.com/news/channel/2/news.rss",
 
        desc:"让您实时掌握国际动态"
 
      },
 
      {
 
        title:"军事频道",
 
        link:"http://www.mobanpai.com/news/channel/3/news.rss",
 
        desc:"军事"
 
      }
 
      ]
 
    },
 
    
 
    {
 
      name:"体育新闻",
 
      subchnls:[
 
      {
 
        title:"体育要闻汇总",
 
        link:"http://www.mobanpai.com/news/channel/4/news.rss",
 
        desc:"erewr"
 
      },
 
      {
 
        title:"国际足坛",
 
        link:"http://www.mobanpai.com/news/channel/5/news.rss",
 
        desc:"werewr"
 
      }
 
      ]
 
    }
 
    
 
    ]
 
  }
 
}

这段JSON描述了一个对象(最外层大括号包围的部分),为了方便区分,我们就将其称为对象A吧。对象A有两个Item(即key-value键值对),一个是result,其值为true;一个是root,其值为一个对象,称为对象B。对象B也有两个Item,一个是version,其值为一个字串” 201007091640”;一个是channels,其值是一个数组,而数组的成员都是一个对象,每个对象又包含两个Item,一个是name,值分别为字串"新闻中心"和"体育新闻";一个是subchnls,值都是数组,每个数组又分别有若干个成员,每个subchnls成员也都是一个对象,每个对象都有三个Item:title、link和desc。也许你看到这,已经是一头大汗了,不过没关系,我们来帖张这段JSON文本对应的结构图,有图就有真相,请看图六:

json格式解析和libjson的用法介绍(关于cjson的使用方法)

(图六:黑色实线为对象,虚线为值,橙色实线为数组)

在RssReader中使用cJSON:


在RssReader中使用了开源库cJSON来解析JSON,所以在此就介绍下cJSON的使用:

在CJSON中,一个key-value键值对被解析并存放在一个cJSON结构体变量中,其value取值集为:FALSE,TRUE,NULL,NUMBER,STRING,OBJECT,ARRAY。它们分别被存放在CJSON对象的child、valuestring、valueint、valuedouble变量中,而用于判断某个CJSON对象value的数据类型则是CJSON对象的type变量,其取值范围与CJSON对象的value集是一一对应的,如:cJSON_False对应FALSE。

cJSON Types:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
#define   cJSON_False   0
 
#define   cJSON_True   1
 
#define   cJSON_NULL 2
 
#define   cJSON_Number  3
 
#define   cJSON_String  4
 
#define   cJSON_Array   5
 
#define   cJSON_Object  6

cJSON 结构体:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct cJSON
 
{
 
  struct cJSON *next,*prev;  //指向上一项/下一项
 
  struct cJSON *child;  //指向下一级,也就是当type为cJSON_Object或cJSON_Array时,此指针不为空。
 
  int type;         
 
  char *valuestring; // 当type为cJSON_String时
 
  int valueint;    // 当 type为cJSON_Number时
 
  double valuedouble; //当type为cJSON_Number时
 
 
 
  char *string;    // 当前项的名称,也就是key-value键值对的key
 
} cJSON;

在解析JSON过程中,从JSON格式描述的value数据到CJSON对象中存放的变量的一个映射关系如图七:

(图七)

json格式解析和libjson的用法介绍(关于cjson的使用方法)

对CJSON格式的解析是使用cJSON_Parse()方法,其传入的参数是一个CJSON的Object/Array结构的字串,解析成功则返回一个cJSON结构体变量的指针,在使用完成后需要调用cJSON_Delete()将该指针销毁。CJSON是以树状结构来组织内部的各个cJSON结构体变量的,一般地,要使用某个cJSON结构体变量,需要调用cJSON_GetObjectItem()方法并根据其父节点的cJSON结构体变量指针与该项的名称来获取其指针,举个例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bool bResult;
 
char jsonString[] = “{result:true}”;
 
//获取result的值true
 
cJSON* pItem = NULL;
 
cJSON* pRoot = cJSON_Parse ( jsonString );
 
if ( pRoot )
 
{
 
  pItem = cJSON_GetObjectItem ( pRoot, “result” );
 
  if ( pItem )
 
  {
 
    bResult = pItem->valueint;  //由于result的值type为cJSON_False或cJSON_True,所以其值被存放在valueint变量中
 
  }
 
  cJSON_Delete ( pRoot );
 
}

在上例中,不管result的值type为何类型,都是通过调用cJSON_GetObjectItem()方法获取其对应的cJSON结构体变量的指针,只是在处理其对应的值时会有所不同。如果result的值type为cJSON_Object,则需要通过调用cJSON_GetObjectItem( pItem, “subItemKey”)来获取其子Item的指针。在处理值type为cJSON_Array的Item时,就需要再用到另外两个API:cJSON_GetArraySize ()和cJSON_GetArrayItem()。我们举个获取一个数组成员值的例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
char* out;
 
char jsonString[] = “{colors:[\“red\”, \“green\”,\ “blue\”, \“yellow\”, \“white\”]}”;
 
cJSON* pArray = NULL;
 
cJSON* pRoot = cJSON_Parse ( jsonString );
 
if ( pRoot )
 
{
 
  pArray = cJSON_GetObjectItem ( pRoot, “colors” );
 
  if ( pArray )
 
  {
 
    cJSON* pArrayItem = NULL;
 
    int nCount = cJSON_GetArraySize ( pArray ); //获取pArray数组的大小
 
    for( int i = 0; i < nCount; i++)
 
    {
 
      pArrayItem = cJSON_GetArrayItem(pArray, i);
 
      out = cJSON_Print( pArrayItem );  //将pArrayItem的值以字串的形式打印到char型buffer上,cJSON_Print()会自动分配内存空间,用完需要释放内存。
 
      SS_printf( “array item %d: %s\n”, i, out);
 
      Free( out );
 
    }
 
  }
 
  cJSON_Delete ( pRoot );
 
}

在提取一个复杂的JSON格式的数据时,也只是将以上两个例子使用到的方法进行组合调用罢了。所以对CJSON提供的API的使用是很简单有效的。有了以上知识的了解,我们就可以编写一些代码将例一中的JSON解析并提取其中的数据,还是贴点代码才是硬道理,代码如下:

TChannelsData.h:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
/** 子频道信息结构体
 
*
 
*/
 
struct SUBCHNL_DATA
 
{
 
  SUBCHNL_DATA();
 
  void clear();
 
 
 
  TUChar * m_title;
 
  char * m_link;
 
  TUChar * m_desc;
 
};
 
 
 
/** 大频道信息结构体
 
*
 
*/
 
struct CHANNEL_DATA
 
{
 
  CHANNEL_DATA();
 
 
 
  TUChar* m_pszTitle;
 
  vector m_aSubChnlList;
 
};
 
 
 
//………….
 
// TChannelsData 类成员变量:RSSReaderConfig 版本号
 
char m_pszVersion[32];
 
// TChannelsData 类成员变量:频道信息列表
 
vector m_aChnlsList;
 
//………….
 
TChannelsData.cpp:
 
 
/** 解析JSON格式的内容
 
*
 
* \param pszJsonText 解析的JSON格式内容字串
 
*
 
* \return true:有更新数据; false:没有更新数据
 
*/
 
Boolean TChannelsData::ParseJson(char* pszJsonText)
 
{
 
  //char* out;
 
  cJSON* objJson;
 
 
 
  objJson= cJSON_Parse(pszJsonText);
 
 
 
  if (objJson)
 
  {
 
    //out=cJSON_Print(objJson);
 
    cJSON* objRootItem = NULL;
 
 
 
    //判断是否需要更新
 
    objRootItem = cJSON_GetObjectItem(objJson, "result");
 
    if (objRootItem)
 
    {
 
      if (!objRootItem->valueint)
 
      {
 
        return FALSE;
 
      }
 
    }
 
    else
 
    {
 
      return FALSE;
 
    }
 
 
 
    //获取更新数据,根节点root
 
    objRootItem = cJSON_GetObjectItem(objJson, "root");
 
    if (objRootItem)
 
    {
 
      cJSON* objJsonItem = NULL;
 
 
 
      //获取版本号
 
      objJsonItem = cJSON_GetObjectItem(objRootItem, "version");
 
      if (objJsonItem)
 
      {
 
        Int32 nLen = strlen(objJsonItem->valuestring);
 
        strncpy(m_pszVersion, objJsonItem->valuestring, (nLen < 32)? nLen : 31);
 
      }
 
 
 
      //解析出大频道
 
      _ParseChannels(objRootItem);
 
    }
 
    
 
    //SS_printf("=======[parse json]%s\n",out);
 
    cJSON_Delete(objJson);
 
    //free(out);
 
  }
 
 
 
  return TRUE;
 
}
 
 
 
/** 解析出大频道
 
*
 
* \param pCJson cJSON对象指针
 
*
 
* \return void
 
*/
 
void TChannelsData::_ParseChannels(cJSON* pCJson)
 
{
 
  cJSON* pJsonArray = NULL;
 
 
 
  if (!pCJson)
 
  {
 
    return;
 
  }
 
 
 
  pJsonArray = cJSON_GetObjectItem(pCJson, "channels");
 
  if(pJsonArray)
 
  {
 
    cJSON* pArrayItem = NULL;
 
    cJSON* pJsonTemp = NULL;
 
 
 
    Int32 nSize = cJSON_GetArraySize(pJsonArray);
 
    for (Int32 i = 0; i < nSize; i++)
 
    {
 
      pArrayItem = cJSON_GetArrayItem(pJsonArray, i);
 
      if (pArrayItem)
 
      {
 
        CHANNEL_DATA tChannelData;
 
        Int32 nLen = 0;
 
 
 
        //获取大频道名称
 
        tChannelData.m_pszTitle = _JsonGetTUString(pArrayItem, "name");
 
        
 
        //解析出子频道
 
        _ParseSubChnls(tChannelData.m_aSubChnlList, pArrayItem);
 
 
 
        //将大频道信息对象压入列表中
 
        m_aChnlsList.push_back(tChannelData);
 
      }
 
      else
 
      {
 
        continue;
 
      }
 
    }
 
  }
 
}
 
 
 
/** 解析子频道
 
*
 
* \param aSubChnlList 存放子频道数据的vector对象
 
* \param pCJson cJSON对象指针
 
*
 
* \return void
 
*/
 
void TChannelsData::_ParseSubChnls(vector& aSubChnlList, cJSON* pCJson)
 
{
 
  cJSON* pJsonArray = NULL;
 
 
 
  if (!pCJson)
 
  {
 
    return;
 
  }
 
 
 
  pJsonArray = cJSON_GetObjectItem(pCJson, "subchnls");
 
  if (pJsonArray)
 
  {
 
    cJSON* pArrayItem = NULL;
 
    //cJSON* pJsonTemp = NULL;
 
 
 
    Int32 nSize = cJSON_GetArraySize(pJsonArray);
 
    for (Int32 i = 0; i < nSize; i++)
 
    {
 
      pArrayItem = cJSON_GetArrayItem(pJsonArray, i);
 
      if (pArrayItem)
 
      {
 
        SUBCHNL_DATA tSubChnlData;
 
        Int32 nLen = 0;
 
 
 
        //get title
 
        tSubChnlData.m_title = _JsonGetTUString(pArrayItem, "title");
 
 
 
        //get link
 
        tSubChnlData.m_link = _JsonGetString(pArrayItem, "link");
 
 
 
        //get desc
 
        tSubChnlData.m_desc = _JsonGetTUString(pArrayItem, "desc");
 
 
 
        aSubChnlList.push_back(tSubChnlData);
 
      }
 
    }
 
  }
 
}
 
 
 
/** 获取指定的cJSON对象的指定属性值
 
*
 
* \param pJsonItem cJSON对象指针
 
* \param pszKey cJSON对象属性
 
*
 
* \return 返回JSON对象的值,以TUChar字串形式返回
 
*/
 
TUChar* TChannelsData::_JsonGetTUString(cJSON* pJsonItem, char* pszKey)
 
{
 
  TUChar* pszValue = NULL;
 
  Int32 nLen;
 
  cJSON* pJsonTemp = NULL;
 
 
 
  pJsonTemp = cJSON_GetObjectItem(pJsonItem, pszKey);
 
  if (pJsonTemp)
 
  {
 
    nLen = strlen(pJsonTemp->valuestring) + 1;
 
    pszValue = new TUChar[nLen];
 
    if(pszValue)
 
    {
 
      MemSet(pszValue, 0, nLen * sizeof(TUChar));
 
      TUString::StrUtf8ToStrUnicode(pszValue, (const Char*)pJsonTemp->valuestring);
 
    }
 
  }
 
 
 
  return pszValue;
 
}
 
 
 
/** 获取指定的cJSON对象的指定属性值
 
*
 
* \param pJsonItem cJSON对象指针
 
* \param pszKey cJSON对象属性
 
*
 
* \return 返回JSON对象的值,以char字串形式返回
 
*/
 
char* TChannelsData::_JsonGetString(cJSON* pJsonItem, char* pszKey)
 
{
 
  char* pszValue = NULL;
 
  Int32 nLen;
 
  cJSON* pJsonTemp = NULL;
 
 
 
  pJsonTemp = cJSON_GetObjectItem(pJsonItem, pszKey);
 
  if (pJsonTemp)
 
  {
 
    nLen = strlen(pJsonTemp->valuestring) + 1;
 
    pszValue = new char[nLen];
 
    if(pszValue)
 
    {
 
      MemSet(pszValue, 0, nLen);
 
      strncpy(pszValue, pJsonTemp->valuestring, nLen - 1);
 
    }
 
  }
 
 
 
  return pszValue;
 
}
 
 
 
/** 获取指定的cJSON对象的指定属性值
 
*
 
* \param pJsonItem cJSON对象指针
 
* \param pszKey cJSON对象属性
 
*
 
* \return 返回JSON对象的值,以int32形式返回
 
*/
 
Int32 TChannelsData::_JsonGetInt(cJSON* pJsonItem, char* pszKey)
 
{
 
  Int32 nValue = 0;
 
  cJSON* pJsonTemp = NULL;
 
 
 
  pJsonTemp = cJSON_GetObjectItem(pJsonItem, pszKey);
 
  if (pJsonTemp)
 
  {
 
    nValue = pJsonTemp->valueint;
 
  }
 
 
 
  return nValue;
 
}
 
 
 
/** 获取指定的cJSON对象的指定属性值
 
*
 
* \param pJsonItem cJSON对象指针
 
* \param pszKey cJSON对象属性
 
*
 
* \return 返回JSON对象的值,以Boolean形式返回
 
*/
 
Boolean TChannelsData::_JsonGetBoolean(cJSON* pJsonItem, char* pszKey)
 
{
 
  Boolean bValue = FALSE;
 
  cJSON* pJsonTemp = NULL;
 
 
 
  pJsonTemp = cJSON_GetObjectItem(pJsonItem, pszKey);
 
  if (pJsonTemp)
 
  {
 
    if(pJsonTemp->valueint)
 
    {
 
      bValue = TRUE;
 
    }
 
  }
 
 
 
  return bValue;
 
}

总结:

JSON的结构简约,所以使得JSON的文档的数据量比较小,比较适合用于网络数据的交换,而且对JSON文档的解析和数据提取的方法也很简单,方便程序员的使用,当然也正是因为JSON的结构简约,使得JSON的可读性与可编辑性会稍差于XML,所以JSON比较适合在较少有人工阅读和编辑的情况下使用期。

备注:经验证名称需加“ 比如char jsonString[] = "{\"result\":true}";

以上就是小编为大家带来的json格式解析和libjson的用法介绍(关于cjson的使用方法)全部内容了,希望大家多多支持服务器之家~