Json解析C#的四个库

  目前通讯中http是使用最多的,而其中Json基本是首选。大家平时解析时都是直接调用dll,但是有没有考虑过dll里面怎么处理?这个dll又从哪里来?本文将分享我接触到的4个解析Json的C#开源库。

  目前我用得较多的是Newtonsoft ,后面了解到还有轻量的MiniJSON,SimpleJson以及litjson。

  这里MiniJSON最精简,是一个class文件,通过对string进行简单字符串的处理来解析。

  然后就是SimpleJson,也是一个class文件,不过代码量超级多,功能相对比较全面。看其他博客对这个评价蛮高的,不过我没使用过,暂不评价。

  litjson,就文件多一点,好几个class组成。但是看部分博客提到跨平台上有问题。

  由于之前是做windows应用,所以一直使用的是Newtonsoft,代码量远超上面的几个,编译出来的dll也是比他们的要大。所以,比较推荐用Newtonsoft,可定制化高很多,功能和内部考虑的东西也完善。

  本文暂不讲怎么使用,只简单介绍怎么去github获取上面几个的源码,以及怎么编译。

  提到github,这是一个好东西,好多好的开源项目都在上面。不过国内网速超级慢,使用起来不是很方便。一般情况下,可以不注册账号直接下载项目代码,下载下来是一个zip文件。也可以用VS里面的扩展插件GitHub Extension for Visual Studio,下载安装过程有点久,请耐心等待:

Result pic 1

  使用起来相对没svn好用(当然,这里是可以用svn来使用的)。

  推荐使用客户端:GitHubDesktop 下载下来的应该是一个77M的全量包,直接安装就可以使用:

Result pic 2

  这里使用到的就是File–>Clone…;打开后

Result pic 3

  这里的url,就是网页上获取的:

Result pic 4

  这里贴一下看到的其他博主写的专门介绍GitHub Desktop的:Windows 上 GitHub Desktop 的操作

