Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。Jackson 社 区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson 是最流行的 json 解析器之一 。 Spring MVC 的默认 json 解析器便是 Jackson。 Jackson 优点很多。 Jackson 所依赖的 jar 包较少 ,简单易用。与其他 Java 的 json 的框架 Gson 等相比, Jackson 解析大的 json 文件速度比较快;Jackson 运行时占用内存比较低,性能比较好;Jackson 有灵活的 API,可以很容易进行扩展和定制。
一、Jackson
Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。Jackson 社 区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson 是最流行的 json 解析器之一 。 Spring MVC 的默认 json 解析器便是 Jackson。 Jackson 优点很多。 Jackson 所依赖的 jar 包较少 ,简单易用。与其他 Java 的 json 的框架 Gson 等相比, Jackson 解析大的 json 文件速度比较快;Jackson 运行时占用内存比较低,性能比较好;Jackson 有灵活的 API,可以很容易进行扩展和定制。
Jackson 的 1.x 版本的包名是 org.codehaus.jackson ,当升级到 2.x 版本时,包名变为 com.fasterxml.jackson,本文讨论的内容是基于最新的 Jackson 的 2.9.1 版本。
Jackson 的核心模块由三部分组成。
- jackson-core,核心包,提供基于”流模式”解析的相关 API,它包括 JsonPaser 和 JsonGenerator。 Jackson 内部实现正是通过高性能的流模式 API 的 JsonGenerator 和 JsonParser 来生成和解析 json。
- jackson-annotations,注解包,提供标准注解功能;
- jackson-databind ,数据绑定包, 提供基于”对象绑定” 解析的相关 API ( ObjectMapper ) 和”树模型” 解析的相关 API (JsonNode);基于”对象绑定” 解析的 API 和”树模型”解析的 API 依赖基于”流模式”解析的 API。
在了解 Jackson 的概要情况之后,下面介绍 Jackson 的基本用法。
1、Jackson 的 基本用法
若想在 Java 代码中使用 Jackson 的核心模块的 jar 包 ,需要在 pom.xml 中添加如下信息。
1.1 在 pom.xml 的 Jackson 的配置信息
1 | <dependency> |
jackson-databind 依赖 jackson-core 和 jackson-annotations,当添加 jackson-databind 之后, jackson-core 和 jackson-annotations 也随之添加到 Java 项目工程中。在添加相关依赖包之后,就可以使用 Jackson。
1.2 ObjectMapper 的 使用
Jackson 最常用的 API 就是基于”对象绑定” 的 ObjectMapper。下面是一个 ObjectMapper 的使用的简单示例。
示例
1 | ObjectMapper mapper = new ObjectMapper(); |
ObjectMapper 通过 writeValue 系列方法 将 java 对 象序列化 为 json,并 将 json 存 储成不同的格式,String(writeValueAsString),ByteArray(writeValueAsString),Writer, File,OutStream 和 DataOutput。
ObjectMapper 通过 readValue 系列方法从不同的数据源像 String , Byte Array, Reader,File,URL, InputStream 将 json 反序列化为 java 对象。
1 | ObjectMapper objectMapper = new ObjectMapper(); |
1 | 如果JSON字符串包含其值设置为null的字段(对于在相应的Java对象中是基本数据类型(int,long,float,double等)的字段),Jackson ObjectMapper默认会处理基本数据类型为null的情况,我们可以可以将Jackson ObjectMapper默认配置为失效objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);,这样基本数据为null就会转换失败。 |
上面描述了json反序列化成java对象,下面描述下java对象序列化成json
Jackson ObjectMapper也可以用于从对象生成JSON。 可以使用以下方法之一进行操作:
writeValue()
writeValueAsString()
writeValueAsBytes()
1 | //此示例首先创建一个ObjectMapper,然后创建一个Car实例,最后调用ObjectMapper的writeValue()方法,该方法将Car对象转换为JSON并将其写入给定的FileOutputStream。 |
信息配置
在调用 writeValue 或调用 readValue 方法之前,往往需要设置 ObjectMapper 的相关配置信息。这些配置信息应用 java 对象的所有属性上。示例如下:
1.3、配置信息使用示例
1 | //在反序列化时忽略在 json 中存在但 Java 对象不存在的属性 |
更多配置信息可以查看 Jackson 的 DeserializationFeature,SerializationFeature 和 I nclude。
1.4、Jackson 的 注解的使用
Jackson 根据它的默认方式序列化和反序列化 java 对象,若根据实际需要,灵活的调整它的默认方式,可以使用 Jackson 的注解。常用的注解及用法如下。
1.4.1 Jackson 注解
JSON工具包包含一组Java注解,可以使用这些注解来设置将JSON读入对象的方式或从对象生成什么JSON的方式。 此Jackson注解教程介绍了如何使用Jackson的注解。
下面是一些常用的注解:
注解 | 用法 |
---|---|
@JsonProperty | 用于属性,把属性的名称序列化时转换为另外一个名称。示例: @JsonProperty(“birth_ d ate”) private Date birthDate; |
@JsonFormat | 用于属性或者方法,把属性的格式序列化时转换成指定的格式。示例: @JsonFormat(timezone = “GMT+8”, pattern = “yyyy-MM-dd HH:mm”) public Date getBirthDate() |
@JsonPropertyOrder | 用于类, 指定属性在序列化时 json 中的顺序 , 示例: @JsonPropertyOrder({ “birth_Date”, “name” }) public class Person |
@JsonCreator | 用于构造方法,和 @JsonProperty 配合使用,适用有参数的构造方法。 示例: @JsonCreator public Person(@JsonProperty(“name”)String name) {…} |
@JsonAnySetter | 用于属性或者方法,设置未反序列化的属性名和值作为键值存储到 map 中 @JsonAnySetter public void set(String key, Object value) { map.put(key, value); } |
@JsonAnyGetter | 用于方法 ,获取所有未序列化的属性 public Map<String, Object> any() { return map; } |
1.4.2 Read + Write注解
Jackson包含一组注解,这些注解会影响从JSON读取Java对象以及将Java对象写入JSON。 我将这些注解称为“读+写注解”。 以下各节将更详细地介绍Jackson的读写注解。
1.4.2.1 @JsonIgnore
Jackson注解@JsonIgnore用于告诉Jackson忽略Java对象的某个属性(字段)。 在将JSON读取到Java对象中以及将Java对象写入JSON时,都将忽略该属性。
1.4.2.2 @JsonIgnoreProperties
@JsonIgnoreProperties Jackson注解用于指定要忽略的类的属性列表。 @JsonIgnoreProperties注解放置在类声明上方,而不是要忽略的各个属性(字段)上方。
1 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
在此示例中,属性firstName和lastName都将被忽略,因为它们的名称在类声明上方的@JsonIgnoreProperties注解声明内列出。
1.4.2.3 @JsonIgnoreType
@JsonIgnoreType Jackson注解用于将整个类型(类)标记为在使用该类型的任何地方都将被忽略。
1 | import com.fasterxml.jackson.annotation.JsonIgnoreType; |
1.4.2.4 @JsonAutoDetect
Jackson注解@JsonAutoDetect用于告诉Jackson在读写对象时包括非public修饰的属性
1 | import com.fasterxml.jackson.annotation.JsonAutoDetect; |
JsonAutoDetect.Visibility类包含与Java中的可见性级别匹配的常量,表示ANY,DEFAULT,NON_PRIVATE,NONE,PROTECTED_AND_PRIVATE和PUBLIC_ONLY。
1.4.3、Read注解
Jackson包含一组注解,这些注解仅影响Jackson将JSON解析为对象的方式-意味着它们影响Jackson对JSON的读取。 我称这些为“读注解”。 以下各节介绍了Jackson的读注解。
1、@JsonSetter
Jackson注解@JsonSetter用于告诉Jackson,当将JSON读入对象时,应将此setter方法的名称与JSON数据中的属性名称匹配。 如果Java类内部使用的属性名称与JSON文件中使用的属性名称不同,这个注解就很有用了。
以下Person类用personId名称对应JSON中名为id的字段:
1 | public class Person { |
但是在此JSON对象中,使用名称id代替personId:
1 | { |
Jackson无法将id属性从JSON对象映射到Java类的personId字段。
@JsonSetter注解指示Jackson为给定的JSON字段使用setter方法。 在我们的示例中,我们在setPersonId()方法上方添加@JsonSetter注解。
这是添加@JsonSetter注解的实例:
1 | public class Person { |
@JsonSetter注解中指定的值是要与此setter方法匹配的JSON字段的名称。 在这种情况下,名称为id,因为这是我们要映射到setPersonId()setter方法的JSON对象中字段的名称。
2、@JsonAnySetter
Jackson注解@JsonAnySetter表示Jackson为JSON对象中所有无法识别的字段调用相同的setter方法。 “无法识别”是指尚未映射到Java对象中的属性或设置方法的所有字段。
看一下这个Bag类:
1 | public class Bag { |
然后查看此JSON对象:
1 | { |
Jackson无法直接将此JSON对象的id和name属性映射到Bag类,因为Bag类不包含任何公共字段或setter方法。
可以通过添加@JsonAnySetter注解来告诉Jackson为所有无法识别的字段调用set()方法,如下所示
1 | public class Bag { |
现在,Jackson将使用JSON对象中所有无法识别的字段的名称和值调用set()方法。
请记住,这仅对无法识别的字段有效。 例如,如果您向Bag Java类添加了公共名称属性或setName(String)方法,则JSON对象中的名称字段将改为映射到该属性/设置器。
3、@JsonCreator
Jackson注解@JsonCreator用于告诉Jackson该Java对象具有一个构造函数(“创建者”),该构造函数可以将JSON对象的字段与Java对象的字段进行匹配。
@JsonCreator注解在无法使用@JsonSetter注解的情况下很有用。 例如,不可变对象没有任何设置方法,因此它们需要将其初始值注入到构造函数中。
以这个PersonImmutable类为例:
1 | public class PersonImmutable { |
要告诉Jackson应该调用PersonImmutable的构造函数,我们必须在构造函数中添加@JsonCreator注解。 但是,仅凭这一点还不够。 我们还必须注解构造函数的参数,以告诉Jackson将JSON对象中的哪些字段传递给哪些构造函数参数。
添加了@JsonCreator和@JsonProperty注解的PersonImmutable类的示例如下:
1 | public class PersonImmutable { |
请注意,构造函数上方的注解以及构造函数参数之前的注解。 现在,Jackson能够从此JSON对象创建PersonImmutable:
1 | { |
4、@JacksonInject
Jackson注解@JacksonInject用于将值注入到解析的对象中,而不是从JSON中读取这些值。 例如,假设正在从各种不同的源下载Person JSON对象,并且想知道给定Person对象来自哪个源。 源本身可能不包含该信息,但是可以让Jackson将其注入到根据JSON对象创建的Java对象中。
要将Java类中的字段标记为需要由Jackson注入其值的字段,请在该字段上方添加@JacksonInject注解。
这是一个示例PersonInject类,在属性上方添加了@JacksonInject注解:
1 | public class PersonInject { |
为了让Jackson将值注入属性,需要在创建Jackson ObjectMapper时做一些额外的工作。
这是让Jackson将值注入Java对象的过程:
1 | InjectableValues inject = new InjectableValues.Std().addValue(String.class, "jenkov.com"); |
请注意,如何在InjectableValues addValue()方法中设置要注入到source属性中的值。 还要注意,该值仅绑定到字符串类型-而不绑定到任何特定的字段名称。 @JacksonInject注解指定将值注入到哪个字段。
如果要从多个源下载人员JSON对象,并为每个源注入不同的源值,则必须为每个源重复以上代码。
5、@JsonDeserialize
Jackson注解@JsonDeserialize用于为Java对象中给定的属性指定自定义反序列化器类。
例如,假设想优化布尔值false和true的在线格式,使其分别为0和1。
首先,需要将@JsonDeserialize注解添加到要为其使用自定义反序列化器的字段。 这是将@JsonDeserialize注解添加到字段的示例:
1 | public class PersonDeserialize { |
其次,这是@JsonDeserialize注解中引用的OptimizedBooleanDeserializer类的实例:
1 | public class OptimizedBooleanDeserializer |
请注意,OptimizedBooleanDeserializer类使用通用类型Boolean扩展了JsonDeserializer。 这样做会使deserialize()方法返回一个布尔对象。 如果要反序列化其他类型(例如java.util.Date),则必须在泛型括号内指定该类型。
可以通过调用jsonParser参数的getText()方法来获取要反序列化的字段的值。 然后,可以将该文本反序列化为任何值,然后输入反序列化程序所针对的类型(在此示例中为布尔值)。
最后,需要查看使用自定义反序列化器和@JsonDeserializer注解反序列化对象的格式:
1 | PersonDeserialize person = objectMapper |
注意,我们首先需要如何使用ObjectMapper的reader()方法为PersonDeserialize类创建一个阅读器,然后在该方法返回的对象上调用readValue()。
1.4.4、Write注解
Jackson还包含一组注解,这些注解可以影响Jackson将Java对象序列化(写入)到JSON的方式。 以下各节将介绍这些写(序列化)注解中的每一个。
1、@JsonInclude
Jackson注解@JsonInclude告诉Jackson仅在某些情况下包括属性。 例如,仅当属性为非null,非空或具有非默认值时,才应包括该属性。 这是显示如何使用@JsonInclude注解的示例:
1 | import com.fasterxml.jackson.annotation.JsonInclude; |
如果为该示例设置的值是非空的,则此示例将仅包括name属性,这意味着不为null且不是空字符串。
@JsonInclude注解的一个更通俗的名称应该是@JsonIncludeOnlyWhen,但是写起来会更长。
2、@JsonGetter
@JsonGetter Jackson注解用于告诉Jackson,应该通过调用getter方法而不是通过直接字段访问来获取某个字段值。 如果您的Java类使用jQuery样式的getter和setter名称,则@JsonGetter注解很有用。
例如,您可能拥有方法personId()和personId(long id),而不是getPersonId()和setPersonId()。
这是一个名为PersonGetter的示例类,它显示了@JsonGetter注解的用法:
1 | public class PersonGetter { |
如您所见,personId()方法带有@JsonGetter注解。 @JsonGetter注解上设置的值是JSON对象中应使用的名称。 因此,用于JSON对象中personId的名称是id。 生成的JSON对象如下所示:
1 | {"id":0} |
还要注意,personId(long personId)方法使用@JsonSetter注解进行注解,以使Jackson识别为与JSON对象中的id属性匹配的设置方法。 从JSON读取Java对象时使用@JsonSetter注解-将Java对象写入JSON时不使用。 为了完整起见,仅包含@JsonSetter注解。
3、@JsonAnyGetter
@JsonAnyGetter Jackson注解使您可以将Map用作要序列化为JSON的属性的容器。 这是在Java类中使用@JsonAnyGetter注解的示例:
1 | public class PersonAnyGetter { |
当看到@JsonAnyGetter注解时,Jackson将从@JsonAnyGetter注解的方法中获取返回的Map,并将该Map中的每个键值对都视为一个属性。 换句话说,Map中的所有键值对都将作为PersonAnyGetter对象的一部分序列化为JSON。
4、@JsonPropertyOrder
@JsonPropertyOrder Jackson注解可用于指定将Java对象的字段序列化为JSON的顺序。 这是显示如何使用@JsonPropertyOrder注解的示例:
1 |
|
通常,Jackson会按照在类中找到的顺序序列化PersonPropertyOrder中的属性。 但是,@JsonPropertyOrder注解指定了不同的顺序,在序列化的JSON输出中,name属性将首先出现,personId属性将随后出现。
5、@JsonRawValue
@JsonRawValue Jackson注解告诉Jackson该属性值应直接写入JSON输出。 如果该属性是字符串,Jackson通常会将值括在引号中,但是如果使用@JsonRawValue属性进行注解,Jackson将不会这样做。
为了更清楚@JsonRawValue的作用,看看没有使用@JsonRawValue的此类:
1 | public class PersonRawValue { |
Jackson会将其序列化为以下JSON字符串:
1 | {"personId":0,"address":"$#"} |
现在,我们将@JsonRawValue添加到address属性,如下所示:
1 | public class PersonRawValue { |
现在,当对地址属性进行序列化时,杰克逊将省略引号。 因此,序列化的JSON如下所示:
1 | {"personId":0,"address":$#} |
当然它是无效的JSON,那么为什么要这么做呢?
如果address属性包含一个JSON字符串,那么该JSON字符串将被序列化为最终的JSON对象,作为JSON对象结构的一部分,而不仅是序列化为JSON对象的address字段中的字符串。
要查看其工作原理,让我们像下面这样更改address属性的值:
1 | public class PersonRawValue { |
Jackson会将其序列化为以下JSON:
1 | {"personId":0,"address":{ "street" : "Wall Street", "no":1}} |
请注意,JSON字符串现在如何成为序列化JSON结构的一部分。
没有@JsonRawValue注解,Jackson会将对象序列化为以下JSON:
1 | {"personId":0,"address":"{ \"street\" : \"Wall Street\", \"no\":1}"} |
请注意,address属性的值现在如何用引号引起来,并且值内的所有引号均被转义。
6、@JsonValue
Jackson注解@JsonValue告诉Jackson,Jackson不应该尝试序列化对象本身,而应在对象上调用将对象序列化为JSON字符串的方法。 请注意,Jackson将在自定义序列化返回的String内转义任何引号,因此不能返回例如 完整的JSON对象。 为此,应该改用@JsonRawValue(请参阅上一节)。
@JsonValue注解已添加到Jackson调用的方法中,以将对象序列化为JSON字符串。 这是显示如何使用@JsonValue注解的示例:
1 | public class PersonValue { |
要求Jackson序列化PersonValue对象所得到的输出是:
1 | "0,null" |
引号由Jackson添加。 请记住,对象返回的值字符串中的所有引号均会转义。
7、@JsonSerialize
@JsonSerialize Jackson注解用于为Java对象中的字段指定自定义序列化程序。 这是一个使用@JsonSerialize注解的Java类示例:
1 | public class PersonSerializer { |
注意启用字段上方的@JsonSerialize注解。
OptimizedBooleanSerializer将序列的真值序列化为1,将假值序列化为0。这是代码:
1 | public class OptimizedBooleanSerializer extends JsonSerializer<Boolean> { |
在了解 Jackson 的基本用法后,下面详细地介绍它的一些高阶应用。
2、Jackson 的 高阶应用
2.1、格式处理(含日期格式)
不同类型的日期类型,Jackson 的处理方式也不同。
- 对于日期类型为 java.util.Calendar,java.util.GregorianCalendar,java.sql.Date,java.util.Date,java.sql.Timestamp,若不指定格式, 在 json 文件中将序列化 为 long 类型的数据。显然这种默认格式,可读性差,转换格式是必要的。Jackson 有 很多方式转换日期格式。
- 注解方式,请参照” Jackson 的注解的使用”的@ JsonFormat 的示例。
- ObjectMapper 方式,调用 ObjectMapper 的方法 setDateFormat,将序列化为指定格式的 string 类型的数据。
- 对于日期类型为 java.time.LocalDate,还需要添加代码 mapper.registerModule(new JavaTimeModule()),同时添加相应的依赖 jar 包
清单 4 . JSR31 0 的配置信息
1 | <dependency> |
对于 Jackson 2.5 以下版本,需要添加代码 objectMapper.registerModule(new JSR310Module ())
- 对于日期类型为 org.joda.time.DateTime,还需要添加代码 mapper.registerModule(new JodaModule()),同时添加相应的依赖 jar 包
2.2 泛型反序列化
2.2.1 List 泛 型使用示例
Jackson 对泛型反序列化也提供很好的支持。
- 对于 List 类型 ,可以调用 constructCollectionType 方法来序列化,也可以构造 TypeReference 来序列化。
1 | // 方式1 |
- 对于 map 类型, 与 List 的实现方式相似。
2.2.2 . Map 泛型使用示例
1 | //第二参数是 map 的 key 的类型,第三参数是 map 的 value 的类型 |
Array 和 Collection 的处理与 List,Map 相似,这里不再详述。
2.2.3 自定义(反)序列化
2.2.3.1 自定义的序列化类
当 Jackson 默认序列化和反序列化的类不能满足实际需要,可以自定义新的序列化和反序列化的类。
- 自定义序列化类。自定义的序列化类需要直接或间接继承 StdSerializer 或 JsonSerializer,同时需要利用 JsonGenerator 生成 json,重写方法 serialize,示例如下:
1 | public class CustomSerializer extends StdSerializer<Person> { |
2.2.3.2 自定义反序列化类
JsonGenerator 有多种 write 方法以支持生成复杂的类型的 json,比如 writeArray,writeTree 等 。若想单独创建 JsonGenerator,可以通过 JsonFactory() 的 createGenerator。
- 自定义反序列化类。自定义的反序列化类需要直接或间接继承 StdDeserializer 或 StdDeserializer,同时需要利用 JsonParser 读取 json,重写方法 deserialize,示例如下:
1 | class CustomDeserializer extends StdDeserializer<Person> { |
2.2.3.3 注 册 Module
JsonParser 提供很多方法来读取 json 信息, 如 isClosed(), nextToken(), getValueAsString()等。若想单独创建 JsonParser,可以通过 JsonFactory() 的 createParser。
- 定义好自定义序列化类和自定义反序列化类,若想在程序中调用它们,还需要注册到 ObjectMapper 的 Module,示例如下:
1 | SimpleModule module = new SimpleModule("myModule"); |
2.3 属性可视化
是 java 对象的所有的属性都被序列化和反序列化,换言之,不是所有属性都可视化,默认的属性可视化的规则如下:
- 若该属性修饰符是 public,该属性可序列化和反序列化。
- 若属性的修饰符不是 public,但是它的 getter 方法和 setter 方法是 public,该属性可序列化和反序列化。因为 getter 方法用于序列化, 而 setter 方法用于反序列化。
- 若属性只有 public 的 setter 方法,而无 public 的 getter 方 法,该属性只能用于反序列化。
若想更改默认的属性可视化的规则,需要调用 ObjectMapper 的方法 setVisibility。
下面的示例使修饰符为 protected 的属性 name 也可以序列化和反序列化。
2.3.1 属性可视化示例
1 | //设置 addMixIn |
2.3.2 属性过滤
在将 Java 对象序列化为 json 时 ,有些属性需要过滤掉,不显示在 json 中 , Jackson 有多种实现方法。
- 注解方式, 可以用 @JsonIgnore 过滤单个属性或用 @JsonIgnoreProperties 过滤多个属性,示例如下:
属性过滤示例一
1 |
|
- addMixIn 方法加注解方式@JsonIgnoreProperties。
addMixIn 方法签名如下:
public ObjectMapper addMixIn(Class> target, Class> mixinSource);
addMixIn 方法的作用是用 mixinSource 接口或类的注解会重写 target 或 target 的子类型的注解。 用ixIn 设置
Person peixIn 的 @JsonIgnoreProperties(“name”)所重写,最终忽略的属性为 name,最终生成的 json 如下:
{“birthDate”:”2017/09/13”,”age”:40}
- SimpleBeanPropertyFilter 方式。这种方式比前两种方式更加灵活,也更复杂一些。
首先需要设置@JsonFilter 类或接口,其次设置 addMixIn,将@JsonFilter 作用于 java 对象上,最后调用 SimpleBeanPropertyFilter 的 serializeAllExcept 方法或重写 S impleBeanPropertyFilter 的 serializeAsField 方法来过滤相关属性。示例如下:
属性过滤示例三
1 | //设置 Filter 类或接口 |
2.4 树模型处理
Jackson 也提供了树模型(tree model)来生成和解析 json。若想修改或访问 json 部分属性,树模型是不错的选择。树模型由 JsonNode 节点组成。程序中常常使用 ObjectNode,ObjectNode 继承于 JsonNode,示例如下:
ObjectNode 生成和解析 json 示例
1 | ObjectMapper mapper = new ObjectMapper(); |
2.5 springMVC配置ObjectMapper
这样就可以在我们接收和返回参数的时候SpringMVC自动帮我们序列化。注意这里仅仅是json传输的时候有用。如果是表单需要重新配置相应的Formatter。
1 |
|
二、参考
1 | https://juejin.im/post/5ec940ace51d45784c52d607 |
- 本文作者: 初心
- 本文链接: http://funzzz.fun/2021/02/07/jackson---1简单使用/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!