json相关介绍

JSON

k/v形式表示数据,{}表示对象,[]表示数组

核心方法

  • parse > string to js obj

  • stringify > js obj to string

ie8

原生 json

var obj = {"a":1 "b":2, "c":3};
var str = JSON.stringify(obj); 

firefox,chrome 没问题,ie8 默认会 JSON Not Defined

原生JSON在JS 5.8后引入

IE8为最大保证向后兼容,默认用IE7渲染引擎(JS 5.7),但支持多种文档兼容性模式

所以 IE8中使用JSON原生对象是有条件的

1. <meta http-equiv="X-UA-Compatible" content="IE=8" >

2.<!DOCTYPE>

早期不支持原生json对象时的2个方法

  • eval
function strToJson(str){ 
    var json = eval('(' + str + ')'); 
    return json; 
} 
  • new Function
function strToJson(str){ 
    var json = (new Function("return " + str))(); 
    return json; 
} 

eval不太安全(恶意代码执行),new Function 写法看起来比较怪异

str = "alert('hello')";
eval(str);

安全做法是进行判断,不支持原生JSON(内置JSON对象:IE6、IE7:JSON.stringify() JSON“未定义”),用json2.js(json2.min.js)

https://github.com/douglascrockford/JSON-js

<!--[if lte IE 8]>
    <script type="text/javascript" src="/js/json2.js"></script>
<![endif]-->

中文乱码

IE8 原生JSON,stringify对象包含中文时,ie8会将中文转为unicode编码,post给服务器后。。。

var json = '{"PermID":"30","PermName":"普通员工级","Remark":"最基层员工使用的权限。"}'; 
var o = JSON.parse(json); 
document.write(JSON.stringify(o));
"PermID":"30","PermName":"u666eu901au5458u5de5u7ea7","Remark":"u6700u57fau5c42u5458u5de5u4f7fu7528u7684u6743u9650u3002"}

所以 IE8 还是用 json2 吧,统一 utf-8

序列化方法

var jsonObj = { id: '01', name: 'Tom' };
JSON.stringify(jsonObj);

反序列化方法

var jsonString = "{\"memberNull\": null, \"memberNum\": 3, \"memberStr\": \"StringJSON\", \"memberBool\": true , \"memberObj\": { \"mnum\": 1, \"mbool\": false}, \"memberX\": {}, \"memberArray\": [33, \"StringTst\",null,{}]}"; 
JSON.parse(jsonString);

可以在字符串中放任何脚本语句,包括声明语句:

define = "{name:'dd',email:'i@ddatsh.com'}";   //直接量方式
eval("data = "+define);
alert("name:"+data.name);
alert("email:"+data.email);

如果后台异步传来的文本是JavaScript的声明语句,一条eval方法就能解析了

对于解析复杂的XML,效率是多么大的提高!

XML:

<contact>
    <friend>
        <name>ddatsh</name>
        <email>ddatsh@gmail.com</email>
    </friend>
    <friend>
        <name>you</name>
        <email>you@gmail.com</email>
    </friend>
</contact>

JSON:

[
 {
   name:"dd",
   email:"ddatsh@gmail.com"
 },
 {
   name:"you",
   email:"you@gmail.com"
 }
]

简单的不只是表达上,最重要的是可以丢弃让人晕头转向的DOM解析了

只要符合js声明规范,js会自动帮你解析好

js直接量(Literals)

js创建对象,通常情况下可能会这样写:

function Person(name, sex) {
    this.name = name;
    this.sex = sex;
} 
var p = new Person('dd', 1);
alert(p.name);

js 1.2

var p = {"name": 'dd', "sex" : 24};

就是对象的“直接量

像java Map toString()输出

常见数组声明

var a = new Array();
a[0] = 'dd';
a[1] = 24;

或者

var a = new Array('dd', 24);

数组的直接量 如下:

var a = ['dd', 24];  

也很象 ArrayList toString() 输出

fastjson

http://www.zhdba.com/mysqlops/2011/08/07/fastjson/

  1. Serialize 优化
  • SerializeWriter

obj>json 字符串拼接性能差,StringBuilder的SerializeWriter提供针对性的方法减少数组越界检查

