目录
- JSON解析
- 1、概述
- 2、fastjson2
- 2.1、Maven依赖
- 2.2、API
- 1)、常用API
- 2)、序列化API
- 3)、JSON字符串反序列化API
- 2.3、简单使用
- 1)、JSON->JSONObject
- 2)、JSON->JSONArray
- 3)、JSON->Java Bean
- 4)、Java Bean->JSON
- 5)、JSONObject->Java Bean
- 6)、Java Bean->JSONObject
- 7)、JSONObject/JSONArray
- 8)、嵌套对象
- 2.4、进阶使用
- 1)、JSONB
- 2)、JSONPath
- 2.5、注解
- 1、JSONField
- 定制序列化和反序列化时的属性名
- 配置字段输出和反序列化的格式
- 序列化/反序列化时忽略字段
- 配置字段的序列化输出的的顺序
- 配置序列化Features
- 通过JSONField(value = true)配置JavaBean序列化字段和反序列化构造方式
- 通过JSONField(value = true)配置Enum基于其中一个字段序列化和反序列化
- 2)、JSONType
- 配置序列化和反序列化时忽略某些字段
- 配置序列化时保持原生类字段顺序
- 配置序列化时的JSONReader/JSONWriter的Features
- 配置序列化时字段顺序
- 2.6、Features
- 1)、JSONReader.Feature
- 2)、JSONWriter.Feature
- 3、gson
- 3.1、Maven依赖
- 3.2、基本用法
- 1)、创建Gson实例
- new Gson()
- GsonBuilder.build()
- 2)、Java对象->JSON
- 3)、JSON->Java对象
- 4)、美化输出
- 5)、JSONArray -> Java Array/List
- 6)、Null值处理
- 3.3、注解
- 1)、@Since
- 2)、@SerializedName
- 3)、@Expose
- 3.4、排除字段
- 3.5、GsonBuilder
- 1)、setPrettyPrinting
- 2)、setFieldNamingPolicy
- 3)、serializeNulls
- 4)、setExclusionStrategies
- 5)、setLenient
- 3.6、JsonReader
- 1)、概述
- 2)、Tokens
- 3)、创建JsonReader
- 4)、读取JSON流
- 3.7、JsonParser
- 1)、创建JsonParser
- 2)、转化JSON
- 3)、JsonElement, JsonObject 和JsonArray
- 4)、JsonParser 示例
- 5)、使用fromJson() 获取JsonObject
- 6)、迭代JSON树结构
- 3.8、自定义序列化
- 1)、JsonSerializer接口
- 2)、自定义序列化示例
- 3.9、自定义反序列化
- 1)、JsonDeserializer接口
- 2)、自定义反序列化示例
- 4、jackson
- 4.1、Maven依赖
- 4.2、简单使用
- 1)、ObjectMapper
- 2)、Java Bean->JSON
- 3)、JSON->Java Bean
- 4)、JSON->List/Map
- 5)、字段忽略
- 6)、日期格式化
- Date类型
- LocalDateTime类型
- 4.3、注解
- 1)、序列化注解
- @JsonAnyGetter
- @JsonGetter
- @JsonPropertyOrder
- @JsonRawValue
- @JsonValue
- @JsonRootName
- @JsonSerialize
- 2)、反序列化注解
- @JsonCreator
- @JacksonInject
- @JsonAnySetter
- @JsonSetter
- @JsonDeserialize
- @JsonAlias
- 3)、属性注解
- @JsonIgnoreProperties
- @JsonIgnore
- @JsonIgnoreType
- @JsonInclude
- @JsonAutoDetect
- 4)、常用注解
- @JsonProperty
- @JsonFormat
- @JsonUnwrapped
- @JsonView
- @JsonManagedReference、@JsonBackReference
- @JsonIdentityInfo
- @JsonFilter
- 5)、其它注解
- @JacksonAnnotationsInside
- 4.4、配置属性
JSON解析
1、概述
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,相比xml数据交换格式来说,因为解析xml比较的复杂且需要编写大段的代码,所以客户端和服务器的数据交换格式往往通过JSON来进行交换。
JSON简单说就是javascript中的对象和数组,所以这两种结构就是对象和数组两种结构,通过这两种结构可以表示各种复杂的结构。
常用JSON解析库:
- fastjson:阿里
- gson:谷歌
- jackson:SpringMVC自带
2、fastjson2
2.1、Maven依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.15</version>
</dependency>
2.2、API
1)、常用API
//把JSON文本parse为JSONObject或者JSONArray
public static final Object parse(String text);
//把JSON文本parse成JSONObject
public static final JSONObject parseObject(String text);
//把JSON文本parse为JavaBean
public static final <T> T parseObject(String text, Class<T> clazz);
//把JSON文本parse成JSONArray
public static final JSONArray parseArray(String text);
//把JSON文本parse成JavaBean集合
public static final <T> List<T> parseArray(String text, Class<T> clazz);
//将JavaBean序列化为JSON文本
public static final String toJSONString(Object object);
//将JavaBean序列化为带格式的JSON文本
public static final String toJSONString(Object object, boolean prettyFormat);
//将JavaBean转换为JSONObject或者JSONArray。
public static final Object toJSON(Object javaObject);
2)、序列化API
package com.alibaba.fastjson;
public abstract class JSON {
// 将Java对象序列化为JSON字符串,支持各种各种Java基本类型和JavaBean
public static String toJSONString(Object object, SerializerFeature... features);
// 将Java对象序列化为JSON字符串,返回JSON字符串的utf-8 bytes
public static byte[] toJSONBytes(Object object, SerializerFeature... features);
// 将Java对象序列化为JSON字符串,写入到Writer中
public static void writeJSONString(Writer writer,
Object object,
SerializerFeature... features);
// 将Java对象序列化为JSON字符串,按UTF-8编码写入到OutputStream中
public static final int writeJSONString(OutputStream os,
Object object,
SerializerFeature... features);
}
3)、JSON字符串反序列化API
package com.alibaba.fastjson;
public abstract class JSON {
// 将JSON字符串反序列化为JavaBean
public static <T> T parseObject(String jsonStr,
Class<T> clazz,
Feature... features);
// 将JSON字符串反序列化为JavaBean
public static <T> T parseObject(byte[] jsonBytes, // UTF-8格式的JSON字符串
Class<T> clazz,
Feature... features);
// 将JSON字符串反序列化为泛型类型的JavaBean
public static <T> T parseObject(String text,
TypeReference<T> type,
Feature... features);
// 将JSON字符串反序列为JSONObject
public static JSONObject parseObject(String text);
}
2.3、简单使用
1)、JSON->JSONObject
@Test
public void jsonToJsonObject() {
String jsonStr = "{\"id\": 1, \"name\": \"tom\", \"age\": 18}";
byte[] jsonBytes = jsonStr.getBytes();
//将JSON解析为JSONObject,支持各种形式的JSON
JSONObject jsonObject1 = JSON.parseObject(jsonStr);
JSONObject jsonObject2 = JSON.parseObject(jsonBytes);
System.out.println(jsonObject1.get("id"));//1
System.out.println(jsonObject2.get("name"));//tom
}
2)、JSON->JSONArray
@Test
public void jsonToJsonArray() {
//language=JSON
String jsonStr = "[\"one\", \"two\", \"three\"]";
byte[] jsonBytes = jsonStr.getBytes();
//将JSON解析为JSONArray,支持各种形式的JSON
JSONArray jsonArray1 = JSON.parseArray(jsonStr);
JSONArray jsonArray2 = JSON.parseArray(jsonBytes);
System.out.println(jsonArray1.get(1));//two
System.out.println(jsonArray2.get(2));//three
}
3)、JSON->Java Bean
@Data
@NoArgsConstructor
@AllArgsConstructor
class Book {
private Long id;
private String name;
private String author;
private Double price;
}
@Test
public void jsonToJavaBean() {
String jsonStr = "{\"id\": 1, \"name\": \"三国演义\", \"author\": \"罗贯中\", \"price\": 24.6}";
//将JSON解析为Java Bean
Book book = JSON.parseObject(jsonStr, Book.class);
//DemoTest.Book(id=1, name=三国演义, author=罗贯中, price=24.6)
System.out.println(book);
}
4)、Java Bean->JSON
@Test
public void javaBeanToJson() {
Book book = new Book(10l, "水浒传", "施耐庵", 33.33);
//将JavaBean转换为Json
String jsonStr = JSON.toJSONString(book);
byte[] jsonBytes = JSON.toJSONBytes(book);
//{"author":"施耐庵","id":10,"name":"水浒传","price":33.33}
System.out.println(jsonStr);
//{"author":"施耐庵","id":10,"name":"水浒传","price":33.33}
System.out.println(new String(jsonBytes));
}
5)、JSONObject->Java Bean
class Book {
private Long id;
private String name;
private String author;
private Double price;
public Book() {
}
public Book(Long id, String name, String author, Double price) {
this.id = id;
this.name = name;
this.author = author;
this.price = price;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
}
@Test
public void jsonObjectToJavaBean() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", 10L);
jsonObject.put("name", "水浒传");
jsonObject.put("author", "施耐庵");
jsonObject.put("price", 33.33);
//方式一:使用JSON.to(),实体对象必须要有无参构造和get/set方法
Book book1 = JSON.to(Book.class, jsonObject);
//方式二:使用JsonObject.to()
Book book2 = jsonObject.to(Book.class);
//方式三:JsonObject.getObject(),如果指定的key不存在,返回null
Book book3 = jsonObject.getObject("book", Book.class);
//方式四:JSONObject->JSON->JavaBean
Book book4 = JSON.parseObject(jsonObject.toJSONString(), Book.class);
System.out.println(book1);//Book{id=10, name='水浒传', author='施耐庵', price=33.33}
System.out.println(book2);//Book{id=10, name='水浒传', author='施耐庵', price=33.33}
System.out.println(book3);//null
System.out.println(book4);//Book{id=10, name='水浒传', author='施耐庵', price=33.33}
}
6)、Java Bean->JSONObject
@Test
public void javaBeanToJsonObject() {
Book book = new Book();
book.setId(1L);
book.setName("水浒传");
book.setAuthor("施耐庵");
book.setPrice(33.33);
JSONObject jsonObject = (JSONObject) JSON.toJSON(book);
System.out.println(jsonObject);//{"author":"施耐庵","id":1,"name":"水浒传","price":33.33}
}
7)、JSONObject/JSONArray
@Test
public void jsonObjectTest() {
String jsonStr = "{\n" +
" \"name\": \"tom\",\n" +
" \"age\": 18,\n" +
" \"address\": {\n" +
" \"city\": \"BeiJing\",\n" +
" \"district\": \"ChaoYang\"\n" +
" },\n" +
" \"scores\": [\n" +
" 88.8,\n" +
" 77.7,\n" +
" 99.9\n" +
" ]\n" +
"}\n" +
"\n";
JSONObject jsonObject = JSON.parseObject(jsonStr);
//读取简单属性
String name = jsonObject.getString("name");
Integer age = jsonObject.getInteger("age");
System.out.println(age);//18
System.out.println(name);//tom
//读取数组
JSONArray jsonArray = jsonObject.getJSONArray("scores");
System.out.println(jsonArray);//[88.8, 77.7, 99.9]
//读取数据元素
Double score = jsonArray.getDouble(0);
System.out.println(score);//88.8
//读取JavaBean
Address address = jsonObject.getObject("address", Address.class);
System.out.println(address);//Address{city='BeiJing', district='ChaoYang'}
}
8)、嵌套对象
@Test
public void test() {
User user = new User();
user.setId(1L);
user.setUsername("tom");
user.setPassword("123456");
List<String> roles = new ArrayList<>();
roles.add("admin");
roles.add("db");
user.setRoles(roles);
Map<String, String> desc = new HashMap<>();
desc.put("a", "aaa");
desc.put("b", "bbb");
desc.put("c", "ccc");
user.setDesc(desc);
//JavaBean -> JSONObject
JSONObject jsonObject = (JSONObject) JSON.toJSON(user);
//{"desc":{"a":"aaa","b":"bbb","c":"ccc"},"id":1,"password":"123456","roles":["admin","db"],"username":"tom"}
System.out.println(jsonObject);
//JavaBean -> JSON
String jsonStr = JSON.toJSONString(user);
//{"desc":{"a":"aaa","b":"bbb","c":"ccc"},"id":1,"password":"123456","roles":["admin","db"],"username":"tom"}
System.out.println(jsonStr);
//JSONObject -> JavaBean
User u1 = JSON.to(User.class, jsonObject);
//User{id=1, username='tom', password='123456', roles=[admin, db], desc={a=aaa, b=bbb, c=ccc}}
System.out.println(u1);
//JSON -> JavaBean
User u2 = JSON.parseObject(jsonStr, User.class);
//User{id=1, username='tom', password='123456', roles=[admin, db], desc={a=aaa, b=bbb, c=ccc}}
System.out.println(u2);
}
2.4、进阶使用
1)、JSONB
@Test
public void jsonbTest() {
Book book = new Book();
book.setId(1L);
book.setName("水浒传");
book.setAuthor("施耐庵");
book.setPrice(33.33);
//JavaBean -> JSONB
byte[] bookJSONB = JSONB.toBytes(book);
//JSONB -> JavaBean
Book b = JSONB.parseObject(bookJSONB, Book.class);
//Book{id=1, name='水浒传', author='施耐庵', price=33.33}
System.out.println(b);
}
2)、JSONPath
@Test
public void jsonpathTest() {
String jsonStr = "{\n" +
" \"name\": \"tom\",\n" +
" \"age\": 18,\n" +
" \"address\": {\n" +
" \"city\": \"BeiJing\",\n" +
" \"district\": \"ChaoYang\"\n" +
" },\n" +
" \"scores\": [\n" +
" 88.8,\n" +
" 77.7,\n" +
" 99.9\n" +
" ]\n" +
"}\n" +
"\n";
JSONPath path = JSONPath.of("$.address.city");
JSONReader reader = JSONReader.of(jsonStr);
String city = (String) path.extract(reader);
System.out.println(city);//BeiJing
}
2.5、注解
1、JSONField
JSONField是作用在Field、Method、Parameter上的Annotation,可以用来指定序列化字段的顺序、名字、格式、是否忽略、配置JSONReader/JSONWriter的Features等。
定制序列化和反序列化时的属性名
可以通过JSONField.name来配置序列化输出的字段名和反序列化是映射的字段名。
//方式一:配置在public field上
public class A {
@JSONField(name = "ID")
public int id;
}
//方式二:配置在public getter/setter method上
public class A {
private int id;
@JSONField(name = "ID")
public int getId() {return id;}
@JSONField(name = "ID")
public void setId(int value) {this.id = id;}
}
配置字段输出和反序列化的格式
针对Date类型的字段,经常需要用定制的日期格式进行序列化和反序列化,可以通过JSONField.format来配置自定义日期格式。
public class VO {
// 配置date序列化和反序列使用yyyyMMdd日期格式
@JSONField(format = "yyyyMMdd")
public Date date;
}
序列化/反序列化时忽略字段
可以通过JSONField.serialize配置该字段是否要序列化,通过JSONField.deserialize配置该字段是否需要反序列化。
//配置序列化时忽略特定字段
public class VO {
@JSONField(serialize = false)
public Date date;
}
//配置反序列化忽略特定字段
public class VO {
@JSONField(deserialize = false)
public Date date;
}
配置字段的序列化输出的的顺序
可以通过JSONField.ordinal来配置序列化时输出的顺序。
public static class VO {
@JSONField(ordinal = 1)
public String type;
@JSONField(ordinal = 2)
public String templateId;
}
配置序列化Features
可以通过JSONField.serializeFeatures来指定序列化的Feature。
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter.Feature;
import org.junit.jupiter.api.Test;
@Test
public void test() {
Bean bean = new Bean();
bean.id = 100;
assertEquals("{\"id\":\"100\"}", JSON.toJSONString(bean));
}
public static class Bean {
@JSONField(serializeFeatures = Feature.WriteNonStringValueAsString)
public int id;
}
通过JSONField(value = true)配置JavaBean序列化字段和反序列化构造方式
将@JSONField(value = true)配置在其中一个字段上,序列化时,会将对象序列化时按照该字段的值输出。将@JSONField(value = true)配置在构造函数参数上,而且该构造函数只有1个参数,就会按照这个参数来构造对象
public static class Bean2 {
private final int code;
public Bean2(@JSONField(value = true) int code) {
this.code = code;
}
@JSONField (value = true)
public int getCode() {
return code;
}
}
@Test
public void test2() {
Bean2 bean = new Bean2(101);
String str = JSON.toJSONString(bean);
assertEquals("101", str);
Bean2 bean1 = JSON.parseObject(str, Bean2.class);
assertEquals(bean.code, bean1.code);
}
通过JSONField(value = true)配置Enum基于其中一个字段序列化和反序列化
将@JSONField(value = true)配置在enum字段上,序列化和序列化都会基于这个字段来进行,比如:
public enum Type {
X(101, "Big"),
M(102, "Medium"),
S(103, "Small");
private final int code;
private final String name;
Type(int code, String name) {
this.code = code;
this.name = name;
}
@JSONField(value = true)
public int getCode() {
return code;
}
public String getName() {
return name;
}
}
public class Bean1 {
public Type type;
}
@Test
public void test1() {
Bean1 bean = new Bean1();
bean.type = Type.M;
String str = JSON.toJSONString(bean);
assertEquals("{\"type\":102}", str);
Bean1 bean1 = JSON.parseObject(str, Bean1.class);
assertEquals(bean.type, bean1.type);
}
2)、JSONType
JSONType是配置在类/接口上的注解,可以配置改类的所有字段的NamingStrategy、序列化和反序列化忽略的字段、JSONReader/JSONWriter的Features等。
JSONType注解支持方法 | 简介 |
---|---|
ignores | 序列化时忽略某些字段 |
alphabetic | 配置序列化时保持原生类字段顺序 |
serializeFeatures | 配置序列化时JSONWriter 的Features |
deserializeFeatures | 配置反序列化时JSONReader 的Features |
orders | 配置序列化时的字段顺序 |
naming | 配置字段名的NamingStrategy ,详细内容请参考PropertyNamingStrategy 枚举类 |
serializer | 自定义序列化行为 |
deserializer | 自定义反序列化行为 |
serializeFilters | 通过自定义列化Filters 控制序列化行为 |
配置序列化和反序列化时忽略某些字段
在下面的例子中,序列化输出只包括id1,忽略id2和id3。
@JSONType(ignores = {"id2", "id3"})
public static class Bean {
public int getId() {
return 101;
}
public int getId2() {
return 102;
}
public int getId3() {
return 103;
}
}
配置序列化时保持原生类字段顺序
从2.0.13版本开始,您可以通过@JSONType(alphabetic = false)配置序列化时保持原生类字段顺序。
@Slf4j
public class JSONTypeAlphabetic {
@JSONType(alphabetic = false)
public static class Bean {
public int f3;
public int f1;
public int f2;
public int f0;
}
@Test
public void test() {
Bean bean = new Bean();
bean.f0 = 101;
bean.f1 = 102;
bean.f2 = 103;
bean.f3 = 104;
log.info(JSON.toJSONString(bean));
//{"f3":104,"f1":102,"f2":103,"f0":101}
}
}
配置序列化时的JSONReader/JSONWriter的Features
您可以通过@JSONType(serializeFeatures= …)或@JSONType(deserializeFeatures = …)注解配置序列化和反序列时JSONWriter/JSONReader的Features。
@Slf4j
public class JSONTypeFeatures {
// 反序列化时对字符串进行trim
// 序列化时输出为null的字段
@JSONType(deserializeFeatures = JSONReader.Feature.TrimString, serializeFeatures = JSONWriter.Feature.WriteNulls)
public static class OrdersBean {
public String filed1;
public String filed2;
}
@Test
public void test() {
OrdersBean bean = new OrdersBean();
bean.filed1 = "fastjson2";
log.info(JSON.toJSONString(bean));
//{"filed1":"fastjson2","filed2":null}
String json="{\"filed1\":\" fastjson2 \",\"filed2\":\"2\"}";
OrdersBean bean2 = JSON.parseObject(json, OrdersBean.class);
log.info(bean2.filed1);
//fastjson2
}
}
配置序列化时字段顺序
您可以通过@JSONType(orders = {“filed1”, “filed2”})注解指定序列化时的字段顺序。
@Slf4j
public class JSONTypeOrders {
@JSONType(orders = {"filed4", "filed3", "filed2", "filed1"})
public static class OrdersBean {
public String filed1;
public String filed2;
public String filed3;
public String filed4;
}
@Test
public void test() {
OrdersBean bean = new OrdersBean();
bean.filed1 = "1";
bean.filed2 = "2";
bean.filed3 = "3";
bean.filed4 = "4";
log.info(JSON.toJSONString(bean));
//{"filed4":"4","filed3":"3","filed2":"2","filed1":"1"}
}
}
2.6、Features
在fastjson 2.x中,有两个Feature,分别用来配置序列化和反序列化的行为。
- JSONWriter.Feature配置序列化的行为
- JSONReader.Feature配置反序列化的行为
使用方式:
//在JSON的toJSONString方法中使用JSONWriter.Feature
Bean bean = ...;
JSON.toJSONString(bean, JSONWriter.Feature.WriteNulls); // 输出对象中值为null的字段
//在JSON的parse方法中使用JSONReader.Feature
String jsonStr = ...;
JSON.parseObject(jsonStr, JSONReader.Feature.UseBigDecimalForDoubles); // 将小数数值读取为double
1)、JSONReader.Feature
JSONReader.Feature | 介绍 |
---|---|
FieldBased | 基于字段反序列化,如果不配置,会默认基于public的field和getter方法序列化。配置后,会基于非static的field(包括private)做反序列化。在fieldbase配置下会更安全 |
IgnoreNoneSerializable | 反序列化忽略非Serializable类型的字段 |
SupportArrayToBean | 支持数据映射的方式 |
InitStringFieldAsEmpty | 初始化String字段为空字符串"" |
SupportAutoType | 支持自动类型,要读取带"@type"类型信息的JSON数据,需要显式打开SupportAutoType |
SupportSmartMatch | 默认下是camel case精确匹配,打开这个后,能够智能识别camel/upper/pascal/snake/Kebab五中case |
UseNativeObject | 默认是使用JSONObject和JSONArray,配置后会使用LinkedHashMap和ArrayList |
SupportClassForName | 支持类型为Class的字段,使用Class.forName。为了安全这个是默认关闭的 |
IgnoreSetNullValue | 忽略输入为null的字段 |
UseDefaultConstructorAsPossible | 尽可能使用缺省构造函数,在fieldBase打开这个选项没打开的时候,会可能用Unsafe.allocateInstance来实现 |
UseBigDecimalForFloats | 默认配置会使用BigDecimal来parse小数,打开后会使用Float |
UseBigDecimalForDoubles | 默认配置会使用BigDecimal来parse小数,打开后会使用Double |
ErrorOnEnumNotMatch | 默认Enum的name不匹配时会忽略,打开后不匹配会抛异常 |
TrimString | 对读取到的字符串值做trim处理 |
ErrorOnNotSupportAutoType | 遇到AutoType报错(缺省是忽略) |
DuplicateKeyValueAsArray | 重复Key的Value不是替换而是组合成数组 |
AllowUnQuotedFieldNames | 支持不带双引号的字段名 |
NonStringKeyAsString | 非String类型的Key当做String处理 |
Base64StringAsByteArray | 将byte[]序列化为Base64格式的字符串 |
2)、JSONWriter.Feature
JSONWriter.Feature | 介绍 |
---|---|
FieldBased | 基于字段序列化,如果不配置,会默认基于public的field和getter方法序列化。配置后,会基于非static的field(包括private)做序列化。 |
IgnoreNoneSerializable | 序列化忽略非Serializable类型的字段 |
BeanToArray | 将对象序列为[101,"XX"]这样的数组格式,这样的格式会更小 |
WriteNulls | 序列化输出空值字段 |
BrowserCompatible | 在大范围超过JavaScript支持的整数,输出为字符串格式 |
NullAsDefaultValue | 将空置输出为缺省值,Number类型的null都输出为0,String类型的null输出为"",数组和Collection类型的输出为[] |
WriteBooleanAsNumber | 将true输出为1,false输出为0 |
WriteNonStringValueAsString | 将非String类型的值输出为String,不包括对象和数据类型 |
WriteClassName | 序列化时输出类型信息 |
NotWriteRootClassName | 打开WriteClassName的同时,不输出根对象的类型信息 |
NotWriteHashMapArrayListClassName | 打开WriteClassName的同时,不输出类型为HashMap/ArrayList类型对象的类型信息,反序列结合UseNativeObject使用,能节省序列化结果的大小 |
NotWriteDefaultValue | 当字段的值为缺省值时,不输出,这个能节省序列化后结果的大小 |
WriteEnumsUsingName | 序列化enum使用name |
WriteEnumUsingToString | 序列化enum使用toString方法 |
IgnoreErrorGetter | 忽略setter方法的错误 |
PrettyFormat | 格式化输出 |
ReferenceDetection | 打开引用检测,这个缺省是关闭的,和fastjson 1.x不一致 |
WriteNameAsSymbol | 将字段名按照symbol输出,这个仅在JSONB下起作用 |
WriteBigDecimalAsPlain | 序列化BigDecimal使用toPlainString,避免科学计数法 |
UseSingleQuotes | 使用单引号 |
MapSortField | 对Map中的KeyValue按照Key做排序后再输出。在有些验签的场景需要使用这个Feature |
WriteNullListAsEmpty | 将List类型字段的空值序列化输出为空数组"[]" |
WriteNullStringAsEmpty | 将String类型字段的空值序列化输出为空字符串"" |
WriteNullNumberAsZero | 将Number类型字段的空值序列化输出为0 |
WriteNullBooleanAsFalse | 将Boolean类型字段的空值序列化输出为false |
NotWriteEmptyArray | 数组类型字段当length为0时不输出 |
WriteNonStringKeyAsString | 将Map中的非String类型的Key当做String类型输出 |
ErrorOnNoneSerializable | 序列化非Serializable对象时报错 |
WritePairAsJavaBean | 将 Apache Common 包中的Pair对象当做JavaBean序列化 |
BrowserSecure | 浏览器安全,将会'<' '>' '(' ')'字符做转义输出 |
WriteLongAsString | 将Long序列化为String |
WriteEnumUsingOrdinal | 序列化Enum使用Ordinal,缺省是name |
WriteThrowableClassName | 序列化Throwable时带上类型信息 |
LargeObject | 这个是一个保护措施,是为了防止序列化有循环引用对象消耗过大资源的保护措施。 |
UnquoteFieldName | 不带引号输出Key |
NotWriteSetClassName | 当打开WriteClassName时又不想输出Set的类型信息,使用这个Feature |
NotWriteNumberClassName | 当打开WriteClassName时又不想输出Number的类型信息,比如L/S/B/F/D这种后缀,使用这个Feature |
3、gson
Gson是Java序列化/反序列化库,用于将 Java 对象转换为JSON并返回。 Gson由Google创建,供内部使用,后来开源。
Gson具有三种API:
- 数据绑定API:数据绑定 API 使用属性访问器将JSON与POJO之间进行转换。 Gson使用数据类型适配器处理 JSON 数据。 它类似于XML JAXB解析器
- 树模型API:树模型 API创建JSON 文档的内存树表示。 它构建JsonElements的树。 它类似于XML DOM解析器
- 流API:流 API是一种低级 API,它使用JsonReader和JsonWriter作为离散记号读取和写入 JSON 内容。 这些类将数据读取为JsonTokens。 该 API 具有最低的开销,并且在读/写操作中速度很快。 它类似于 XML 的 Stax 解析器
3.1、Maven依赖
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.0</version>
</dependency>
3.2、基本用法
1)、创建Gson实例
创建Gson对象有两种方式:
- 使用 new Gson()
- 创建GsonBuilder实例,使用 create() 方法
new Gson()
Gson gson = new Gson();
GsonBuilder.build()
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
2)、Java对象->JSON
在Gson中的序列化即将Java对象转换为其JSON表示形式。 为了进行序列化,首先需要一个Gson对象,该对象可以处理转换。 接下来,需要调用函数toJson()方法并传入Java对象
@Test
public void toJsonTest() {
Map<Integer, String> map = new HashMap<>();
map.put(1, "a");
map.put(2, "b");
map.put(3, "c");
Gson gson = new Gson();
String str1 = gson.toJson(map);
//{"1":"a","2":"b","3":"c"}
System.out.println(str1);
Book book = new Book(1L, "水浒传", "施耐庵", 22.22);
String str2 = gson.toJson(book);
//{"id":1,"name":"水浒传","author":"施耐庵","price":22.22}
System.out.println(str2);
}
3)、JSON->Java对象
在Gson进行反序列指的是将JSON字符串转换为Java对象。 为了进行反序列化,我们需要使用Gson对象调用fromJson()函数,并在解析完成后传递两个参数,即JSON字符串和所需的Java类型。
@Test
public void fromJsonTest() {
//language=JSON
String str1 = "{\"id\": 1, \"name\": \"水浒传\", \"author\": \"施耐庵\", \"price\": 22.22}";
String str2 = "{\"1\": \"a\", \"2\": \"b\", \"3\": \"c\"}";
Gson gson = new Gson();
Book book = gson.fromJson(str1, Book.class);
//Book{id=1, name='水浒传', author='施耐庵', price=22.22}
System.out.println(book);
Map map = gson.fromJson(str2, Map.class);
//{1=a, 2=b, 3=c}
System.out.println(map);
}
4)、美化输出
默认情况下,Gson以紧凑格式打印JSON,即字段名称及其值,对象字段以及JSON输出中数组内的对象等之间将没有空格。
但是,这种紧凑的JSON可能很难阅读。因此,GSON提供了一个漂亮的打印选项,可以在其中打印JSON,以便于更加方便阅读。
@Test
public void prettyTest() {
Book book = new Book(1L, "水浒传", "施耐庵", 22.22);
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
String str = gson.toJson(book);
System.out.println(str);
// {
// "id": 1,
// "name": "水浒传",
// "author": "施耐庵",
// "price": 22.22
// }
}
5)、JSONArray -> Java Array/List
@Test
public void jsonArrayToJavaArrayOrList() {
//language=JSON
String jsonArray = "[\n" +
" {\"id\": 1, \"name\": \"tom\"},\n" +
" {\"id\": 2, \"name\": \"jerry\"},\n" +
" {\"id\": 3, \"name\": \"charles\"}\n" +
"]";
Gson gson = new Gson();
User[] users = gson.fromJson(jsonArray, User[].class);
for (User user : users) {
System.out.println(user);
}
// User{id=1, name='tom'}
// User{id=2, name='jerry'}
// User{id=3, name='charles'}
Type type = new TypeToken<ArrayList<User>>() {}.getType();
ArrayList<User> list = gson.fromJson(jsonArray, type);
for (User user : list) {
System.out.println(user);
}
// User{id=1, name='tom'}
// User{id=2, name='jerry'}
// User{id=3, name='charles'}
}
6)、Null值处理
Gson中实现的默认行为是忽略空对象字段。如果需要允许序列化Null值,则需要使用GsonBuilder进行配置。
@Test
public void nullsTest() {
Book book = new Book(1L, "水浒传", "施耐庵", null);
Gson gson = new Gson();
String str = gson.toJson(book);
//{"id":1,"name":"水浒传","author":"施耐庵"}
System.out.println(str);
//序列化null值
Gson gson2 = new GsonBuilder()
.serializeNulls()
.create();
String str2 = gson2.toJson(book);
//{"id":1,"name":"水浒传","author":"施耐庵","price":null}
System.out.println(str2);
}
3.3、注解
1)、@Since
在Gson中,可以使用@Since注释维护同一对象的多个版本。可以在类,字段以及将来的方法中使用此注释。它采用单个参数ignoreVersionsAfter。
当我们为Gson实例配置版本号“ M.N”时,所有标记有版本大于M.N的类字段都将被忽略。例如,如果我们将Gson配置为版本号“ 1.2”,则所有版本号更高的字段(例如1.3、1.4…)都将被忽略。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private Integer id;
@Since(1.0)
private String firstName;
@Since(1.1)
private String lastName;
@Since(1.2)
private String email;
}
要创建使用过@Since注解的Gson实例,需要使用GsonBuilder.setVersion()方法:
Gson gson = new GsonBuilder()
.setVersion(1.1)
.create();
让序列号以上的Employee对象序列化:
@Test
public void sinceTest1() {
Employee employee = new Employee(1, "Lokesh", "Gupta", "123@qq.com");
Gson gson = new GsonBuilder()
.setVersion(1.1)
.setPrettyPrinting()
.create();
String str = gson.toJson(employee);
System.out.println(str);
// {
// "id": 1,
// "firstName": "Lokesh",
// "lastName": "Gupta"
// }
}
让序列号以上的JSON反序列化:
@Test
public void sinceTest2() {
//language=JSON
String str = "{\"id\": 1001, \"first\": \"Lokesh\", \"lastName\": \"Gupta\", \"email\": \"123@qq.com\"}";
Gson gson = new GsonBuilder()
.setVersion(1.1)
.setPrettyPrinting()
.create();
Employee employee = gson.fromJson(str, Employee.class);
System.out.println(employee);
//Employee(id=1001, firstName=null, lastName=Gupta, email=null)
}
2)、@SerializedName
@SerializedName注解指示带注解的成员变量应使用提供的名称值作为其字段名称序列化为JSON。此注解将覆盖可能一直在使用GsonBuilder类的任何FieldNamingPolicy,包括默认的字段命名策略。
请注意,在此注解中指定的值必须是有效的JSON字段名称。
注解包含属性:
- value:序列化或反序列化时所需的字段名称
- alternate:反序列化时字段的名称
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private Integer id;
private String firstName;
private String lastName;
@SerializedName(value = "emailId", alternate = "emailAddress")
private String email;
}
@Test
public void serializedNameTest() {
Employee employee = new Employee(1, "Lokesh", "Gupta", "123@qq.com");
Gson gson = new Gson();
//序列化
String str = gson.toJson(employee);
System.out.println(str);
//{"id":1,"firstName":"Lokesh","lastName":"Gupta","emailId":"123@qq.com"}
String jsonStr = "{\"id\": 1001, \"firstName\": \"Lokesh\", \"lastName\": \"Gupta\", \"email\": \"123@qq.com\"}";
//反序列化
Employee e = gson.fromJson(jsonStr, Employee.class);
System.out.println(e);
//Employee(id=1001, firstName=Lokesh, lastName=Gupta, email=null)
}
3)、@Expose
@Expose注解可用于标记对象序列化或反序列化时是否公开(包括活不包括)的字段。
注解包含属性:
- serialize:是否序列化该字段,默认true
- deserialize:是否反序列化该字段,默认true
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private Integer id;
@Expose(serialize = true, deserialize = false)
private String firstName;
@Expose(serialize = false, deserialize = true)
private String lastName;
private String email;
}
@Test
public void exposeTest() {
Employee employee = new Employee(1, "Lokesh", "Gupta", "123@qq.com");
//需要使用GsonBuilder.excludeFieldsWithoutExposeAnnotation创建Gson实例
//才可以使@Expose注解生效
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
//序列化
String str = gson.toJson(employee);
System.out.println(str);//{"firstName":"Lokesh"}
String jsonStr = "{\"id\": 1001, \"firstName\": \"Lokesh\", \"lastName\": \"Gupta\", \"email\": \"123@qq.com\"}";
//反序列化
Employee e = gson.fromJson(jsonStr, Employee.class);
System.out.println(e);//Employee(id=null, firstName=null, lastName=Gupta, email=null)
}
3.4、排除字段
除了上述@Expose注解,还可以使用transient
修饰符。效果等同于serialize=false, deserialize=false
。
还可以使用GsonBuilder的excludeFieldsWithModifiers()方法,我们可以排除具有某些公共修饰符的字段。
例如,我们要排除一个类的所有静态成员,我们可以这样创建Gson对象:
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC)
.create();
可以使用任意数量的Modifier常量来“ excludeFieldsWithModifiers”方法。例如:
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC,
Modifier.TRANSIENT,
Modifier.VOLATILE)
.create();
最后,如果以上任何一种技术都不适合我们,那么我们可以创建自己的策略。
ExclusionStrategy
用于确定是否应将字段或顶级类作为JSON输出/输入的一部分进行序列化或反序列化。
- 对于序列化,如果shouldSkipClass(Class)或shouldSkipField(fieldAttributes)方法返回true,则该类或字段类型将不属于JSON输出。
- 对于反序列化,如果shouldSkipClass(Class)或shouldSkipField(fieldAttributes)方法返回true,则不会将其设置为Java对象结构的一部分。
例如,在ExclusionStrategy定义下面,将排除所有使用@Hidden注释注释的字段:
//public @interface Hidden {
// some implementation here
//}
// Excludes any field (or class) that is tagged with an "@Hidden"
public class HiddenAnnotationExclusionStrategy implements ExclusionStrategy
{
public boolean shouldSkipClass(Class<?> clazz) {
return clazz.getAnnotation(Hidden.class) != null;
}
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(Hidden.class) != null;
}
}
要使用此排除策略,在GsonBuilder对象中进行设置:
GsonBuilder builder = new GsonBuilder();
builder.setExclusionStrategies( new HiddenAnnotationExclusionStrategy() );
Gson gson = builder.create();
3.5、GsonBuilder
自定义Gson的行为,则可以使用GsonBuilder自定义的配置来创建新的Gson实例。
1)、setPrettyPrinting
默认情况下,Gson将创建紧凑的JSON字符串。这对于减少通过网络传输的数据量非常有用。
但是,这种紧凑的JSON对开发人员进行开发/调试应用程序时不友好。使用漂亮的打印来格式化JSON输出:
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
2)、setFieldNamingPolicy
FieldNamingPolicy
枚举在序列化期间为JSON字段名称提供了几种标准命名约定。
它有助于Gson实例将Java字段名称正确转换为所需的JSON字段名称。
注意:以下任何命名约定均不会影响以@SerializedName注释的字段。我们将验证使用User类的每个策略生成的名称。
public class User
{
private int id;
private String first_Name;
private String lastName;
private String _email;
}
User user = new User(1, "Lokesh", "Gupta", "admin@howtodoinjava.com");
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
.setPrettyPrinting().create();
System.out.println(gson.toJson(user));
- FieldNamingPolicy.IDENTITY:使用此命名策略字段名称不变。这个是默认的策略
- FieldNamingPolicy.LOWER_CASE_WITH_DASHES:Gson会将Java字段名称从其驼峰大小写形式修改为小写的字段名称,其中每个单词都用破折号(
-
)分隔 - FieldNamingPolicy.LOWER_CASE_WITH_DOTS:Gson会将Java字段名称从其驼峰大小写形式修改为小写的字段名称,其中每个单词都用点(
.
)分隔 - FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES:Gson会将Java字段名称从其驼峰大小写形式修改为小写的字段名称,其中每个单词都用下划线(
_
)分隔 - FieldNamingPolicy.UPPER_CAMEL_CASE:Gson将确保序列化为JSON格式的Java字段名称的第一个“字母”大写
- FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES:Gson将确保在将Java字段名称的第一个“字母”序列化为JSON格式时将其大写,并且单词之间将使用
空格
分隔
3)、serializeNulls
默认情况下,Gson会在序列化过程中忽略null值。但是,有时我们想序列化具有空值的字段,以便它必须出现在JSON中。为此,可以使用serializeNulls()方法:
Employee employeeObj = new Employee(1, "Lokesh", "Gupta", null);
Gson gson = new GsonBuilder()
.serializeNulls()
.setPrettyPrinting().create();
System.out.println(gson.toJson(employeeObj));
4)、setExclusionStrategies
ExclusionStrategy
用于确定是否应将字段或顶级类作为JSON输出/输入的一部分进行序列化或反序列化。
- 对于序列化,如果shouldSkipClass(Class)方法返回true,则该类或字段类型将不会在JSON中输出。
- 对于反序列化,如果shouldSkipClass(Class)返回true,则不会将其设置为Java对象结构的一部分。
在下面的示例中,使用@NPI注解和属于Account类的实例的成员字段不会进行序列化和反序列化。
Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(NPI.class) != null;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return clazz.getAnnotation(Account.class) != null;
}
})
.setPrettyPrinting()
.create();
5)、setLenient
在反序列化期间,Gson使用了一个宽松的JsonReader类。这意味着它仅接受兼容的JSON输入。
如果JSON违反结构规则之一,它将抛出MalformedJsonException。如果我们将lenient设置为true,则它将忽视某些违规行为,并尝试读取格式不正确的JSON。
Gson gson = new GsonBuilder()
.setLenient()
.setPrettyPrinting().create();
3.6、JsonReader
使用Gson JsonReader类,该类是基于拉式的流JSON解析器。它有助于将JSON作为令牌流读取。
1)、概述
- JsonReader是流式JSON解析器,也是pull parser的示例。pull parser解析JSON令牌并将其推送到事件处理程序中。
- 它有助于读取JSON(RFC 7159)编码值作为令牌流。
- 它读取字面值(字符串,数字,布尔值和null)以及对象和数组的开始和结束定界符。
- 令牌以深度优先顺序遍历,与JSON文档中出现的顺序相同。
2)、Tokens
在流模式下,每个JSON数据都被视为一个单独的令牌。
当我们使用JsonReader对其进行处理时,每个令牌将被顺序处理。例如:
{
"name": "Lokesh"
}
在使用JsonReader进行解析时,以上JSON将生成4个令牌:
token1 = {
token2 = name
token3 = Lokesh
token4 = }
3)、创建JsonReader
我们可以使用它的简单构造函数创建一个JsonReader实例,该实例接受java.io.Reader类型的输入流。
String json = "{}";
JsonReader jsonReader = new JsonReader( new StringReader(json) );
我们可以根据JSON流的来源使用以下阅读器之一:
- BufferedReader
- LineNumberReader
- CharArrayReader
- InputStreamReader
- FileReader
- FilterReader
- PushbackReader
- PipedReader
- StringReader
4)、读取JSON流
创建包含有效JSON源的JsonReader之后,我们可以开始遍历流令牌并查看令牌值。
以下是使用JsonReader以令牌形式读取简单JSON的示例:
@Test
public void jsonReaderTest() throws IOException {
String jsonStr = "{\"id\": 1001, \"firstName\": \"Lokesh\", \"lastName\": \"Gupta\", \"email\": null}";
JsonReader jsonReader = new JsonReader(new StringReader(jsonStr));
while (jsonReader.hasNext()) {
JsonToken nextToken = jsonReader.peek();
if (JsonToken.BEGIN_OBJECT.equals(nextToken)) {
jsonReader.beginObject();
} else if (JsonToken.NAME.equals(nextToken)) {
String name = jsonReader.nextName();
System.out.println("Token KEY >>>> " + name);
} else if (JsonToken.STRING.equals(nextToken)) {
String value = jsonReader.nextString();
System.out.println("Token Value >>>> " + value);
} else if (JsonToken.NUMBER.equals(nextToken)) {
long value = jsonReader.nextLong();
System.out.println("Token Value >>>> " + value);
} else if (JsonToken.NULL.equals(nextToken)) {
jsonReader.nextNull();
System.out.println("Token Value >>>> null");
} else if (JsonToken.END_OBJECT.equals(nextToken)) {
jsonReader.endObject();
}
}
jsonReader.close();
}
Token KEY >>>> id
Token Value >>>> 1001
Token KEY >>>> firstName
Token Value >>>> Lokesh
Token KEY >>>> lastName
Token Value >>>> Gupta
Token KEY >>>> email
Token Value >>>> null
在上面的示例中:
- 如果JsonReader的hasNext()方法具有更多令牌,则返回true。
- peek()方法返回下一个JSON令牌,但不移至该令牌。
- 随后多次调用peek()将返回相同的JSON令牌。
- 可以使用JsonToken类的常量检查返回令牌的类型。
- 使用beginArray()和endArray()方法检查数组的左括号和右括号“ [”和“]”。使用beginObject()和endObject()方法检查对象的左括号和右括号“ {”和“}”。
- 令牌的密钥为JsonToken.NAME类型。使用nextName()方法获取密钥名称。
- 确定令牌的类型后,使用诸如nextLong(),nextString(),nextInt()等方法获取令牌的值。可以使用nextNull()或skipValue()使用空文字。
- 所有next …()方法都返回当前标记的值,并将内部指针移至下一个。
- 当遇到未知名称时,严格的解析器应该失败,并带有异常。宽大的解析器应调用skipValue()以递归地跳过该值的嵌套令牌,否则可能会发生冲突。
3.7、JsonParser
Gson JsonParser用于将Json数据解析为JsonElement的解析树,从而解析为JsonObject。
JsonObject可用于使用JSON字符串中的相应键来访问值。
1)、创建JsonParser
JsonParser类只有一个默认构造函数,并且不需要任何参数或配置。
JsonParser parser = new JsonParser();
2)、转化JSON
JsonParser类提供3种方法来提供JSON作为源并将其解析为JsonElements树。
- JsonElement parse(JsonReader json)使用JsonReader读取JSON作为令牌流,并从JSON流中返回下一个值作为分析树。
- JsonElement parse(java.io.Reader json)–使用指定的阅读器读取JSON并将JSON字符串解析为解析树。
- JsonElement parse(java.lang.String json)–将指定的JSON字符串解析为解析树。
如果指定的文本不是有效的JSON,则这三个方法都将抛出JsonParseException和JsonSyntaxException。
3)、JsonElement, JsonObject 和JsonArray
在JsonElement树中解析了JSON字符串后,我们就可以使用它的各种方法来访问JSON数据元素。
例如,使用一种类型检查方法找出它代表什么类型的JSON元素:
jsonElement.isJsonObject();
jsonElement.isJsonArray();
jsonElement.isJsonNull();
jsonElement.isJsonPrimitive();
我们可以使用相应的方法将JsonElement转换为JsonObject和JsonArray:
JsonObject jsonObject = jsonElement.getAsJsonObject();
JsonArray jsonArray = jsonElement.getAsJsonArray();
一旦有了JsonObject或JsonArray实例,就可以使用其get()方法从中提取字段。
4)、JsonParser 示例
使用JsonParser将JSON解析为JsonElement(和JsonObject),并使用键获取JSON值:
@Test
public void jsonParserTest() {
//language=JSON
String jsonStr = "{\"id\": 1001, \"firstName\": \"Lokesh\", \"lastName\": \"Gupta\", \"email\": \"123@qq.com\"}";
JsonElement jsonElement = JsonParser.parseString(jsonStr);
JsonObject jsonObject = jsonElement.getAsJsonObject();
System.out.println(jsonObject.get("id"));
System.out.println(jsonObject.get("firstName"));
System.out.println(jsonObject.get("lastName"));
System.out.println(jsonObject.get("email"));
}
5)、使用fromJson() 获取JsonObject
JsonObject jsonObject = new Gson().fromJson(json, JsonObject.class);
6)、迭代JSON树结构
这是一个完整的示例,展示了如何迭代从JsonReader获得的JsonElement:
@Test
public void parserTest() {
//language=JSON
String jsonStr = "{\"name\": \"tom\", \"age\": 22, \"scores\": [70, 80, 90], \"address\": {\"a\": \"BeiJing\", \"b\": \"ShangHai\"}}";
JsonElement jsonElement = JsonParser.parseString(jsonStr);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
JsonElement e1 = jsonObject.get("name");
JsonElement e2 = jsonObject.get("age");
JsonElement e3 = jsonObject.get("scores");
JsonElement e4 = jsonObject.get("address");
System.out.println(e1.getAsString());
System.out.println(e2.getAsInt());
if (e3.isJsonArray()) {
JsonArray arr = e3.getAsJsonArray();
System.out.println(arr);
}
if (e4.isJsonObject()) {
JsonObject o = e4.getAsJsonObject();
System.out.println(o.get("a"));
System.out.println(o.get("b"));
}
}
}
3.8、自定义序列化
1)、JsonSerializer接口
public interface JsonSerializer<T>
{
public JsonElement serialize(T value, Type type,
JsonSerializationContext jsonSerializationContext) {
}
}
为Json创建自定义序列化程序后,我们还需要通过GsonBuilder.registerTypeAdapter(Type,Object)注册该序列化程序。
当Gson遇到指定类型的字段时,它会在序列化期间调用其回调方法serialize()。
2)、自定义序列化示例
假设我们遇到一种情况,我们必须将Java对象序列化为json,这样所有布尔值都应写为1或0,而不是打印true或false。
public class BooleanSerializer implements JsonSerializer<Boolean> {
@Override
public JsonElement serialize(Boolean src, Type typeOfSrc, JsonSerializationContext context) {
if (src) {
return new JsonPrimitive(1);
}
return new JsonPrimitive(0);
}
public static void main(String[] args) {
Map<String, Boolean> map = new HashMap<>();
map.put("a", true);
map.put("b", false);
map.put("c", true);
Gson gson = new GsonBuilder()
.registerTypeAdapter(Boolean.class, new BooleanSerializer())
.setPrettyPrinting()
.create();
String json = gson.toJson(map);
System.out.println(json);
// {
// "a": 1,
// "b": 0,
// "c": 1
// }
}
}
3.9、自定义反序列化
1)、JsonDeserializer接口
定制反序列化器必须实现JsonDeserializer接口。JsonDeserializer接口如下所示:
public interface JsonDeserializer<T>
{
public Boolean deserialize(JsonElement jsonElement,
Type type, JsonDeserializationContext jsonDeserializationContext)
throws JsonParseException;
}
为Json创建自定义反序列化器之后,我们还需要通过GsonBuilder.registerTypeAdapter(Type,Object)注册此反序列化器。
当Gson遇到指定类型的字段时,它会在序列化期间调用其回调方法deserialize()。
2)、自定义反序列化示例
假设某些服务将日期字段分别分为天,月和年等部分分别返回给我们。在JSON字符串中,它们可能有意义,但是在Java中,它们只有作为单个java.time.LocalDate对象的一部分时才有意义。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private LocalDate birthday;
}
public class UserDeserializer implements JsonDeserializer<User> {
@Override
public User deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
LocalDate localDate = LocalDate.of(
jsonObject.get("year").getAsInt(),
jsonObject.get("month").getAsInt(),
jsonObject.get("day").getAsInt()
);
return new User(
jsonObject.get("id").getAsLong(),
jsonObject.get("name").getAsString(),
localDate);
}
public static void main(String[] args) {
//language=JSON
String jsonStr = "{\n" +
" \"id\": 1001,\n" +
" \"name\": \"tom\",\n" +
" \"year\": 2002,\n" +
" \"month\": 11,\n" +
" \"day\": 24\n" +
"}";
Gson gson = new GsonBuilder()
.registerTypeAdapter(User.class, new UserDeserializer())
.create();
User user = gson.fromJson(jsonStr, User.class);
System.out.println(user);
//User(id=1001, name=tom, birthday=2002-11-24)
}
}
4、jackson
Jackson有三个核包,分别是 Streaming、Databid、Annotations,通过这些包可以方便的对 JSON 进行操作:
- Streaming (opens new window)在 jackson-core 模块。 定义了一些流处理相关的 API 以及特定的 JSON 实现
- Annotations (opens new window)在 jackson-annotations 模块,包含了 Jackson 中的注解
- Databind (opens new window)在 jackson-databind 模块, 在 Streaming 包的基础上实现了数据绑定,依赖于 Streaming 和 Annotations 包
4.1、Maven依赖
jackson-databind依赖 jackson-core 和 jackson-annotations,所以可以只显示地添加jackson-databind依赖。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.4</version>
</dependency>
4.2、简单使用
1)、ObjectMapper
ObjectMapper是Jackson 库中最常用的一个类,使用它可以进行 Java 对象和JSON字符串之间快速转换。
- readValue:进行JSON 的反序列化操作,比如可以将字符串、文件流、字节流、字节数组等将常见的内容转换成 Java 对象。
- writeValue:进行JSON 的序列化操作,可以将 Java 对象转换成 JSON 字符串。
ObjectMapper的工作原理是通过 Java Bean 对象的 Get/Set 方法进行转换时映射的,所以Java对象的 Get/Set方法尤为重要。
2)、Java Bean->JSON
@Test
public void javaBeanToJson() throws JsonProcessingException {
Book book = new Book(1L, "水浒传", "施耐庵", 22.22);
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(book);
//{"id":1,"name":"水浒传","author":"施耐庵","price":22.22}
System.out.println(jsonStr);
}
3)、JSON->Java Bean
@Test
public void jsonToJavaBean() throws JsonProcessingException {
//language=JSON
String jsonStr = "{\"id\": 1, \"name\": \"水浒传\", \"author\": \"施耐庵\", \"price\": 22.22}";
ObjectMapper objectMapper = new ObjectMapper();
Book book = objectMapper.readValue(jsonStr, Book.class);
//Book{id=1, name='水浒传', author='施耐庵', price=22.22}
System.out.println(book);
}
4)、JSON->List/Map
@Test
public void jsonToListOrMap() throws JsonProcessingException {
String jsonList = "[1, 2, 3, 4, 5]";
String jsonMap = "{\"a\": 1, \"b\": 2, \"c\": 3, \"d\": 4}";
ObjectMapper objectMapper = new ObjectMapper();
List<Integer> list = objectMapper.readValue(jsonList, new TypeReference<List<Integer>>() {});
Map<String, Integer> map = objectMapper.readValue(jsonMap, new TypeReference<Map<String, Integer>>() {});
System.out.println(list);
//[1, 2, 3, 4, 5]
System.out.println(map);
//{a=1, b=2, c=3, d=4}
}
5)、字段忽略
如果在进行JSON 转 Java对象时,JSON中出现 Java 类中不存在的属性,那么在转换时会遇到com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException
异常。
使用objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false
)配置可以忽略不存在的属性。
@Test
public void ignoreField() throws JsonProcessingException {
String jsonStr = "{\"id\": 1, \"name\": \"水浒传\", \"author\": \"施耐庵\", \"price\": 22.22, \"xxx\": \"XXX\"}";
ObjectMapper objectMapper = new ObjectMapper();
//配置字段忽略
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Book book = objectMapper.readValue(jsonStr, Book.class);
//Book{id=1, name='水浒传', author='施耐庵', price=22.22}
System.out.println(book);
}
6)、日期格式化
开发中通常使用java.util.Date类或时间类java.time.LocalDateTime类(JDK8),两者在Jackson中的处理略有不同。
Date类型
不使用@JsonFormat注解,可以正常的进行JSON的序列化与反序列化,但是JSON 中的时间是一个时间戳格式
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
private String username;
private Integer age;
private Date createTime;
}
@Test
public void dateTest() throws JsonProcessingException {
User user = new User("tom", 20, new Date());
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(user);
//{"username":"tom","age":20,"createTime":1707214013642}
System.out.println(jsonStr);
User u = objectMapper.readValue(jsonStr, User.class);
//User(username=tom, age=20, createTime=Tue Feb 06 18:06:53 CST 2024)
System.out.println(u);
}
使用:@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
{"username":"Jack","age":20,"createTime":"2022-11-29 10:56:28"}
User(username=Jack, age=20, createTime=Tue Nov 29 10:56:28 CST 2022)
LocalDateTime类型
默认情况下进行 LocalDateTime 类的 JSON 反序列化会遇到报错。异常信息:com.fasterxml.jackson.databind.exc.InvalidDefinitionException
@Test
public void localDateTimeTest() throws JsonProcessingException {
User2 user = new User2("tom", 20, LocalDateTime.now());
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(user);
System.out.println(jsonStr);
//LocalDateTime被序列化为嵌套JSON
//{"username":"tom","age":20,"createTime":{"dayOfYear":37,"dayOfWeek":"TUESDAY","year":2024,"month":"FEBRUARY","nano":659000000,"monthValue":2,"dayOfMonth":6,"hour":20,"minute":56,"second":47,"chronology":{"id":"ISO","calendarType":"iso8601"}}}
//反序列化报错:com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
User2 u = objectMapper.readValue(jsonStr, User2.class);
System.out.println(u);
}
解决此问题,需添加响应的数据绑定支持包:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.11.4</version>
</dependency>
义 ObjectMapper 时通过 findAndRegisterModules() 方法来注册依赖。
ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();
4.3、注解
Jackson 根据它的默认方式序列化和反序列化 java 对象,若根据实际需要,灵活的调整它的默认方式,可以使用 Jackson 的注解。常用的注解及用法如下。
1)、序列化注解
@JsonAnyGetter
@JsonAnyGetter注解 可以灵活地把类型为Map的属性作为标准属性序列化到JSON字符串中,有如下特点:
- 方法为非静态方法,并且方法不带任何参数,方法名没有特殊约定(随意定义方法名称)
- 方法返回值必须是Map类型
- 在一个实体类中只能用在一个方法上
public class ExtendableBean {
private String name;
private Map<String, String> properties;
@JsonAnyGetter
public Map<String, String> getProperties() {
return properties;
}
public ExtendableBean(String name) {
this.name = name;
properties = new HashMap<String, String>();
}
public void add(String key, String value) {
properties.put(key, value);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Test
public void jsonAnyGetterTest() throws JsonProcessingException {
ExtendableBean bean = new ExtendableBean("bean");
bean.add("attr1", "val1");
bean.add("attr2", "val2");
String json = new ObjectMapper().writeValueAsString(bean);
System.out.println(json);
//{"name":"bean","attr2":"val2","attr1":"val1"}
}
@JsonGetter
@JsonGetter注解 是@JsonProperty注解的替代方法,用于将方法标记为getter方法。
下例中,getTheName
方法被指定为name
属性的getter
方法:
public class MyBean {
public int id;
private String name;
@JsonGetter("name")
public String getTheName() {
return name;
}
}
@JsonPropertyOrder
该注解可以指定实体属性序列化后的顺序。
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
public int id;
public String name;
}
//序列化后结果:{ "name":"My bean", "id":1}
//该注解有一个参数alphabetic, 如果为true, 表示按字母顺序序列化,此时输出结果:{ "id":1, "name":"My bean"}
@JsonRawValue
如果对象中某个字段的值是 JSON,在使用Jackson序列化时,输出的JSON会有问题,这时我们可以使用注解 @JsonRawValue将JSON值原样输出。
@NoArgsConstructor
@AllArgsConstructor
@Data
public class RawUser {
private String name;
@JsonRawValue
private String address;
}
@Test
public void jsonRawValue() throws JsonProcessingException {
String jsonString="{\"province\":\"广州\",\"city\":\"深圳\",\"area\":\"罗湖区\"}";
RawUser rawUser = new RawUser("Tom", jsonString);
String result = new ObjectMapper().writeValueAsString(rawUser);
System.out.println(result);
//{"name":"Tom","address":{"province":"广州","city":"深圳","area":"罗湖区"}}
}
@JsonValue
@JsonValue可以用在字段或者字段对应的get方法上,当加上@JsonValue注解时,序列化返回的不再是JSON字符串,而是字段对应的值。一个类中只能有一个@JsonValue注解。
@NoArgsConstructor
@AllArgsConstructor
@Data
public class UserJsonValue {
private String name;
private int age;
@JsonValue
public String getName() {
return this.name;
}
}
@Test
public void JsonValue() throws JsonProcessingException {
UserJsonValue user = new UserJsonValue("John", 27);
ObjectMapper mapper = new ObjectMapper();
String result = mapper.writeValueAsString(user);
System.out.println(result);//John
}
@JsonRootName
正常情况下,我们定义一个User实体,并序列生成JSON字符串,输出结果如下:
{
"id":1,
"name":"John"
}
但是,在某些情况下我们需要将JSON包装成如下形式:
{
"user":{
"id":1,
"name":"John"
}
}
针对以上问题,Jackson提供了很好的解决方案,即使用@JsonRootName注解。使用@JsonRootName注解指定要包装的root名称,并在ObjectMapper实例中启用包装。
@AllArgsConstructor
@NoArgsConstructor
@Data
@JsonRootName(value = "user")
public class UserWithRoot {
private Integer id;
private String name;
}
@Test
public void jsonRootName() throws JsonProcessingException {
UserWithRoot user = new UserWithRoot(1, "John");
ObjectMapper mapper = new ObjectMapper();
//开启包装
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
String result = mapper.writeValueAsString(user);
System.out.println(result);
//{"user":{"id":1,"name":"John"}}
}
可选参数namespace,可用于XML等数据格式。如果添加它,就为xml前缀赋予了一个与某个命名空间相关联的限定名称。
@JsonSerialize
@JsonSerialize表示在序列化时要使用自定义序列化器。
我们定义一个需要序列化的实体Person,使用自定义序列化器:
public class CustomDateSerializer extends StdSerializer<Date> {
private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public CustomDateSerializer() {
this(null);
}
public CustomDateSerializer(final Class<Date> t) {
super(t);
}
@Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(format.format(date));
}
}
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Person {
private String name;
@JsonSerialize(using = CustomDateSerializer.class)
private Date birthady;
}
@Test
public void jsonSerialize() throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String toParse = "2020-05-19 02:30:00";
Date date = df.parse(toParse);
Person person = new Person("Tom", date);
String result = new ObjectMapper().writeValueAsString(person);
System.out.println(result);
//{"name":"Tom","birthady":"2020-05-19 02:30:00"}
}
2)、反序列化注解
@JsonCreator
JSON反序列化为java对象时,@JsonCreator注解用于定义构造函数。反序列化时,默认选择类的无参构造函数创建对象,当没有无参构造函数时会报错,@JsonCreator作用就是指定反序列化时用的无参构造函数。构造方法的参数前面需要加上@JsonProperty,否则会报错。
当我们需要反序列化某些与我们需要获取的目标实体不完全匹配的JSON时,此注解非常有用。
我们需要反序列化以下JSON:
{
"id":1,
"theName":"Jack"
}
但是,目标实体中没有theName字段,只有一个name字段。而我们不想更改实体本身,Jackson针对此问题提供了很好的解决方案。我们只要使用@JsonCreator注解 构造函数并同时使用@JsonProperty注解 构造函数参数:
@Data
@NoArgsConstructor
public class UserWithCreator {
private Integer id;
private String name;
public UserWithCreator(@JsonProperty("id") Integer id, @JsonProperty("theName")String name) {
this.id = id;
this.name = name;
}
}
@Test
public void jsonCreator() throws IOException {
String json = "{\"id\":1,\"theName\":\"Jack\"}";
ObjectMapper mapper = new ObjectMapper();
UserWithCreator user = mapper.readerFor(UserWithCreator.class).readValue(json);
System.out.println(user);
//UserWithCreator(id=1, name=Jack)
}
@JacksonInject
在反序列化时,如果JSON中的字段少于目标java对象中的字段,被序列化后的对象将缺少字段设置为字段的默认值,而在某些需求中,我们希望自定义默认值,这时候我们就可以用到这个注解了,它的功能就是在反序列化的时候将没有的字段设置为我们期望的默认值。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserWIthInject {
@JacksonInject
private Integer id;
private String name;
@JacksonInject
private String address;
}
@Test
public void jsonInject() throws IOException {
String json = "{\"name\":\"Jack\"}";
InjectableValues.Std injectableValues = new InjectableValues.Std();
injectableValues.addValue(String.class,"北京");
injectableValues.addValue(Integer.class, 1);
ObjectMapper mapper = new ObjectMapper();
UserWIthInject user = mapper.reader(injectableValues).forType(UserWIthInject.class).readValue(json);
System.out.println(user);
//UserWIthInject(id=1, name=Jack, address=北京)
}
@JsonAnySetter
一般情况下对象属性名都是确定的, 但有时候我们还需要给对象设定一些扩展属性, 这些扩展属性名称暂时不好确定, 通常使用 Map来存放这些扩展属性,针对此问题Jackson也提供了很好的解决方案。
要把JSON数据中的扩展属性反序列化到类的Map中, 需要在类中增加一个K/V的setter方法, 而且这个setter方法要加上@JsonAnySetter注解。
@JsonAnySette注意事项:
- 此注解用在非静态方法上,注解的方法必须有两个参数,第一个是json字段中的key,第二个是value,方法名随意。
- 此注解也可以用在Map对象属性上面,建议用在Map对象属性上面。
- 反序列化的时候将对应不上的字段全部放到Map里面。
@Data
public class ExtendableUser {
private String name;
private Map<String, String> properties;
public ExtendableUser() {
properties = new HashMap<>();
}
public ExtendableUser(String name) {
this.name = name;
properties = new HashMap<>();
}
@JsonAnySetter
public void add(String key, String value) {
properties.put(key, value);
}
}
@Test
public void jsonAnySetter() throws IOException {
String json = "{\"name\":\"Jack\",\"province\":\"四川\",\"city\":\"成都\"}";
ObjectMapper mapper = new ObjectMapper();
ExtendableUser user = mapper.readerFor(ExtendableUser.class).readValue(json);
System.out.println(user);
//ExtendableUser(name=Jack, properties={province=四川, city=成都})
}
@JsonSetter
@JsonSetter 标注于 setter 方法上,类似 @JsonProperty ,也可以解决 json 键名称和 java 对象 字段名称不匹配的问题。
例如,下例中将方法setTheName
指定为name
属性的setter
方法:
public class UserWithGetter {
private String name;
private int age;
@JsonProperty("name")
@JsonSetter("name")
public void setTheName( String name) {
this.name = name;
}
//省略构造函数和其他方法
}
@JsonDeserialize
@JsonDeserialize注解指定使用自定义反序列化器。
public class CustomDateDeserializer extends StdDeserializer<Date> {
private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public CustomDateDeserializer() {
this(null);
}
protected CustomDateDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String date = jsonParser.getText();
try {
return formatter.parse(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
public class Person {
private String name;
@JsonDeserialize(using = CustomDateDeserializer.class)
private Date birthday;
//省略 constructor getters setters
}
@Test
public void jsonDeserialize() throws IOException {
String json = "{\"name\":\"Jack\",\"birthday\":\"2020-05-20 02:30:00\"}";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
ObjectMapper mapper = new ObjectMapper();
Person person =mapper.readerFor(Person.class).readValue(json);
System.out.println(person);
System.out.println(df.format(person.getBirthday()));
//Person{name='Jack', birthday=Wed May 20 02:30:00 CST 2020}
//2020-05-20 02:30:00
}
@JsonAlias
@JsonAlias注解定义在反序列化过程中给属性设置一个或者多个别名。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AliasUser {
@JsonAlias({"fName", "f_name"})
private String firstName;
private String lastName;
}
给AliasUser类的字段firstName,定义了两个别名fName和f_name。在反序列化的过程中,如果JSON字段中存在fName或者f_name,都会将值映射到AliasUser类的firstName字段中。
@Test
public void jsonAlias() throws IOException {
String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";
ObjectMapper mapper = new ObjectMapper();
AliasUser aliasUser = mapper.readerFor(AliasUser.class).readValue(json);
System.out.println(aliasUser);
//AliasUser(firstName=John, lastName=Green)
}
3)、属性注解
@JsonIgnoreProperties
@JsonIgnoreProperties是一个类级别的注解 ,作用是将java对象序列化为JSON时,忽略的一个或多个属性。
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties({"id"})
public class UserWithIgnore {
private Integer id;
private String name;
}
@Test
public void jsonIgnoreProperties() throws JsonProcessingException {
UserWithIgnore bean = new UserWithIgnore(1, "Jack");
String result = new ObjectMapper().writeValueAsString(bean);
System.out.println(result);
//{"name":"Jack"}
}
@JsonIgnore
@JsonIgnore是一个字段级别的注解 ,作用是在java对象序列化为JSON时,忽略此字段。
public class UserWithIgnore {
@JsonIgnore
private int id;
private String name;
//省略 constructor getters setters
}
@Test
public void jsonIgnore() throws JsonProcessingException {
UserWithIgnore user = new UserWithIgnore(1, "Jack");
String result = new ObjectMapper().writeValueAsString(user);
System.out.println(result);
//{"name":"Jack"}
}
@JsonIgnoreType
@JsonIgnoreType注解 要忽略注解 类型的所有属性。
public class UserWithIgnoreType {
public int id;
public Name name;
public UserWithIgnoreType() {
}
public UserWithIgnoreType(final int id, final Name name) {
this.id = id;
this.name = name;
}
@JsonIgnoreType
public static class Name {
public String firstName;
public String lastName;
public Name() {
}
public Name(final String firstName, final String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
}
@Test
public void jsonIgnoreType() throws JsonProcessingException {
UserWithIgnoreType.Name name = new UserWithIgnoreType.Name("John", "Doe");
UserWithIgnoreType user = new UserWithIgnoreType(1, name);
String result = new ObjectMapper().writeValueAsString(user);
System.out.println(result);//{"id":1}
}
@JsonInclude
我们可以使用@JsonInclude排除具有empty/null/默认值的属性。
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class IncludeUser {
private Integer id;
private String name;
private Integer age;
private String address;
}
@Test
public void jsonInclude() throws JsonProcessingException {
IncludeUser user = new IncludeUser();
user.setId(1);
String result = new ObjectMapper().writeValueAsString(user);
System.out.println(result);//{"id":1}
}
@JsonAutoDetect
我们定义的类中的所有属性由private修饰,不提供对应的get,set方法,如果按照默认的属性发现规则我们将无法序列化和反序列化类的字段(如果没有get,set方法,只有被public修饰的属性才会被发现)。这时,我们可以通过修改@JsonAutoDetect的fieldVisibility来调整自动发现级别,为了使字段被自动发现,我们需要将级别调整为ANY。
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class PrivateUser {
private int id;
private String name;
public PrivateUser() {
}
public PrivateUser(final int id, final String name) {
this.id = id;
this.name = name;
}
}
@Test
public void jsonAutoDetect() throws JsonProcessingException {
PrivateUser bean = new PrivateUser(1, "Jack");
String result = new ObjectMapper().writeValueAsString(bean);
System.out.println(result);//{"id":1,"name":"Jack"}
}
4)、常用注解
@JsonProperty
@JsonProperty 注解用于属性上,作用是把该属性的名称序列化为另外一个名称,例如将theName属性序列化为name,使用@JsonProperty(“name”)注解 。
@JsonProperty不仅仅是在序列化的时候有用,反序列化的时候也有用,比如有些接口返回的是json字符串,命名又不是标准的驼峰形式,在映射成对象的时候,在类的属性上加上@JsonProperty注解,里面写上返回的json串对应的名字即可。
public class UserWithGetter {
private String name;
private int age;
@JsonProperty("name")
public void setTheName( String name) {
this.name = name;
}
@JsonProperty("name")
public String getTheName() {
return name;
}
//.....
}
@Test
public void jsonProperty() throws IOException {
UserWithGetter user = new UserWithGetter("Jack", 25);
String result = new ObjectMapper().writeValueAsString(user);
System.out.println(result);
UserWithGetter resultUser= new ObjectMapper().readerFor(UserWithGetter.class)
.readValue(result);
System.out.println(resultUser);
//{"age":25,"name":"Jack"}
//UserWithGetter{name='Jack', age=25}
}
@JsonFormat
@JsonFormat注解指定序列化日期和时间的格式
public class PersonWithFormat {
private String name;
@JsonFormat(shape = JsonFormat.Shape.BINARY, pattern = "yyyy-MM-dd hh:mm:ss")
private Date birthday;
//.....
}
@Test
public void jsonFormat() throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
String toParse = "20-05-2020 02:30:00";
Date date = df.parse(toParse);
PersonWithFormat event = new PersonWithFormat("Jack", date);
String result = new ObjectMapper().writeValueAsString(event);
System.out.println(result);
}
{
"name":"Jack",
"birthday":"2020-05-20 02:30:00"
}
@JsonUnwrapped
@JsonUnwrapped定义了在序列化/反序列化时,将嵌套的对象属性,序列化到JSON的同一级。
public class UnwrappedUser {
public int id;
@JsonUnwrapped
public Name name;
public static class Name {
public String firstName;
public String lastName;
public Name() {
}
public Name( String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
}
@Test
public void jsonUnwrapped() throws JsonProcessingException {
UnwrappedUser.Name name = new UnwrappedUser.Name("John", "Doe");
UnwrappedUser user = new UnwrappedUser(1, name);
String result = new ObjectMapper().writeValueAsString(user);
System.out.println(result);
//{"id":1,"firstName":"John","lastName":"Doe"}
}
@JsonView
@JsonView注解作用是将java对象序列化为JSON时,过滤敏感字段或者不需要字段,使用步骤如下:
声明视图:
public class Views {
public static class UserInfo {
}
public static class UserDetailInfo extends UserInfo {
}
}
定义实体:
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserInfo {
@JsonView(Views.UserInfo.class)
private String username;
@JsonView(Views.UserDetailInfo.class)
private String password;
@JsonView(Views.UserInfo.class)
private Integer age;
}
测试:
@Test
public void jsonView() throws JsonProcessingException {
UserInfo item = new UserInfo("Tom", "123456", 27);
String result = new ObjectMapper().writerWithView(Views.UserInfo.class)
.writeValueAsString(item);
System.out.println(result);
//{"username":"Tom","age":27}
result = new ObjectMapper().writerWithView(Views.UserDetailInfo.class)
.writeValueAsString(item);
System.out.println(result);
//{"username":"Tom","password":"123456","age":27}
}
@JsonManagedReference、@JsonBackReference
这两个注解通常配对使用, 可以解决两个不同类的属性的父子关系和循环引用。
@JsonBackReference标注的属性在序列化时,会被忽略。而@JsonManagedReference标注的属性则会被序列化。在序列化时,@JsonBackReference的作用相当于@JsonIgnore,此时可以没有@JsonManagedReference。但在反序列化时,如果没有@JsonManagedReference,则不会自动注入@JsonBackReference标注的属性(被忽略的父或子);如果有@JsonManagedReference,则会自动注入自动注入@JsonBackReference标注的属性。
public class ItemWithRef {
public int id;
public String itemName;
@JsonManagedReference
public UserWithRef owner;
public ItemWithRef(int id, String itemName, UserWithRef user) {
this.id = id;
this.itemName = itemName;
this.owner = user;
}
}
public class UserWithRef {
public int id;
public String name;
@JsonBackReference
public List<ItemWithRef> userItems;
public UserWithRef(int id, String name) {
this.id = id;
this.name = name;
this.userItems = new ArrayList<>();
}
public void addItem(ItemWithRef item) {
this.userItems.add(item);
}
}
@Test
public void jacksonReferenceAnnotation() throws JsonProcessingException {
UserWithRef user = new UserWithRef(1, "John");
ItemWithRef item = new ItemWithRef(2, "book", user);
user.addItem(item);
String result = new ObjectMapper().writeValueAsString(item);
System.out.println(result);
//{"id":2,"itemName":"book","owner":{"id":1,"name":"John"}}
}
@JsonIdentityInfo
@JsonIdentityInfo注解,作用于类或属性上,被用来在序列化/反序列化时为该对象或字段添加一个对象识别码,通常是用来解决循环嵌套的问题。
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class ItemWithIdentity {
public int id;
public String itemName;
public UserWithIdentity owner;
}
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class UserWithIdentity {
public int id;
public String name;
public List<ItemWithIdentity> userItems;
}
@Test
public void jsonIdentityInfo() throws JsonProcessingException {
UserWithIdentity user = new UserWithIdentity(1, "John");
ItemWithIdentity item = new ItemWithIdentity(2, "book", user);
user.addItem(item);
String result = new ObjectMapper().writeValueAsString(item);
System.out.println(result);
}
{"id":2,"itemName":"book","owner":{"id":1,"name":"John","userItems":[2]}}
@JsonFilter
@JsonFilter注解的作用是在序列化的过程中过滤掉指定的属性,类似于@JsonIgnore注解。但是当有一堆字段需要配置,而且整个项目都需要统一处理的时候,@JsonFilter比较适合。
@JsonFilter("myFilter")
public class BeanWithFilter {
public int id;
public String name;
}
public void whenSerializingUsingJsonFilter_thenCorrect() throws JsonProcessingException {
BeanWithFilter bean = new BeanWithFilter(1, "My bean");
FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",
SimpleBeanPropertyFilter.filterOutAllExcept("name"));
String result = new ObjectMapper().writer(filters).writeValueAsString(bean);
//{"name":"My bean"}
}
5)、其它注解
@JacksonAnnotationsInside
自定义Jackson注解.
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "age","id" })
public @interface CustomAnnotation {
}
@CustomAnnotation
public class UserCustomAnnotation {
private int id;
private String name;
private int age;
//省略 constructor getters setters
}
@Test //测试自定义注解
public void customAnnotation() throws JsonProcessingException {
UserCustomAnnotation bean = new UserCustomAnnotation(1, "Jack", 28);
String result = new ObjectMapper().writeValueAsString(bean);
System.out.println(result);
}
//添加注解 前
{"id":1,"name":"Jack","age":28}
//添加注解 后
{"name":"Jack","age":28,"id":1}
4.4、配置属性
spring:
jackson:
# 设置属性命名策略,对应jackson下PropertyNamingStrategy中的常量值,SNAKE_CASE-返回的json驼峰式转下划线,json body下划线传到后端自动转驼峰式
property-naming-strategy: SNAKE_CASE
# 全局设置@JsonFormat的格式pattern
date-format: yyyy-MM-dd HH:mm:ss
# 当地时区
locale: zh_CN
# 设置全局时区
time-zone: GMT+8
# 常用,全局设置pojo或被@JsonInclude注解的属性的序列化方式
default-property-inclusion: NON_NULL #不为空的属性才会序列化,具体属性可看JsonInclude.Include
# 常规默认,枚举类SerializationFeature中的枚举属性为key,值为boolean设置jackson序列化特性,具体key请看SerializationFeature源码
visibility:
#属性序列化的可见范围
getter: non_private
#属性反序列化的可见范围
setter: protected_and_public
#静态工厂方法的反序列化
CREATOR: public_only
#字段
FIELD: public_only
#布尔的序列化
IS_GETTER: public_only
#所有类型(即getter setter FIELD)不受影响,无意义
NONE: public_only
#所有类型(即getter setter FIELD)都受其影响(慎用)
ALL: public_only
serialization:
#反序列化是否有根节点
WRAP_ROOT_VALUE: false
#是否使用缩进,格式化输出
INDENT_OUTPUT: false
FAIL_ON_EMPTY_BEANS: true # 对象不含任何字段时是否报错,默认true
FAIL_ON_SELF_REFERENCES: true #循环引用报错
WRAP_EXCEPTIONS: true #是否包装异常
FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS: true #JsonUnwrapped标记的类有类型信息是否报错
WRITE_SELF_REFERENCES_AS_NULL: false #循环引用返回null
CLOSE_CLOSEABLE: true #若对象实现了CLOSEABLE接口,在序列化后是否调用Close方法
FLUSH_AFTER_WRITE_VALUE: false #流对象序列化之后是否强制刷新
WRITE_DATES_AS_TIMESTAMPS: true # 返回的java.util.date转换成时间戳
WRITE_DATES_WITH_ZONE_ID: true #2011-12-03T10:15:30+01:00[Europe/Paris]带时区id
WRITE_DURATIONS_AS_TIMESTAMPS: true #将DURATIONS转换成时间戳
WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS: false #是否字符数组输出json数组 (false则输出字符串)
WRITE_ENUMS_USING_TO_STRING: false # 将枚举输出toString
WRITE_ENUMS_USING_INDEX: false #枚举下标
WRITE_ENUM_KEYS_USING_INDEX: false #枚举key类似
WRITE_NULL_MAP_VALUES: false #是否输出map中的空entry(此特性已过期,请使用JsonInclude注解)
WRITE_EMPTY_JSON_ARRAYS: true # 对象属性值是空集合是否输出空json数组
WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED: false #是否将单个元素的集合展开,(即:去除数组符号"[]")
WRITE_BIGDECIMAL_AS_PLAIN: false #是否调用BigDecimal#toPlainString()输出
WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS: #将timestamp输出为纳秒
ORDER_MAP_ENTRIES_BY_KEYS: false #map序列化后,是否用key对其排序
EAGER_SERIALIZER_FETCH: true #是否马上获取序列化器
USE_EQUALITY_FOR_OBJECT_ID: false #是否使用objectId比较是否相等(在ORM框架Hibernate中有应用)
# 枚举类DeserializationFeature中的枚举属性为key,值为boolean设置jackson反序列化特性,具体key请看DeserializationFeature源码
deserialization:
USE_BIG_DECIMAL_FOR_FLOATS: false #将浮点数反序列化为BIG_DECIMAL
USE_BIG_INTEGER_FOR_INTS: false #将整数反序列化为BIG_INTEGER
USE_LONG_FOR_INTS: false #将整型反序列化为长整
USE_JAVA_ARRAY_FOR_JSON_ARRAY: false #无明确类型时,是否将json数组反序列化为java数组(若是true,就对应Object[] ,反之就是List<?>)
FAIL_ON_UNKNOWN_PROPERTIES: false # 常用,json中含pojo不存在属性时是否失败报错,默认true
FAIL_ON_NULL_FOR_PRIMITIVES: false #将null反序列化为基本数据类型是否报错
FAIL_ON_NUMBERS_FOR_ENUMS: false #用整数反序列化为枚举是否报错
FAIL_ON_INVALID_SUBTYPE: false #找不至合适的子类否报错 (如注解JsonTypeInfo指定的子类型)
FAIL_ON_READING_DUP_TREE_KEY: false #出现重复的json字段是否报错
FAIL_ON_IGNORED_PROPERTIES: false #如果json中出现了java实体字段中已显式标记应当忽略的字段,是否报错
FAIL_ON_UNRESOLVED_OBJECT_IDS: true #如果反序列化发生了不可解析的ObjectId是否报错
FAIL_ON_MISSING_CREATOR_PROPERTIES: false #如果缺少静态工厂方法的参数是否报错(false,则使用null代替需要的参数)
FAIL_ON_NULL_CREATOR_PROPERTIES: false #将空值绑定到构造方法或静态工厂方法的参数是否报错
FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY: false #注解JsonTypeInfo.As#EXTERNAL_PROPERTY标记的属性缺失,是否报错
FAIL_ON_TRAILING_TOKENS: false #出现尾随令牌是否报错(如果是true,则调用JsonParser#nextToken,检查json的完整性)
WRAP_EXCEPTIONS: true #是否包装反序列化出现的异常
ACCEPT_SINGLE_VALUE_AS_ARRAY: true #反序列化时是否将一个对象封装成单元素数组
UNWRAP_SINGLE_VALUE_ARRAYS: false #反序列化时是否将单元素数组展开为一个对象
UNWRAP_ROOT_VALUE: false #是否将取消根节点的包装
ACCEPT_EMPTY_STRING_AS_NULL_OBJECT: false #是否将空字符("")串当作null对象
ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT: false #是否接受将空数组("[]")作为null
ACCEPT_FLOAT_AS_INT: true #是否接受将浮点数作为整数
READ_ENUMS_USING_TO_STRING: false #按照枚举toString()方法读取,(false则按枚举的name()方法读取)
READ_UNKNOWN_ENUM_VALUES_AS_NULL: false #读取到未知的枚举当作null
READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE: false #读取到未知的枚举,将其当作被JsonEnumDefaultValue注解标记的枚举
READ_DATE_TIMESTAMPS_AS_NANOSECONDS: true #将时间戳视为纳秒(false,则视为毫秒)
ADJUST_DATES_TO_CONTEXT_TIME_ZONE: true #反序列化是否会适应DeserializationContext#getTimeZone()提供的时区 (此特性仅对java8的时间/日期有效)
EAGER_DESERIALIZER_FETCH: true #是否马上获取反序列化器
# 枚举类MapperFeature中的枚举属性为key,值为boolean设置jackson ObjectMapper特性
# ObjectMapper在jackson中负责json的读写、json与pojo的互转、json tree的互转,具体特性请看MapperFeature,常规默认即可
mapper:
USE_ANNOTATIONS: true #是否使用注解自省(检查JsonProperties这些)
# 使用getter取代setter探测属性,这是针对集合类型,可以直接修改集合的属性
USE_GETTERS_AS_SETTERS: true #默认false
PROPAGATE_TRANSIENT_MARKER: false #如何处理transient字段,如果true(不能访问此属性) ,若是false则不能通过字段访问(还是可以使用getter和setter访问)
AUTO_DETECT_CREATORS: true #是否自动检测构造方法或单参且名为valueOf的静态工厂方法
AUTO_DETECT_FIELDS: true #是否自动检测字段 (若true,则将所有public实例字段视为为属性)
AUTO_DETECT_GETTERS: true #确定是否根据标准 Bean 命名约定自动检测常规“getter”方法的(不包括is getter)
AUTO_DETECT_IS_GETTERS: true #确定是否根据标准 Bean 命名约定自动检测“is getter”方法
AUTO_DETECT_SETTERS: false # 确定是否根据标准 Bean 命名约定自动检测“setter”方法
REQUIRE_SETTERS_FOR_GETTERS: false #getter方法必需要有对应的setter或字段或构造方法参数,才能视为一个属性
ALLOW_FINAL_FIELDS_AS_MUTATORS: true #是否可以修改final成员字段
INFER_PROPERTY_MUTATORS: true #是否能推断属性,(即使用字段和setter是不可见的,但getter可见即可推断属性)
INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES: true #是否自动推断ConstructorProperties注解
CAN_OVERRIDE_ACCESS_MODIFIERS: true #调用AccessibleObject#setAccessible设为true .将原来不可见的属性,变为可见
OVERRIDE_PUBLIC_ACCESS_MODIFIERS: true #对所有的属性调用AccessibleObject#setAccessible设为true .(即使用是公共的)
USE_STATIC_TYPING: false #序列化使用声明的静态类型还是动态类型 JsonSerialize#typing注解可覆盖它
USE_BASE_TYPE_AS_DEFAULT_IMPL: false # 反序列化是否使用基本类作为默实现 @JsonTypeInfo.defaultImpl
DEFAULT_VIEW_INCLUSION: true #没有JsonView注解标记的属性是否会被包含在json序列化视图中
SORT_PROPERTIES_ALPHABETICALLY: false #按字母表顺序序列化字段(若false,按字段声明的顺序)
ACCEPT_CASE_INSENSITIVE_PROPERTIES: false #反序列化属性时不区分大小写 (true时,会影响性能)
ACCEPT_CASE_INSENSITIVE_ENUMS: false #枚举反序列化不区别大小写
ACCEPT_CASE_INSENSITIVE_VALUES: false #允许解析一些枚举的基于文本的值类型但忽略反序列化值的大小写 如日期/时间类型反序列化器
USE_WRAPPER_NAME_AS_PROPERTY_NAME: false # 使用包装器名称覆盖属性名称 AnnotationIntrospector#findWrapperName指定的
USE_STD_BEAN_NAMING: false # 是否以强制与 Bean 名称自省严格兼容的功能,若开启后(getURL())变成URL (jackson默认false, url)
ALLOW_EXPLICIT_PROPERTY_RENAMING: false #是否允许JsonProperty注解覆盖PropertyNamingStrategy
ALLOW_COERCION_OF_SCALARS: true # 是否允许强制使用文本标题 ,即将字符串的"true"当作布尔的true ,字符串的"1.0"当作"double"
IGNORE_DUPLICATE_MODULE_REGISTRATIONS: true #如果模块相同(Module#getTypeId()返回值相同),只有第一次能会真正调用注册方法
IGNORE_MERGE_FOR_UNMERGEABLE: true #在合并不能合并的属性时是否忽略错误
BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES: false #阻止不安全的基类(如Object Closeable Cloneable AutoCloseable Serializable)
parser:
AUTO_CLOSE_SOURCE: true #是否自动关闭不属于解析器的底层输入流
ALLOW_COMMENTS: false #是否允许json注解(Json规范是不能加注释的,但这里可以配置)
ALLOW_YAML_COMMENTS: false #是否允许出现yaml注释
ALLOW_UNQUOTED_FIELD_NAMES: false #是否允许出现字段名不带引号
ALLOW_SINGLE_QUOTES: false # 是否允许出现单引号,默认false
ALLOW_UNQUOTED_CONTROL_CHARS: false #是否允许出现未加转义的控制字符
ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER: false #是否允许对所有字符都可加反斜杠转义
ALLOW_NUMERIC_LEADING_ZEROS: false #是否允许前导的零 000001
ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS: false #是否允许前导的小点数 如 ".04314"会被解析成"0.04314"
ALLOW_NON_NUMERIC_NUMBERS: false #是否允许NaN型的浮点数 ("INF"当作正无穷 "-INF"当作负无穷 "NaN"非数字,类型于除数为0)
ALLOW_MISSING_VALUES: false # 是否允许json数组中出现缺失值 (如["value1",,"value3",]将被反序列化为["value1", null, "value3", null])
ALLOW_TRAILING_COMMA: false # 是否允许json尾部有逗号 (如{"a": true,})
STRICT_DUPLICATE_DETECTION: false #是否启用严格的字段名重复检查(开启后会增加20-30%左右的性能开销)
IGNORE_UNDEFINED: false #属性定义未找到是否报错(这不是针对json,是针对Avro, protobuf等需要Schema的格式)
INCLUDE_SOURCE_IN_LOCATION: false #是否包含其源信息(如总字节数,总字符数 行号 列号 )
generator:
AUTO_CLOSE_TARGET: true #是否自动关闭不属于生成器的底层输出流
AUTO_CLOSE_JSON_CONTENT: true #是否自动补全json(当有不匹配的JsonToken#START_ARRAY JsonToken#START_OBJECT时)
FLUSH_PASSED_TO_STREAM: true #是否刷新generator
QUOTE_FIELD_NAMES: true #是否为字段名添加引号
QUOTE_NON_NUMERIC_NUMBERS: true #对于NaN浮点数是否加引号
ESCAPE_NON_ASCII: false #非ASCII码是否需要转义
WRITE_NUMBERS_AS_STRINGS: false #将数字当作字符串输出 (防止Javascript长度限制被截断)
WRITE_BIGDECIMAL_AS_PLAIN: false #按BigDecimal的toPlainString()输出
STRICT_DUPLICATE_DETECTION: false #是否启用严格的字段名重复检查
IGNORE_UNKNOWN: false #属性定义未找到是否报错(这不是针对json,是针对Avro, protobuf等需要Schema的格式)