`
wenshao
  • 浏览: 271685 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

吹牛:我写的JSONParser可能是这个星球上最快了(Java)

阅读更多
我拥有多年手写Parser的经验,在以前,编写过多个Parser,有SQL的Parser,也有JavaScript的Parser,也有Java的Parser。

在最近的项目中,也需要使用JSON,其中client部分不希望存在依赖,所以就写了一个JSON     Parser。最初是直接使用SimpleJSON的,因为其代码少,接口简洁。一个同事说,SimpleJSON存在性能问题,噢,我想,那算了,我自己写一个吧,JSON语法这么简单,对于有“多年行医经验”(来自典故《日本兵与老中医》)的我,自然不是困难的事情,也就是一个晚上就写完了,应用于项目中,测试结果,网络协议的性能提升了两倍,从原来的80,000 TPS提升到了240,000 TPS。顺带说一个花絮,周六晚上加班写完的,直接就应用到项目中了,虽然有TestCase,但还是有一个地方没注意好,出现了一个Bug,星期一还影响了测试工作,早会的时候被同事血泪控诉。

我跟朋友炫耀了一下,朋友说,为啥不用jackson,jackson速度很快。好吧,我就和jackson做了一个性能比较,在大多数场景都是比jackson性能好很多,但是在某一个场景,竟然比jackson稍慢。我不服气啊,因为一看就觉得jackson的Parser写得不够专业,没理由比我这种多年经验的老鸟快的。跟踪其实现代码找到了其优化的办法,觉得那是取巧,不屑于使用。于是优化一下我写的Parser,优化的结果自然是全面超越它。好吧,废话少说,直接上结果。

场景1:
[{"S":321061,"T":"GetAttributeResp"},{"ERROR":null,"TS":0,"VAL":{"SqlList":[{"BatchSizeMax":0,"BatchSizeTotal":0,"ConcurrentMax":1,"DataSource":"jdbc:wrap-jdbc:filters=default,encoding:name=ds-offer:jdbc:mysql://100.10.10.10:8066/xxxx","EffectedRowCount":0,"ErrorCount":0,"ExecuteCount":5,"FetchRowCount":5,"File":null,"ID":2001,"LastError":null,"LastTime":1292742908178,"MaxTimespan":16,"MaxTimespanOccurTime":1292742668191,"Name":null,"RunningCount":0,"SQL":"SELECT @@SQL_MODE","TotalTime":83}]}}] 

fast-json : 8,608,334 (我写的parser)
jackson : 17,172,500
simple-json : 36,258,008


场景2:
{"S":321061,"T":"GetAttributeResp"}

fast-json : 1,028,024
jackson : 6,673,850
simple-json : 20,294,257

场景3:
引用
{"name":null,"flag":true}

fast-json : 842,032
jackson : 7,284,100
simple-json : 20,553,188

场景4:
[-5.041598256063065E-20,-7210028408342716000]

fast-json : 1,338,822
jackson : 10,000,239
simple-json : 20,202,679

场景5:
-6470204979932713723

fast-json : 389,004
jackson : 5,405,129
simple-json : 20,418,252

以上的结果都是执行一千次的时间总和,单位是nano。连续执行测试50次,取最后一次的结果,让JVM能够充分优化。JVM的参数是-server。

结论,越简单的对象,偶写的parser(fast-json)性能越好。如场景3,都快差不多100倍了。

好了,吹牛结束啦,有什么后续动作呢?有同事建议开源,嗯,这个建议挺好的,我喜欢用开源的东西,也希望对开源有所贡献,我整理好代码之后,就会开源出来。

注明,我写的Parser是能通过JSON官方的兼容性测试的,不是缺乏功能的残废!

----------------------------------------------

svn已经能够外网访问:
http://119.38.217.15/svn/fastjson/trunk/
(注意,IP地址在2011-1-10修改过)

域名申请的事情还在进行中,还有很多事情要做。最终这是一个类似taobao code(淘蝌蚪)的开源平台,我们的名字叫做Open Sesame。

分享到:
评论
295 楼 wenshao 2011-01-09  
应该是循环引用的问题,目前fastjson不会对循环引用作处理,所以一旦出现循环引用对象的序列化,就杯具了!
294 楼 lqixv 2011-01-09  
死循环?那会不会与我写的这个类有关?我的类的部分代码是:

public class Catalog extends IdEntity {

private String name;
private String description;
private int orderNo;

private Catalog parent;
private List<Catalog> children = Lists.newArrayList();
。。。。

我写的测试是:

System.out.println(JSON.toJSONString(catalogs));

这个类是不是与我的 catalog 类里最后那两句有关呢?
293 楼 wenshao 2011-01-09  
lqixv 写道
这个好像对内存要求太高了,我开发用的机器,全部内存才 2G,使用了你的这个 fastjson 后,总是内存溢出,没法跑。

Caused by: java.lang.StackOverflowError
at com.alibaba.fastjson.serializer.FieldSerializer.writePrefix(FieldSerializer.java:54)
at com.alibaba.fastjson.serializer.StringFieldSerializer.write(StringFieldSerializer.java:29)
at com.alibaba.fastjson.serializer.FieldSerializer.write(FieldSerializer.java:64)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:156)
at com.alibaba.fastjson.serializer.ObjectFieldSerializer.write(ObjectFieldSerializer.java:47)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:156)
at com.alibaba.fastjson.serializer.ListSerializer.write(ListSerializer.java:71)
at com.alibaba.fastjson.serializer.ListFieldSerializer.write(ListFieldSerializer.java:31)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
。。。。。。


这个是BUG,死循环了,不是对内存要求高。

能够把序列化的对象结构提供出来么?
292 楼 lqixv 2011-01-09  
这个好像对内存要求太高了,我开发用的机器,全部内存才 2G,运行完浏览器、数据库、qq等后,只剩大约 1G 多点。使用了你的这个 fastjson 后,总是内存溢出,没法跑。

Caused by: java.lang.StackOverflowError
at com.alibaba.fastjson.serializer.FieldSerializer.writePrefix(FieldSerializer.java:54)
at com.alibaba.fastjson.serializer.StringFieldSerializer.write(StringFieldSerializer.java:29)
at com.alibaba.fastjson.serializer.FieldSerializer.write(FieldSerializer.java:64)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:156)
at com.alibaba.fastjson.serializer.ObjectFieldSerializer.write(ObjectFieldSerializer.java:47)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:156)
at com.alibaba.fastjson.serializer.ListSerializer.write(ListSerializer.java:71)
at com.alibaba.fastjson.serializer.ListFieldSerializer.write(ListFieldSerializer.java:31)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
。。。。。。
291 楼 wenshao 2011-01-09  
@厌倦发呆

明白你的意思了,我的输出格式搞错啦,已经改好了!

看来是我对ECMAScript5所支持的ISO8601理解不正确 


290 楼 厌倦发呆 2011-01-09  
wenshao 写道
@厌倦发呆

是的,解析从宽,输出从严,原则和你所说的一样,解析的时候支持的特性如下:
public static final int DEFAULT_PARSER_FEATURE;
static {
	int features = 0;
	features |= Feature.AutoCloseSource.getMask();
	features |= Feature.InternFieldNames.getMask();
	features |= Feature.AllowUnQuotedFieldNames.getMask();
	features |= Feature.AllowSingleQuotes.getMask();
	DEFAULT_PARSER_FEATURE = features;
}



public static final int DEFAULT_GENERATE_FEATURE;
static {
	int features = 0;
	features |= com.alibaba.fastjson.serializer.Feature.QuoteFieldNames.getMask();
	features |= com.alibaba.fastjson.serializer.Feature.WriteMapNullValue.getMask();
	DEFAULT_GENERATE_FEATURE = features;
}


嗯,这里我是指ISO8601日期输出为字段值的时候,最好输出为字符串(加双引号)。即类似输出如下JSON:
{
  "createDate": "2011-01-01T20:12:33"
}
289 楼 wenshao 2011-01-09  
<p>有效代码代码数量已经到了4713行了,不过测试行覆盖率还保持到100%。以下是sonar的图表:</p>
<p><img src="http://dl.iteye.com/upload/picture/pic/79569/46e441a8-6734-34ef-ba51-8c7579d53fe9.png" alt="" width="694" height="258"></p>
<p> </p>
<p>从12月24日时的2000行,增长到了现在的4700行,代码的增长还是挺快的。</p>
<p> </p>
288 楼 wenshao 2011-01-09  
@厌倦发呆

是的,解析从宽,输出从严,原则和你所说的一样,解析的时候支持的特性如下:
public static final int DEFAULT_PARSER_FEATURE;
static {
	int features = 0;
	features |= Feature.AutoCloseSource.getMask();
	features |= Feature.InternFieldNames.getMask();
	features |= Feature.AllowUnQuotedFieldNames.getMask();
	features |= Feature.AllowSingleQuotes.getMask();
	DEFAULT_PARSER_FEATURE = features;
}



public static final int DEFAULT_GENERATE_FEATURE;
static {
	int features = 0;
	features |= com.alibaba.fastjson.serializer.Feature.QuoteFieldNames.getMask();
	features |= com.alibaba.fastjson.serializer.Feature.WriteMapNullValue.getMask();
	DEFAULT_GENERATE_FEATURE = features;
}
287 楼 厌倦发呆 2011-01-09  
wenshao 写道
parser也支持ISO8601的日期格式了,不过不是缺省支持的。如下:

场景1:
java.util.Date date = JSON.parseObject("2011-01-09T13:49:53", java.util.Date.class, Feature.AllowISO8601DateFormat);
Assert.assertEquals(new java.util.Date(1294552193000L), date);


场景2:
public static class Entity {
	private Date d;

	public Date getD() {
		return d;
	}

	public void setD(Date d) {
		this.d = d;
	}

}

Entity entity = JSON.parseObject("{d:2011-01-09T13:49:53}", Entity.class, Feature.AllowISO8601DateFormat);

Assert.assertEquals(new java.util.Date(1294552193000L), entity.getD());



场景3:
JSONObject object = JSON.parseObject("{d:2011-01-09T13:49:53}", Feature.AllowISO8601DateFormat);
Assert.assertEquals(new java.util.Date(1294552193000L), object.get("d"));




"{d:2011-01-09T13:49:53}"
此处d:之后是否应该是字符串呢?JSON规范中尚无原生日期格式,本着“解析从宽,输出从严”的原则,建议输出时输出为字符串,解析则两者皆支持可能更好

不过wenshao真是更新神速,呵呵。
286 楼 wenshao 2011-01-09  
parser也支持ISO8601的日期格式了,不过不是缺省支持的。如下:

场景1:
java.util.Date date = JSON.parseObject("2011-01-09T13:49:53", java.util.Date.class, Feature.AllowISO8601DateFormat);
Assert.assertEquals(new java.util.Date(1294552193000L), date);


场景2:
public static class Entity {
	private Date d;

	public Date getD() {
		return d;
	}

	public void setD(Date d) {
		this.d = d;
	}

}

Entity entity = JSON.parseObject("{d:2011-01-09T13:49:53}", Entity.class, Feature.AllowISO8601DateFormat);

Assert.assertEquals(new java.util.Date(1294552193000L), entity.getD());



场景3:
JSONObject object = JSON.parseObject("{d:2011-01-09T13:49:53}", Feature.AllowISO8601DateFormat);
Assert.assertEquals(new java.util.Date(1294552193000L), object.get("d"));

285 楼 wenshao 2011-01-09  
wtusmchen 写道
又签出测了一下,速度提高不少,特别是今天那个特别慢的问题解决了,时间类型也可以了。
从json-lib到jsontools、gson、jackson,目前来看jackson最好用最快,期待fastjson发布正式版,把我现在用的jackson替换掉。


正式版的发布,最早在春节前,最晚2月底。谢谢支持 
284 楼 wenshao 2011-01-09  
long作为默认实现,而且明确是millis。刚才实现了序列化支持ISO8601,parser的实现还未做。

如下:
// 带毫秒
StringWriter out = new StringWriter();

JSONSerializer serializer = new JSONSerializer();
serializer.config(Feature.UseISO8601DateFormat, true);
Assert.assertEquals(true, serializer.isEnabled(Feature.UseISO8601DateFormat));
serializer.write(out, new Date(1294552193254L));

Assert.assertEquals("2011-01-09T13:49:53.254", out.toString());


//不带毫秒
StringWriter out = new StringWriter();

JSONSerializer serializer = new JSONSerializer();
serializer.config(Feature.UseISO8601DateFormat, true);
Assert.assertEquals(true, serializer.isEnabled(Feature.UseISO8601DateFormat));
serializer.write(out, new Date(1294552193000L));

Assert.assertEquals("2011-01-09T13:49:53", out.toString());


//不带时间
StringWriter out = new StringWriter();

JSONSerializer serializer = new JSONSerializer();
serializer.config(Feature.UseISO8601DateFormat, true);
Assert.assertEquals(true, serializer.isEnabled(Feature.UseISO8601DateFormat));
serializer.write(out, new Date(1294502400000L));

Assert.assertEquals("2011-01-09", out.toString());
283 楼 厌倦发呆 2011-01-09  
wenshao 写道
@厌倦发呆

你的建议很好的,这是我没想到的。但是我不打算把它当作缺省实现,而是一个支持的特性。


顺便提一下,如果用long作为默认实现的话,有一个轻微的跨平台问题,据我的印象,不同语言平台对日期和long的转换的定义有所不同,有的是以millisecond为单位的,有的是以nanosecond为单位的,还有的是以tick为单位的,并且起始时点的选取也有所不同,如fantom语言的标准库就是以2000-01-01T00:00:00UTC为起始时点的。
不过这个问题不严重,只要对转换的long值有一个标准定义,解析的时候转换一下就好。
282 楼 wenshao 2011-01-09  
@厌倦发呆

你的建议很好的,这是我没想到的。但是我不打算把它当作缺省实现,而是一个支持的特性。
281 楼 厌倦发呆 2011-01-09  
wenshao 写道
日期的序列化,目前不同的json processor的实现都是不一样的,如下:

  • fastjson : new Date(1294408029033)
  • jackson : 1294408029033
  • simplejson : Fri Jan 07 21:47:09 CST 2011
  • json-lib : {"date":7,"day":5,"hours":21,"minutes":47,"month":0,"seconds":9,"time":1294408029033,"timezoneOffset":-480,"year":111}
  • gson : "2011-1-7 21:47:09"


目前fastjson采用支持new Date()和long两个格式输出,也可以自定义。大家有什么建议么?

simplejson在日期的序列化是乱来的,json-lib也是乱来的。gson的方式倒是可以考虑直接支持的(目前可以通过扩展支持)。


new Date()的方式是否不标准呢? 至少ECMAScript5的JSON.parse会抛异常
建议支持ISO8601的格式yyyy-MM-ddThh:mm:ss.SSSZ
无论是JodaTime还是ECMAScript5都有良好的支持,应该是日期格式标准的一个趋势
280 楼 wtusmchen 2011-01-08  
又签出测了一下,速度提高不少,特别是今天那个特别慢的问题解决了,时间类型也可以了。
从json-lib到jsontools、gson、jackson,目前来看jackson最好用最快,期待fastjson发布正式版,把我现在用的jackson替换掉。
279 楼 wenshao 2011-01-08  
william_ai 写道
MapSerializer这个类的write怎么没有用buffer啊?


不是很明白你的所说的意思,使用什么buffer?
278 楼 wenshao 2011-01-08  
已经对大数据量的JSON序列化的场景做了一些优化。性能超越jackson。请朋友们重新测试 

一种流氓的做法,和jackson不同,但是都是很流氓的做法 

谢谢wtusmchen帮忙测试 

我的测试

int len = 1000 * 10;
Integer[] array = new Integer[len];
for (int i = 0; i < len; ++i) {
	array[i] = i;
}

List<Integer> list = Arrays.asList(array);
for (Codec codec : codecList) {
	for (int i = 0; i < 5; ++i) {
		encode(list, codec);
	}
	
	System.out.println();
}


结果:
不带server参数
引用
jackson : 639,458,560
jackson : 591,625,179
jackson : 585,910,184
jackson : 582,816,511
jackson : 581,708,546

fastjson : 726,399,732
fastjson : 399,060,741
fastjson : 398,201,205
fastjson : 398,156,372
fastjson : 398,497,205


-server
引用
jackson : 437,903,625
jackson : 338,124,715
jackson : 340,192,846
jackson : 330,229,643
jackson : 329,674,237

fastjson : 573,217,188
fastjson : 318,612,916
fastjson : 317,806,317
fastjson : 318,100,122
fastjson : 318,471,804

277 楼 luffyke 2011-01-08  
Copyright 1999-2101 Alibaba Group.

笔误啊,温少,现在是2011,不是2101哦
276 楼 luffyke 2011-01-08  
日期的序列化,目前不同的json processor的实现都是不一样的,如下:
引用


  • fastjson : new Date(1294408029033)
  • jackson : 1294408029033
  • simplejson : Fri Jan 07 21:47:09 CST 2011
  • json-lib : {"date":7,"day":5,"hours":21,"minutes":47,"month":0,"seconds":9,"time":1294408029033,"timezoneOffset":-480,"year":111}
  • gson : "2011-1-7 21:47:09"


目前fastjson采用支持new Date()和long两个格式输出,也可以自定义。大家有什么建议么?

simplejson在日期的序列化是乱来的,json-lib也是乱来的。gson的方式倒是可以考虑直接支持的(目前可以通过扩展支持)。

个人感觉像jackson一样直接Long输出好点。。

相关推荐

    elasticdump报错

    6: v8::internal::Handle&lt;v8::internal::String&gt; v8::internal::JsonParser&lt;false&gt;::SlowScanJsonString&lt;v8::internal::SeqTwoByteString, unsigned short&gt;(v8::internal::Handle&lt;v8::internal::String&gt;, int, int)...

    JSONParser所需jar包 json simple jar包

    JSONParser是Java编程语言中用于解析JSON(JavaScript Object Notation)数据的一种工具。JSON是一种轻量级的数据交换格式,它基于ECMAScript的一个子集,采用完全独立于语言的文本格式,易于人阅读和编写,同时也...

    JSONParser.java

    json解析的示例

    JsonParser_java:纯java写的JSON解析器-eclipse工程

    "JsonParser_java"项目就是一个基于纯Java实现的JSON解析器,适用于Eclipse开发环境。 这个项目的核心目标是提供一个轻量级的解决方案,让用户能够理解和控制JSON解析过程。在Java中,这通常涉及读取JSON字符串,将...

    Kotlin-JsonParser:Kotlin-JsonParser是用于JSON解析的Kotlin库。 它可以将JSON字符串解析为Kotlin Object或以一种简单的方式进行反向

    Kotlin-杰森·帕瑟(Kotlin-JsonParser) Kotlin-JsonParser是用于JSON解析的Kotlin库。 它可以将JSON字符串解析为Kotlin Object或以一种简单的方式反向。... implementation 'com.tomatobean:jsonparser:1.0.

    matlab开发-JSONParser

    对于`JSONParser`这个项目,`license.txt`可能指定了该代码的许可协议,例如MIT、Apache 2.0或者GPL等。这些协议规定了代码的使用、修改和分发的规则,确保了开源软件的合规使用。 在实际应用中,理解JSONParser的...

    开源项目-buger-jsonparser.zip

    在Go语言中,标准库`encoding/json`提供了JSON的解析和编码功能,但本项目buger/jsonparser作为替代方案,旨在提供更快的性能,这对于处理大量JSON数据的程序尤其重要。 JSON(JavaScript Object Notation)是一种...

    jsonparser.zip

    - JSON-P (JSR 353) 和 JSON-B (JSR 374):Java EE标准,提供了API进行JSON处理。 2. JSON解析的基本步骤: - 创建JSON对象或数组:根据JSON字符串,使用解析器创建相应的JSONObject或JSONArray对象。 - 读取...

    JSON转化工具类 JSONParser.zip

    JSON转化工具类 JSONParser ,iOS数据转化组件 (JSONParser),支持JSON字符串、模型、字典、模型数组、字典数组之间...

    Android代码-JsonParser

    JsonParser Json parsing tool can flexibly configure Import add to build.gradle,${latest.version} is dependencies { compile 'com.blakequ.parser:parser:${latest.version}' } maven ...

    JSONParser:在 Java 对象上使用 Gson (toJsonfromJson)

    JSON解析器在 Java 对象上使用 Gson (toJson / fromJson) 展示了 Gson 库的简单使用,将 Java 对象转换为 JSON 格式,反之亦然; 将 JSON 字符串转换为 Java 对象。

    JSONParser-master_websocket_源码.rar

    JSONParser-master_...这个压缩包可能包含了实现这些功能的源代码,具体细节需要解压后查看代码来深入理解。如果你需要开发基于WebSocket的实时应用,并且涉及到JSON数据的交换,这个源码将是一个很好的学习资源。

    java程序B站json字幕转srt

    9. **面向对象编程**:这个程序很可能采用了面向对象的设计模式,将不同的功能封装到不同的类中,如`JsonParser`用于解析JSON,`SrtGenerator`用于生成SRT,这样可以使代码更清晰、可维护性更强。 10. **软件工程...

    JSONParser-master_websocket_源码.zip

    结合JSONParser和WebSocket,这个项目可能提供了一个解决方案,用于通过WebSocket实时传输和解析JSON数据。例如,服务器可以发送JSON格式的消息,客户端接收到消息后,使用JSONParser解析成对象,然后进行相应的业务...

    JSONParser:JSON解析器

    - `JSONParser` 类:虽然名为`JSONParser`,但这个库实际上并没有提供一个单独的解析器类。解析JSON通常通过`JSONObject`或`JSONArray`的构造函数完成,它们可以接受一个字符串参数并自动解析。 2. **...

    jsonParser

    这个库可能是一个开源项目,用于帮助开发者高效地解析和生成JSON格式的数据。 JSON格式本身具有易读性,它由键值对组成,键用引号括起来,值可以是字符串、数字、布尔值、数组、对象或者null。在C++中,解析JSON...

    JsonParser.java

    利用java反射机制实现的json与java对象互相转化的工具,只暴露了两个空开的接口,其他接口都是私有的。

    PyPI 官网下载 | jsonparser-1.0.tar.gz

    Python的内置`json`模块提供了基本的JSON编码和解码功能,但`jsonparser`可能提供额外的特性或优化,例如更快的解析速度、更丰富的错误处理机制或者支持特定的JSON扩展。 **安装与使用** 要安装`jsonparser`,首先...

Global site tag (gtag.js) - Google Analytics