writeIntAndChar(int i, char c) ,一次写两个值到buf,减少一次越界检查

  • ThreadLocal缓存buf 减少对象分配和gc

  • asm避免反射

基于objectweb asm精简,获取bean属性值,需要调用反射,避免反射开销

  • IdentityHashMap

每种类型使用一种serializer,于是存在class -> JavaBeanSerizlier的映射

不用HashMap避免equals,transfer操作并发时可能死循环

ConcurrentHashMap比HashMap序列化会慢,因为其使用volatile和lock

IdentityHashMap去掉transfer操作的IdentityHashMap,能够在并发时工作,但是不会导致死循环

  • 缺省启用sort field输出

json object是一种key/value结构,正常的hashmap是无序的,fastjson缺省是排序输出的,为deserialize优化做准备

集成jdk实现的一些优化算法 在优化fastjson的过程中,参考了jdk内部实现的算法,比如int to char[]算法等等

  1. deserializer优化 也称为parser或者decoder,fastjson在这方面投入的优化精力最多
  • 读取token基于预测

所有的parser基本上都需要做词法处理,json也不例外 比如key之后,最大的可能是冒号”:”,value之后,可能是有两个,逗号”,”或者右括号”}” 基于预测能够做更少的处理就能够读取到token

  • sort field fast match算法

serialize按照key的顺序进行,于是deserializer时优化算法,假设key/value内容有序,读只需要做key匹配,而不需要把key从输入中读取出来

使fastjson少读取超过50%的token,用asm实现,性能提升十分明显,超过300%的性能提升

{ "id" : 123, "name" : "魏加流", "salary" : 56789.79}
  ------      --------          ----------

虚线标注的三个部分是key,如果key_id、key_name、key_salary这三个key是顺序的,就可以做优化处理,这三个key不需要被读取出来,只需要比较就可以了

这种算法分两种模式,一种是快速模式,一种是常规模式

快速模式是假定key是顺序的,能快速处理,如果发现不能够快速处理,则退回常规模式。保证性能的同时,不会影响功能

在这个例子中,常规模式需要处理13个token,快速模式只需要处理6个token

  • 用asm避免反射

deserialize的时候,会使用asm来构造对象,并且做batch set,即合并连续调用多个setter方法,而不是分散调用

  • 对utf-8的json bytes,针对性使用优化的版本来转换编码

com.alibaba.fastjson.util.UTF8Decoder,来源于JDK中的UTF8Decoder,但是它使用ThreadLocal Cache Buffer,避免转换时分配char[]的开销

ThreadLocal Cache的实现是这个类com.alibaba.fastjson.util.ThreadLocalCache。第一次1k,如果不够,会增长,最多增长到128k

  • symbolTable算法

xml或者javac的parser实现,把一些经常使用的关键字缓存起来,在遍历char[]的时候,同时把hash计算好,通过这个hash值在hashtable中来获取缓存好的symbol,避免创建新的字符串对象。这种优化在fastjson里面用在key的读取,以及enum value的读取。parse性能优化的关键算法之一

FastJSON原理

  • 对象 to JSON

反射找到类所有Get方法,get去掉,小写化,作JSON每个key值,getA>key值为 a,与真实的类成员名无关

  • JSON to pojo

同样反射找类所有Set方法,无参数构造函数(一定要有)new对象,JSON串中取出一个key 如 a,先大写化为A,那么从所有Set方法中找到 SetA(),然后进行赋值。 如果找不到 setA (seta也不行),那么该值被忽略,也不报错。

Jackson原理一致,JSON to Java pojo步骤中,更科学check,能识别seta。但getA和geta没,抛出异常(除非a设为忽略)

结论:

  • 编码(pojo to json): 当循环数量较小时,FastJSON的性能 低于 JackSON;

当循环数量越大时,FastJSON的性能开始超过Jackson;

  • 解码( json to pojo):当成员数量越大时,FastJSON的相对性能越差,JackSON的相对性能则越好;

当成员数量越小时,FastJSON的性能越好

  • 综合(编码+解码): 当成员变量数量越大时,Jackson 获胜。无关于循环数量

当成员变量数量越小时,FastJSON获胜

一般情况下,系统中的循环数量一般不会大的惊人,应该是偏小的。而成员变量变多,是一个企业级系统常见的情况

Jackson 工具类使用及配置指南