由于Java标准序列化机制的一些限制,实践中经常使用一些替代方 案,比如XML/JSON/MessagePack
。
Java SDK中对这些格式的支持有限,有很多第三方的类库提供了更为方便的支持,Jackson是其中一种, 它支持多种格式,包括XML/JSON/MessagePack等
,本节就来介绍如何使用Jackson进行序列化。我们先来简单了解下这些格式以及Jackson。
XML/JSON都是文本格式
,都容易阅读和理解,格式细节我们就不 介绍了,后面我们会看到一些例子,来演示其基本格式。
XML是最早 流行的跨语言数据交换标准格式
,如果不熟悉,可以查看
https://www.w3school.com.cn/xml/index.asp
快速了解。
JSON是一种更为简单的格式
,最近几年来越来越流行,如果不熟悉,可以查 看
http://json.org/json-zh.html
。
MessagePack是一种二进制形式的JSON, 编码更为精简高效
,官网地址是
http://msgpack.org/
。
JSON有多种二进制形式,MessagePack只是其中一种。
Jackson的Wiki地址是
http://wiki.fasterxml.com/JacksonHome
,
它起初主要是用来支持JSON格式的,现在也支持很多其他格式,它的各种 方式的使用方式是类似的。要使用Jackson,需要下载相应的库
。对于 JSON/XML,本节使用2.8.5版本,对于MessagePack,本节使用0.8.11版 本,所有依赖库均可从以下地址下载:
https://github.com/swiftma/program-logic/tree/master/jackson_libs
。配置好依赖库后,下面我们就来介绍如何使用。
我们还是通过Student类来演示Jackson的基本用法,格式包括 JSON、XML和Message-Pack。
序列化一个Student对象的基本代码为:
Jackson序列化的主要类是ObjectMapper,它是一个线程安全的类, 可以初始化并配置一次,被多个线程共享, SerializationFeature.INDENT_OUTPUT的目的是格式化输出,以便于阅 读。ObjectMapper的writeValueAsString方法就可以将对象序列化为字符串
,输出为:
ObjectMapper还有其他方法,可以输出字节数组,写出到文件、 OutputStream、Writer等
,方法声明如下:
比如,输出到文件"student.json",代码为:
ObjectMapper怎么知道要保存哪些字段呢?与Java标准序列化机制 一样,它也使用反射,默认情况下,它会保存所有声明为public的字 段,或者有public getter方法的字段。
反序列化的代码如下所示:
使用readValue方法反序列化
,有两个参数:
一个是输入源
,这里是 文件student.json;
另一个是反序列化后的对象类型
,这里是 Student.class,输出为:
说明反序列化的结果是正确的,除了接受文件,还可以是字节数组、字符串、Input-Stream、Reader等
,如下所示:
在反序列化时,默认情况下,Jackson假定对象类型有一个无参的构 造方法,它会先调用该构造方法创建对象,然后解析输入源进行反序列化。
使用类似的代码,格式可以为XML,
唯一需要改变的是替换 ObjectMapper为Xml-Mapper。XmlMapper是ObjectMapepr的子类
,序列 化代码为:
反序列化代码为:
类似的代码,格式可以为MessagePack,
同样使用ObjectMapper 类,但传递一个Mess-agePackFactory对象
。另外,
MessagePack是二进 制格式,不能写出为String,可以写出为文件、OutpuStream或字节数组
。序列化代码为:
序列后的字节如图14-4所示。
反序列化代码为:
对于容器对象,Jackson也是可以自动处理的
,但用法稍有不同,我 们来看下List和Map。
序列化一个学生列表的代码为:
这与序列化一个学生对象的代码是类似的,输出为:
反序列化代码不同,要新建一个TypeReference匿名内部类对象来指定类型
,代码如下所示:
XML/MessagePack的代码是类似的
,我们就不赘述了。
Map与List类似,序列化不需要特殊处理,但反序列化需要通过 TypeReference指定类型
,我们看一个XML的例子。序列化一个学生Map 的代码为:
反序列化的代码为:
对于复杂一些的对象,Jackson也是可以自动处理的
,我们让 Student类稍微复杂一些,改为如下定义:
分数改为一个Map,键为课程,ContactInfo表示联系信息,是一个 单独的类,定义如下:
构建一个ComplexStudent对象,代码为:
我们看JSON序列化,代码没有特殊的,如下所示:
XML格式的代码也是类似的,替换ObjectMapper为XmlMapper即可
,输出为:
反序列化的代码也不需要特殊处理,指定类型为 ComplexStudent.class即可。
上面的例子中,我们没有做任何定制,默认的配置就是可以的。但 很多情况下,我们需要做一些配置,
Jackson主要支持两种配置方法
。
1)注解,后续章节会详细介绍注解,这里主要是介绍Jackson一些 注解的用法。
2)配置ObjectMapper对象,ObjectMapper支持对序列化和反序列化 过程做一些配置,前面使用的SerializationFeature.INDENT_OUTPUT是 其中一种。
哪些情况需要配置呢?我们看一些典型的场景。
1)配置达到类似标准序列化中transient关键字的效果,忽略一些字段。
2)在标准序列化中,可以自动处理引用同一个对象、循环引用的 情况,反序列化时,可以自动忽略不认识的字段,可以自动处理继承多 态,但Jackson都不能自动处理,这些情况都需要进行配置。
3)标准序列化的结果是二进制、不可读的,但XML/JSON格式是 可读的,有时我们希望控制这个显示的格式。
4)默认情况下,反序列时,Jackson要求类有一个无参构造方法, 但有时类没有无参构造方法,Jackson支持配置其他构造方法。
针对这些场景,我们分别介绍。
在Java标准序列化中,如果字段标记为了transient,就会在序列化 中被忽略
,在Jack-son中,可以使用以下两个注解之一。
·@JsonIgnore:用于字段、getter或setter方法,任一地方的效果都一 样。
·@JsonIgnoreProperties:用于类声明,可指定忽略一个或多个字 段。
比如,上面的Student类,忽略分数字段,可以为:
也可以修饰getter方法,如:
也可以修饰Student类,如:
加了以上任一标记后,序列化后的结果中将不再包含score字段,在 反序列化时,即使输入源中包含score字段的内容,也不会给score字段赋值。
我们看个简单的例子,有两个类Common和A,A中有两个Common 对象,为便于演示,我们将所有属性定义为了public,它们的类定义如下:
有一个A对象,如下所示:
a对象的first和second都指向都一个c对象
,不加额外配置,序列化a的代码为:
输出为:
在反序列化后,first和second将指向不同的对象
,如下所示:
那怎样才能保持这种对同一个对象的引用关系呢?可以使用注解 @JsonIdentityInfo,对Common类做注解
,如下所示:
@JsonIdentityInfo中指定了两个属性,property="id"表示在序列化输 出中新增一个属性"id"以表示对象的唯一标示,generator表示对象唯一 ID的产生方法,这里是使用整数顺序数产生器IntSequenceGenerator。
加了这个标记后,序列化输出会变为:
注意:“first"中加了一个属性"id”,而"second"的值只是1,表示引用 第一个对象,这个格式反序列化后,first和second会指向同一个对象。
我们看个循环引用的例子。有两个类Parent和Child,它们相互引 用,为便于演示,我们将所有属性定义为了public,类定义如下:
有一个对象,如下所示:
如果序列化parent这个对象,Jackson会进入无限循环,最终抛出异常
,解决这个问题,
可以分别标记Parent类中的child和Child类中的 parent字段,将其中一个标记为主引用,而另一个标记为反向引用,主 引用使用@JsonManagedReference,反向引用使用 @JsonBackReference
,如下所示:
加了这个注解后,序列化就没有问题了
。我们看XML格式的序列 化代码:
在输出中,反向引用没有出现。不过,在反序列化时,Jackson会自 动设置Child对象中的parent字段的值
,比如:
输出为:老马。
说明标记为反向引用的字段的值也被正确设置了
。
在Java标准序列化中,反序列化时,对于未知字段会自动忽略,但在Jackson中,默认情况下会抛出异常
。还是以Student类为例,如果 student.json文件的内容为:
其中,other属性是Student类没有的,如果使用标准的反序列化代码:
Jackson会抛出异常:
怎样才能忽略不认识的字段呢?可以配置ObjectMapper
,如下所 示:
这样就没问题了,
这个属性是配置在整个ObjectMapper上的,如果只是希望配置Student类,可以在Student类上使用如下注解
:
Jackson也不能自动处理多态的情况
。我们看个例子,有4个类,定 义如下,我们忽略了构造方法和getter/setter方法:
ShapeManager中的Shape列表中的对象可能是Circle,也可能是 Square
。比如,有一个ShapeManager对象,如下所示:
使用JSON格式序列化,输出为:
这个输出看上去是没有问题的,但由于输出中没有类型信息,反序 列化时,Jackson不知道具体的Shape类型是什么,就会抛出异常
。
解决方法是在输出中包含类型信息,在基类Shape前使用如下注解
:
这些注解看上去比较多,
含义是指在输出中增加属性"type",表示 对象的实际类型,对Circle类,使用"circle"表示其类型,而对于Square 类,使用"square"
。加了注解后,序列化输出变为:
这样,反序列化时就可以正确解析了。
对于XML/JSON格式,有时,我们希望修改输出的名称,比如对 Student类,我们希望输出的字段名变为对应的中文,可以使用
@JsonProperty进行注解
,如下所示:
加了这个注解后,输出的JSON格式变为:
对于XML格式,一个常用的修改是根元素的名称。默认情况下, 它是对象的类名,比如对Student对象,它是"Student",如果希望修改, 比如改为小写"student",可以使用
@JsonRootName修饰整个类
,如下所 示:
默认情况下,日期的序列化格式为一个长整数,比如:
序列化代码:
输出如下所示:
这个格式是不可读的,怎样才能可读呢?
使用@JsonFormat注解
, 如下所示:
加注解后,输出变为如下所示:
前面的Student类,
如果没有定义默认构造方法,只有如下构造方法
:
则反序列化时会抛异常,提示找不到合适的构造方法,可以使用 @JsonCreator和@Json-Property标记该构造方法
,如下所示:
这样,反序列化就没有问题了。
需
要说明的是,对于XML格式,Jackson的支持不是太全面
。比如,
对于一个Map<String,List>对象,Jackson可以序列化,但不能反序列化
,如下所示:
在反序列化时,代码会抛出异常,如果mapper是一个ObjectMapper 对象,反序列化就没有问题
。如果Jackson不能满足需求,可以考虑其他库,如XStream(
http://x-stream.github.io/
)。
本节介绍了如何使用Jackson来实现JSON/XML/MessagePack序列 化。使用方法是类似的,主要是创建的ObjectMapper对象不一样,很多 情况下,不需要做额外配置,但也有很多情况,需要做额外配置,配置 方式主要是注解,我们介绍了Jackson中的很多典型注解,大部分注解适 用于所有格式
。本节完整的代码在github上,地址 为
https://github.com/swiftma/program-logic
,位于包shuo.laoma.file.c63 下。
Jackson还支持很多其他格式,如YAML、AVRO、Protobuf、Smile 等。Jackson中也还有很多其他配置和注解,用得相对较少,限于篇幅, 我们就不介绍了。
从注解的用法,我们可以看出,它也是一种神奇的特性,它类似于 注释,但却能实实在在改变程序的行为,它是怎么做到的呢?我们暂且 搁置这个问题,留待到第22章介绍。
至此,关于文件的整个内容就介绍完了,从下一章开始,让我们一 起探索并发和线程的世界!
绝大多数内容来自于:Java编程的逻辑 作者: 马俊昌(14.5 使用Jackson序列化为JSON/XML/MessagePack)
Java官方文档
https://docs.oracle.com/javase/specs/index.html
前期写了一篇关于 DOM4j 解析
XML
的,但是得知
Jackson
也支持解析
XML
,所以打算也写一篇关于
Jackson
解析
XML
的,俗称
xml
的
序列化
和反
序列化
这边我
使用
maven 构建工程
找到 pom.
xml
添加依赖
<?
xml
version="1.0" encoding="UTF-8"?>
<project
xml
ns="http://...
我们在
使用
Message
Pack
对 List 对象数据进行
序列化
的时候,发现
序列化
以后的二进制数组数据偏大的情况。
请注意,不是所有的 List 对象都会出现这种情况,这个根据你 List 对象中存储的内容有关。
有关本问题的测试源代码请参考:https://github.com/cwiki-us-demo/serialize-deserialize-demo-
java
/blob/mast...
Jackson
实现
xml
序列化
和反
序列化
本文介绍
Jackson
2.X中提供的
xml
序列化
功能。仅介绍基本操作,不涉及复杂和自定义功能。
1.
Xml
Mapper对象
Xml
Mapper是
Jackson
2.x中提供我们实现
xml
序列化
的主要类,因此首先需要创建其实例:
Xml
Mapper mapper = new
Xml
Mapper();
想要maven依赖脚本为:
<dependen...
Jackson
关于
Xml
和
Json
互转中,
Xml
特殊格式问题
Github地址:https://github.com/lingshr/
jackson
-
xml
-example
选型:
json
-lib.jar可以做,但是太老了,抛弃; fast
json
完全没有
xml
相关功能; staxon经测试不能用。 最终选用
jackson
来主要实现。JDK为1.8,如果低于1.8,则只需要将try-catch的语法改回传统书写方式。目前的实现,传参和回参都是String,如果想
JSON
Object对象和
XML
对象互转,只需要再包一层就可以多2个或4个方法。一、依赖pom.
xml
co...
Jackson
是当前用的比较广泛的,用来
序列化
和反
序列化
json
的
Java
的开源框架。
Jackson
社区相对比较活跃,更新速度也比较快。Spring MVC 的默认
json
解析器便是
Jackson
。(目前最新稳定版本:2.13.4)