序列化与反序列化以及⼏种JSON库的性能⽐较
⼀、序列化与反序列化
内存中的数据对象只有转换成⼆进制才可以进⾏数据持久化和⽹络传输。将数据对象转换成⼆进制的流程称之为对象的序列化(Serialization)。
反之,将⼆进制流恢复为数据对象的过程称之为反序列化(Deserialization)。序列化需要保留充分的信息以恢复数据对象,但是为了节省存储空间和⽹络带宽,序列化后的⼆进制流⼜要尽可能的⼩。序列化常见的使⽤时RPC框架的数据传输。
⼆、常见的序列化⽅式
2.1 Java原⽣序列化
Java类型通过实现Serializable接⼝来实现该类对象的序列化,这个接⼝⾮常特殊,没有任何⽅法,只是起到⼀个标识符的作⽤。Java序列化保留了对象的元数据(如类、成员变量、继承类信息等),以及对象数据等,兼容性最好,但是不⽀持跨语⾔,同时性能不是最好的。
实现Serializable接⼝的类建议设置serialVersionUID字段值,如果不设置,那么每次运⾏时,编译器会根据类的内部实现,包括类名、接⼝名、⽅法名和属性等来⾃动⽣成serialVersionUID。如果类的源代码有修改,那么重新编译后serialVersionUID的取值可能会变化。因此实现Serializable接⼝的类最好是显⽰低设置serialVersionUID值,修改类时候可以根据需要或者兼容性来决定是否需要修改serialVersionUID值。
SerialVersionUID是⼀个标识符,当它通常使⽤对象的哈希码序列化时会标记在对象上。我们可以通过Java中serialver⼯具到该对象的serialVersionUID。
如果是兼容升级,请不要修改serialVersionUID字段,避免反序列化失败 java.io.NotSerializableException。
如果是不兼容升级,需要修改serialVersionUID值,避免反序列化失败java.io.NotSerializableException。
使⽤Java原⽣态序列化需要注意,Java反序列化时候不会调⽤类的⽆参构造⽅法,⽽是调⽤native⽅法将成员变量赋值为对应类型的初始值,基于性能以及兼容性的考虑,不推荐使⽤Java序列化。
2.3 Hessian序列化
Hessian序列化是⼀种⽀持动态类型、跨语⾔、基于对象传输的⽹络协议。Java对象序列化的⼆进制
流可以被其他语⾔(如:C++,python 等语⾔)反序列化。
特性:
⾃描述序列化类型,不依赖外部描述⽂件或接⼝定义,⽤⼀个字节表⽰常⽤基础类型,极⼤的缩短了⼆进制流。
语⾔⽆关性,⽀持脚本语⾔
协议简单,⽐Java原⽣态序列化⾼效
相⽐Hessian 1.0、Hessian 2.0中增加了压缩编码,其序列化⼆进制流⼤⼩是Java原⽣态序列化的50%,序列化耗时是Java原⽣态⼤⼩的30%,反序列化耗时是Java原⽣态反序列化的20%。
Hessian 会把复杂对象所有属性存储在⼀个map中进⾏序列化。所以在⽗类、⼦类存在同名成员变量的情况下,Hessian 序列化时,先序列化⼦类,然后序列化⽗类,因此反序列化结果会导致⼦类同名成员变量被⽗类的值覆盖。
2.4 JSON序列化
JSON序列化这⾥的JSON=JavaScript Object Notation,是⼀种轻量级的数据交换格式,JSON 序列化就是将数据对象转换成JSON字符串。在序列化过程中跑起来类型信息,所以反序列化时候只有提供类型信息才能准确低反序列化。相⽐前⾯两种⽅式JSON可读性笔记好,⽅便调试。
序列化通常会通过⽹络协议传输对象,⽽对象中往往有敏感数据,所以序列化常常是⿊客们的攻击点,攻击者穷秒地利⽤反序列化过程构造恶意代码,是得程序在反序列化过程中执⾏任意代码。
Java⼯程中⼴泛使⽤的Apache Commons Collections、Jackson、fastjson等都出现过反序列化漏洞。
2.5 如果防范这种⿊客攻击呢?
将⼀些对象的敏感信息不进⾏序列化传输,可以加关键字transient修饰,避免把该属性信息转化为序列化的⼆进制流。如果⼀定要传递对象的敏感信息,也可以使⽤对称加密和⾮对称加密⽅式独⽴传输,再使⽤某个⽅法把属性还原丹对象中。transient 修饰符仅适⽤于变量,不适⽤于⽅法和类。在序列化时,如果我们不想序列化特定变量以满⾜安全约束,那么我们应该将该变量声明为transient。执⾏序列化时,JVM会忽略transient变量的原始值并将默认值保存到⽂件中。因此,transient意味着不要序列化。
应⽤开发者对序列化要有⼀定的安全意识防范,对传⼊数据的内容进⾏校验或者权限控制,及时更新安全漏洞,避免遭到⿊客攻击。
三、常见的JSON序列化⽅式性能⽐较
⽬前对于Java开源的JSON类库有很多种,下⾯我们取4个常⽤的JSON库进⾏性能测试对⽐, 同时根据测试结果分析如果根据实际应⽤场景选择最合适的JSON库。
这4个JSON类库分别为:Gson,FastJson,Jackson,Json-lib。
简单介绍
选择⼀个合适的JSON库要从多个⽅⾯进⾏考虑:
1. 字符串解析成JSON性能
2. 字符串解析成JavaBean性能
3. JavaBean构造JSON性能
4. 集合构造JSON性能
5. 易⽤性
先简单介绍下四个类库的⾝份背景
Gson
Gson是⽬前功能最全的Json解析神器,Gson当初是为因应Google公司内部需求⽽由Google⾃⾏研发⽽来,但⾃从在2008年五⽉公开发布第⼀版后已被许多公司或⽤户应⽤。Gson的应⽤主要为toJson与fromJson两个转换函数,⽆依赖,不需要例外额外的jar,能够直接跑在JDK上。在使⽤这种对象转换之前,需先创建好对象的类型以及其成员才能成功的将JSON字符串成功转换成相对应的对象。类⾥⾯只要有get和set⽅法,Gson完全可以实现复杂类型的json到bean或bean到json的转换,是JSON解析的神器。
FastJson
Fastjson是⼀个Java语⾔编写的⾼性能的JSON处理器,由阿⾥巴巴公司开发。⽆依赖,不需要例外额外的jar,能够直接跑在JDK上。FastJson在复杂类型的Bean转换Json上会出现⼀些问题,可能会出现引⽤的类型,导致Json转换出错,需要制定引⽤。FastJson采⽤独创的算法,将parse的速度提升到极致,超过所有json库。
Jackson
Jackson是当前⽤的⽐较⼴泛的,⽤来序列化和反序列化json的Java开源框架。Jackson社区相对⽐较
活跃,更新速度也⽐较快, 从Github中的统计来看,Jackson是最流⾏的json解析器之⼀,Spring MVC的默认json解析器便是Jackson。
Jackson优点很多:
1. Jackson 所依赖的jar包较少,简单易⽤。
2. 与其他 Java 的 json 的框架 Gson 等相⽐,Jackson 解析⼤的 json ⽂件速度⽐较快。
3. Jackson 运⾏时占⽤内存⽐较低,性能⽐较好
4. Jackson 有灵活的 API,可以很容易进⾏扩展和定制。
⽬前最新版本是2.9.4,Jackson 的核⼼模块由三部分组成:
1. jackson-core 核⼼包,提供基于”流模式”解析的相关 API,它包括 JsonPaser 和 JsonGenerator。Jackson 内部实现正是通过
⾼性能的流模式 API 的 JsonGenerator 和 JsonParser 来⽣成和解析 json。
2. jackson-annotations 注解包,提供标准注解功能;
3. jackson-databind 数据绑定包,提供基于”对象绑定” 解析的相关 API( ObjectMapper )和”树模型” 解析的相关
API(JsonNode);基于”对象绑定” 解析的 API 和”树模型”解析的 API 依赖基于”流模式”解析的 API。
Json-lib
json-lib最开始的也是应⽤最⼴泛的json解析⼯具,json-lib 不好的地⽅确实是依赖于很多第三⽅包,对于复杂类型的转换,json-lib对于json转换成bean还有缺陷, ⽐如⼀个类⾥⾯会出现另⼀个类的list或者map集合,json-lib从json到bean的转换就会出现问题。json-lib在功能和性能上⾯都不能满⾜现在互联⽹化的需求。
编写性能测试
接下来开始编写这四个库的性能测试代码。
添加maven依赖
当然⾸先是添加四个库的maven依赖,公平起见,我全部使⽤它们最新的版本:
<!-- Json libs-->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId&le.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
shell脚本基本命令awk<artifactId>fastjson</artifactId>
控件不具有caption属性<version>1.2.46</version>
</dependency>
<dependency>
<groupId>com.</groupId>
python解析json文件<artifactId>jackson-databind</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>com.</groupId>
<artifactId>jackson-annotations</artifactId>
sql msdtc不可用<version>2.9.4</version>
</dependency>
四个库的⼯具类
FastJsonUtil.java
public class FastJsonUtil {
public static String bean2Json(Object obj) {
JSONString(obj);
}
public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
return JSON.parseObject(jsonStr, objClass);
}
}
GsonUtil.java
public class GsonUtil {
private static Gson gson = new GsonBuilder().create();
public static String bean2Json(Object obj) {建站模板更改
Json(obj);
}
public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
return gson.fromJson(jsonStr, objClass);
}
public static String jsonFormatter(String uglyJsonStr) {
Gson gson = new GsonBuilder().setPrettyPrinting().create(); JsonParser jp = new JsonParser();
JsonElement je = jp.parse(uglyJsonStr);
Json(je);
c++数组长度怎么获取}
}
JacksonUtil.java
public class JacksonUtil {
private static ObjectMapper mapper = new ObjectMapper();
public static String bean2Json(Object obj) {
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
try {
adValue(jsonStr, objClass);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
JsonLibUtil.java
public class JsonLibUtil {
public static String bean2Json(Object obj) {
JSONObject jsonObject = JSONObject.fromObject(obj);
String();
}
@SuppressWarnings("unchecked")
public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
return (T) Bean(JSONObject.fromObject(jsonStr), objClass); }
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论