一.MiniJSON

  github地址:MiniJSON

  直接在你的项目中新建一个class,全选代码后复制过去,就可以使用了。

  由于需要vpn,所以这里放一下代码吧:

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
/*
* Copyright (c) 2013 Calvin Rien
*
* Based on the JSON parser by Patrick van Bergen
* http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
*
* Simplified it so that it doesn't throw exceptions
* and can be used in Unity iPhone with maximum code stripping.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace MiniJSON {
// Example usage:
//
// using UnityEngine;
// using System.Collections;
// using System.Collections.Generic;
// using MiniJSON;
//
// public class MiniJSONTest : MonoBehaviour {
// void Start () {
// var jsonString = "{ \"array\": [1.44,2,3], " +
// "\"object\": {\"key1\":\"value1\", \"key2\":256}, " +
// "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " +
// "\"unicode\": \"\\u3041 Men\u00fa sesi\u00f3n\", " +
// "\"int\": 65536, " +
// "\"float\": 3.1415926, " +
// "\"bool\": true, " +
// "\"null\": null }";
//
// var dict = Json.Deserialize(jsonString) as Dictionary<string,object>;
//
// Debug.Log("deserialized: " + dict.GetType());
// Debug.Log("dict['array'][0]: " + ((List<object>) dict["array"])[0]);
// Debug.Log("dict['string']: " + (string) dict["string"]);
// Debug.Log("dict['float']: " + (double) dict["float"]); // floats come out as doubles
// Debug.Log("dict['int']: " + (long) dict["int"]); // ints come out as longs
// Debug.Log("dict['unicode']: " + (string) dict["unicode"]);
//
// var str = Json.Serialize(dict);
//
// Debug.Log("serialized: " + str);
// }
// }

/// <summary>
/// This class encodes and decodes JSON strings.
/// Spec. details, see http://www.json.org/
///
/// JSON uses Arrays and Objects. These correspond here to the datatypes IList and IDictionary.
/// All numbers are parsed to doubles.
/// </summary>
public static class Json {
/// <summary>
/// Parses the string json into a value
/// </summary>
/// <param name="json">A JSON string.</param>
/// <returns>An List&lt;object&gt;, a Dictionary&lt;string, object&gt;, a double, an integer,a string, null, true, or false</returns>
public static object Deserialize(string json) {
// save the string for debug information
if (json == null) {
return null;
}

return Parser.Parse(json);
}

sealed class Parser : IDisposable {
const string WORD_BREAK = "{}[],:\"";

public static bool IsWordBreak(char c) {
return Char.IsWhiteSpace(c) || WORD_BREAK.IndexOf(c) != -1;
}

enum TOKEN {
NONE,
CURLY_OPEN,
CURLY_CLOSE,
SQUARED_OPEN,
SQUARED_CLOSE,
COLON,
COMMA,
STRING,
NUMBER,
TRUE,
FALSE,
NULL
};

StringReader json;

Parser(string jsonString) {
json = new StringReader(jsonString);
}

public static object Parse(string jsonString) {
using (var instance = new Parser(jsonString)) {
return instance.ParseValue();
}
}

public void Dispose() {
json.Dispose();
json = null;
}

Dictionary<string, object> ParseObject() {
Dictionary<string, object> table = new Dictionary<string, object>();

// ditch opening brace
json.Read();

// {
while (true) {
switch (NextToken) {
case TOKEN.NONE:
return null;
case TOKEN.COMMA:
continue;
case TOKEN.CURLY_CLOSE:
return table;
default:
// name
string name = ParseString();
if (name == null) {
return null;
}

// :
if (NextToken != TOKEN.COLON) {
return null;
}
// ditch the colon
json.Read();

// value
table[name] = ParseValue();
break;
}
}
}

List<object> ParseArray() {
List<object> array = new List<object>();

// ditch opening bracket
json.Read();

// [
var parsing = true;
while (parsing) {
TOKEN nextToken = NextToken;

switch (nextToken) {
case TOKEN.NONE:
return null;
case TOKEN.COMMA:
continue;
case TOKEN.SQUARED_CLOSE:
parsing = false;
break;
default:
object value = ParseByToken(nextToken);

array.Add(value);
break;
}
}

return array;
}

object ParseValue() {
TOKEN nextToken = NextToken;
return ParseByToken(nextToken);
}

object ParseByToken(TOKEN token) {
switch (token) {
case TOKEN.STRING:
return ParseString();
case TOKEN.NUMBER:
return ParseNumber();
case TOKEN.CURLY_OPEN:
return ParseObject();
case TOKEN.SQUARED_OPEN:
return ParseArray();
case TOKEN.TRUE:
return true;
case TOKEN.FALSE:
return false;
case TOKEN.NULL:
return null;
default:
return null;
}
}

string ParseString() {
StringBuilder s = new StringBuilder();
char c;

// ditch opening quote
json.Read();

bool parsing = true;
while (parsing) {

if (json.Peek() == -1) {
parsing = false;
break;
}

c = NextChar;
switch (c) {
case '"':
parsing = false;
break;
case '\\':
if (json.Peek() == -1) {
parsing = false;
break;
}

c = NextChar;
switch (c) {
case '"':
case '\\':
case '/':
s.Append(c);
break;
case 'b':
s.Append('\b');
break;
case 'f':
s.Append('\f');
break;
case 'n':
s.Append('\n');
break;
case 'r':
s.Append('\r');
break;
case 't':
s.Append('\t');
break;
case 'u':
var hex = new char[4];

for (int i=0; i< 4; i++) {
hex[i] = NextChar;
}

s.Append((char) Convert.ToInt32(new string(hex), 16));
break;
}
break;
default:
s.Append(c);
break;
}
}

return s.ToString();
}

object ParseNumber() {
string number = NextWord;

if (number.IndexOf('.') == -1) {
long parsedInt;
Int64.TryParse(number, out parsedInt);
return parsedInt;
}

double parsedDouble;
Double.TryParse(number, out parsedDouble);
return parsedDouble;
}

void EatWhitespace() {
while (Char.IsWhiteSpace(PeekChar)) {
json.Read();

if (json.Peek() == -1) {
break;
}
}
}

char PeekChar {
get {
return Convert.ToChar(json.Peek());
}
}

char NextChar {
get {
return Convert.ToChar(json.Read());
}
}

string NextWord {
get {
StringBuilder word = new StringBuilder();

while (!IsWordBreak(PeekChar)) {
word.Append(NextChar);

if (json.Peek() == -1) {
break;
}
}

return word.ToString();
}
}

TOKEN NextToken {
get {
EatWhitespace();

if (json.Peek() == -1) {
return TOKEN.NONE;
}

switch (PeekChar) {
case '{':
return TOKEN.CURLY_OPEN;
case '}':
json.Read();
return TOKEN.CURLY_CLOSE;
case '[':
return TOKEN.SQUARED_OPEN;
case ']':
json.Read();
return TOKEN.SQUARED_CLOSE;
case ',':
json.Read();
return TOKEN.COMMA;
case '"':
return TOKEN.STRING;
case ':':
return TOKEN.COLON;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
return TOKEN.NUMBER;
}

switch (NextWord) {
case "false":
return TOKEN.FALSE;
case "true":
return TOKEN.TRUE;
case "null":
return TOKEN.NULL;
}

return TOKEN.NONE;
}
}
}

/// <summary>
/// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string
/// </summary>
/// <param name="json">A Dictionary&lt;string, object&gt; / List&lt;object&gt;</param>
/// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
public static string Serialize(object obj) {
return Serializer.Serialize(obj);
}

sealed class Serializer {
StringBuilder builder;

Serializer() {
builder = new StringBuilder();
}

public static string Serialize(object obj) {
var instance = new Serializer();

instance.SerializeValue(obj);

return instance.builder.ToString();
}

void SerializeValue(object value) {
IList asList;
IDictionary asDict;
string asStr;

if (value == null) {
builder.Append("null");
} else if ((asStr = value as string) != null) {
SerializeString(asStr);
} else if (value is bool) {
builder.Append((bool) value ? "true" : "false");
} else if ((asList = value as IList) != null) {
SerializeArray(asList);
} else if ((asDict = value as IDictionary) != null) {
SerializeObject(asDict);
} else if (value is char) {
SerializeString(new string((char) value, 1));
} else {
SerializeOther(value);
}
}

void SerializeObject(IDictionary obj) {
bool first = true;

builder.Append('{');

foreach (object e in obj.Keys) {
if (!first) {
builder.Append(',');
}

SerializeString(e.ToString());
builder.Append(':');

SerializeValue(obj[e]);

first = false;
}

builder.Append('}');
}

void SerializeArray(IList anArray) {
builder.Append('[');

bool first = true;

foreach (object obj in anArray) {
if (!first) {
builder.Append(',');
}

SerializeValue(obj);

first = false;
}

builder.Append(']');
}

void SerializeString(string str) {
builder.Append('\"');

char[] charArray = str.ToCharArray();
foreach (var c in charArray) {
switch (c) {
case '"':
builder.Append("\\\"");
break;
case '\\':
builder.Append("\\\\");
break;
case '\b':
builder.Append("\\b");
break;
case '\f':
builder.Append("\\f");
break;
case '\n':
builder.Append("\\n");
break;
case '\r':
builder.Append("\\r");
break;
case '\t':
builder.Append("\\t");
break;
default:
int codepoint = Convert.ToInt32(c);
if ((codepoint >= 32) && (codepoint <= 126)) {
builder.Append(c);
} else {
builder.Append("\\u");
builder.Append(codepoint.ToString("x4"));
}
break;
}
}

builder.Append('\"');
}

void SerializeOther(object value) {
// NOTE: decimals lose precision during serialization.
// They always have, I'm just letting you know.
// Previously floats and doubles lost precision too.
if (value is float) {
builder.Append(((float) value).ToString("R"));
} else if (value is int
|| value is uint
|| value is long
|| value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is ulong) {
builder.Append(value);
} else if (value is double
|| value is decimal) {
builder.Append(Convert.ToDouble(value).ToString("R"));
} else {
SerializeString(value.ToString());
}
}
}
}
}

二.SimpleJson

  github地址:SimpleJson

  你可以下载下来然后用vs打开选Net2.0编译出来一个dll(这里可能会报错,将文件EscapeToJavascriptStringTests去掉就正常了)。但是你也可以像前面那样,新建一个class,然后复制SimpleJson.cs的代码,点击SimpleJson.cs后会进到下面的详情网页,选择图中的Raw,就能进到代码页面,然后Ctrl+A进行全选复制。

Result pic 5

三.LitJSON

  github地址:LitJSON

  这个生成超级烦,要搭.net core的环境。由于没接触core开发,后面我还是修改vs工程文件,去掉core后才能生成。

  (后面发现,其实这里是因为VS的版本问题,要新版本。这里有篇文章就是问这个的:msbuild-of-vs2017-cannot-compile-net-standard-2-0-project 查官网,只提到装2017,但是明明我的是2017(但是是16年底下载的,Core2.0是17年底出的),.NET Core 2.0.0 SDK这个也安装了,就是无法编译,其实是msbuild 版本。这个问题,和VS2010的msbuild编译不了2015版本的项目是一样的。所以如果装了新版本的,这里不用修改,是可以直接编译的。)

  这里也贴一下主页:LitJSON

  以及一个老版本的直接dll下载地址:DownloadDll

  推荐直接下载github的然后自己编译,因为后面有更新,而网上那些,好多都是好几年前的版本。

下面记录一下我怎么编译的:

  实际是修改vs工程文件,然后直接用vs打开就可以编译的,不过这里我也记录一下他自带的那些工具是怎么配置到可以用的。

  先记录怎么修改vs工程文件,目录中,实际只要用到\litjson-develop\src\LitJson里的东西:

Result pic 6

  用记事本打开,ctrl+f查找包含“netstandard”的字段,就是下面截图红色部分,删掉:

Result pic 7

  这里简单说明一下为什么要删掉:

  “netstandard”部分是core环境的,目前我没搭这个环境,所以直接编译是会报错的。

  而中间的那个,是检查git的,如果你是部署了git,登陆了帐号,就没影响,可以更新并编译,不然就会报错。

  删掉上面红色部分后,用vs打开,直接编译就成功了。

接下来介绍怎么修改自带的那些工具来生成,过程繁琐,不过不用修改vs工程文件:

  首先运行build.ps1,记住不是sh(这个是linux的),右键–>使用PowerShell运行:

Result pic 8

Result pic 9

  出现下面这样的窗口,实际是里面代码,分析出你要安装这个环境的这个包,正常情况是一直下载不下来的,vpn也救不了,后面挂百度云离线 ( 提取码:0osd)总算下载下来了。

Result pic 10

  现在目录中多了一个文件夹,里面是安装core的环境的,可以安装,目前我系统是win10版:

Result pic 11

  这里也简单记一下怎么修改代码让dotnet-install.ps1文件能运行:

  首先右键–>编辑;里面好多脚本代码,和C#超级像,有兴趣可以去了解:

Result pic 12

  这里意思是传一个下载链接给它,然后下载解压(上面一堆脚本都是下载函数)。而现在这个url,是下不下来的,不过我们可以通过IIS,进行localhost下载:

  windows启用IIS,在程序与功能–>启用或关闭windows功能,然后你看到有iis的就打勾就行了。

Result pic 13

  然后找到上面图中这个,点击“浏览”,把刚才通过百度云下载的dotnet-sdk-2.1.4-win-x64.zip复制过去,当然还有复制dotnet-install.ps1这个文件,不过.ps1这个后缀,在iis里面不能访问,所以我修改成txt,反正脚本中只是找文件下载,下载后保存的命名是自己定的。

Result pic 14

  然后就是修改脚本:

Result pic 15

  修改build.ps1,告诉它去localhost目录下载这个txt;同时修改这个txt去localhost下载这个zip。

  这样就能正常运行了,但是这里由于网络原因,还会报错,就安装cake部分,不过可以重新启动就行,cake的下载和安装过程较顺利,安装好后多了3个文件夹:

Result pic 16

  一个是检查git的,一个是编译工具cake。

  所以如果想用这个工具编译,最好是登陆git的,这样就能用自带工具编译了,core也能编译:

Result pic 17

  这里我删了core的环境安装部分,然后直接运行build.ps1就可以了。

  这种方式就不用修改vs工程文件。

  从ps1文件里面了解到,是调用cake来编译的,所以,其实我们可以直接cmd来操作:

Result pic 18

  cmd到build.cake所在目录,然后调用cake.exe。

四.Newtonsoft.Json

  主页:www.newtonsoft.com

  github地址:Newtonsoft.Json

  这个就编译比较简单,但是会报错readonly struct(去掉readonly就行),用vs编辑工程文件,像前面那个一样删掉core和phone等等那些选项,然后再编译就能编译通过。自带的脚本也是,下载部分很顺利就能下载,155m左右;但是还有附加的编译环境,也是core的。

  这里这个错,其实是C# 7的特性,老版本的vs都不支持。然后core的问题,其实也是更新新版本vs就能解决,后面我更新了新版本后,msbuild版本上来了,就能正常编译了。为什么在第三个那里没注意到时msbuild,因为那里用的是cake,所以当时是能用脚本编译,但是不能vs编译,后面找多点资料后才想起是这个可能。所以这里我就直接更新vs,然后就能直接编译了。什么都不用改。

  这里放一下vs2017最新版的安装索引包吧:

两个官网下载地址:

  地址1:vs2017-relnotes#15.1.26430.06

  地址2:visualstudio.microsoft.com

  以及.NET downloads:.NET

  附加一下百度云,就是本篇文章用到的配置环境所要下载的东西(要vpn的那几个):

  链接 提取码:1t2x

  这里有个文章提到这个新特性:C# 7 Series, Part 6: Read-only structs

Result pic 19

  以及两篇介绍的:

  .NET Core 2.0及.NET Standard 2.0

  NET Standard/Core中配置使用TargetFrameworks输出多版本类库及测试:

  平时用这个库比较多的原因,就是可定制化高,可以设置null值要不要序列化 ,等等之类的定制。

结尾附几个博主写的库分享:

  序列化效率比拼——谁是最后的赢家Newtonsoft.Json

  几个常用Json组件的性能测试


The End

姚佳鑫 wechat
如果您对我的文章感兴趣,可以添加我的微信
感谢您的支持