对象序列化会调对象的get方法,其json的键值是get后字符串的小写
如果一个对象的private属性没有get方法,那么就不会对这个属性进行序列化,如果这个属性是public的,那么就可以没有get方法
如果一个类,没有任何属性就会报错,除非关闭SerializationFeature.FAIL_ON_EMPTY_BEANS
@AllArgsConstructor
@ToString
public class Person {
private Integer age;
public String name;
private String home;
public Integer getTemAge() {
return age;
}
}
JsonMapper jsonMapper = new JsonMapper();
Person person = new Person(1,"赵三","没有");
System.out.println(jsonMapper.writeValueAsString(person));//{"name":"赵三","temAge":1}
一、序列化接口
JsonSerializer
是Jackson定义序列化器的顶层接口(其实是抽象类),它定义了一些抽象API来实现将任意类型转换为JSON,供以ObjectMapper来使用。使用序列化器时你需要注意如下几点:
调用者必须自行处理null值,不要让它直接调用
如果你需要序列化null值,可以使用SerializerProvider#findNullValueSerializer
来找到专门的序列化器
这表明:你并不可以通过自定义序列化器的方式来自定义null值的序列化(一般也没有必要)
public abstract class JsonSerializer<T> implements JsonFormatVisitable{
/**
* 执行对象的序列化
* T value:待序列化的值,不能为null哦
* JsonGenerator gen:生成器
* SerializerProvider serializers:可用于获取序列化器的提供程序
*/
public abstract void serialize(T value, JsonGenerator gen, SerializerProvider serializers) throws IOException;
//其他方法忽略
序列化器是所有JSON库中一个重要且非常庞大的体系,庞大主要是因为它需要兼具各种数据类型,其直接子类如下
1、TypeWrappedSerializer
:它是在原JsonSerializer的基础上加上了类型信息TypeSerializer,属于一种包装模式的实现。最终调用的序列化方法统一为:_serializer.serializeWithType(value, g, provider, _typeSerializer)
2、None:这个没什么好说的,用于@JsonSerialize
注解表示没有指定序列化器
3、StdSerializer<T>
:标准序列化器(重点)。可以认为所有的序列化器子类均是它的子类,所以若你需要自定义序列化器也请继承它而不是直接继承顶层的JsonSerializer
如果我们要自定义序列化器,官方建议实现StdSerializer接口,因为它是所有标准序列化器使用的基类,也可以用于自定义序列化器(实际上,自定义的时候推荐使用这个要使用的基类,而不是自己直接去继承JsonSerializer)。
说明:StdSerializer它并不提供无参构造,当你自定义扩展的时候建议你加上无参构造,这样就也可以通过@JsonSerialize来指定了,更加灵活
1.1、数组的序列化器
他们的父类是
ArraySerializerBase
//对象数组的序列化
ObjectArraySerializer 处理对象数组 序列化方法第一个入参是:Object[] value
//字符串数组的序列化
StringArraySerializer 处理String数组 序列化方法第一个入参是:String[] value
//基本类型数组的处理
CharArraySerializer 处理char[] 序列化方法第一个入参是:char[] value
ShortArraySerializer 处理short[] 序列化方法第一个入参是:short[] value
IntArraySerializer 处理int[] 序列化方法第一个入参是:int[] value
LongArraySerializer 处理long[] 序列化方法第一个入参是:long[] value
FloatArraySerializer 处理float[] 序列化方法第一个入参是:float[] value
DoubleArraySerializer 处理double[] 序列化方法第一个入参是:double[] value
BooleanArraySerializer 处理boolean[] 序列化方法第一个入参是:boolean[] value
1.2、集合的序列化器
他们的父类是
AsArraySerializerBase
CollectionSerializer 处理Collection集合 序列化方法第一个入参是:Collection<?> value
EnumSetSerializer 处理EnumSet集合 序列化方法第一个入参是:EnumSet<? extends Enum<?>> value
IndexedListSerializer 处理List集合 序列化方法第一个入参是:List<?> value
IterableSerializer 处理Iterable集合 序列化方法第一个入参是:Iterable<?> value
IteratorSerializer 处理Iterator集合 序列化方法第一个入参是:Iterator<?> value
1.3、MapEntry的序列化器
MapEntrySerializer 处理Map.Entry 序列化方法第一个入参是:Map.Entry<?, ?> value
1.4、Map的序列化器
MapSerializer 处理Map集合 序列化方法第一个入参是:Map<?,?> value
1.5 基本类型的序列化器
他们的序列化方法都是:
public void serialize(Object value, JsonGenerator gen,SerializerProvider provider)
ShortSerializer 处理short与其包装器 底层处理方法:gen.writeNumber(((Short) value).shortValue());
IntegerSerializer 处理int与其包装器 底层处理方法:gen.writeNumber(((Integer) value).intValue());
LongSerializer 处理long与其包装器 底层处理方法:gen.writeNumber(((Long) value).longValue());
FloatSerializer 处理float与其包装器 底层处理方法:gen.writeNumber(((Float) value).floatValue());
DoubleSerializer 处理double与其包装器 底层处理方法:gen.writeNumber(((Double) value).doubleValue());
BooleanSerializer 处理boolean与其包装器 底层处理方法: gen.writeBoolean(Boolean.TRUE.equals(value));
IntLikeSerializer 处理其他的Number 底层处理方法:gen.writeNumber(((Number) value).intValue());
1.6 原子类的序列化器
AtomicBooleanSerializer 序列化方法第一个入参是:AtomicBoolean
AtomicIntegerSerializer 序列化方法第一个入参是:AtomicInteger
AtomicLongSerializer 序列化方法第一个入参是:AtomicLong
1.7 时间对象的序列化器
SqlDateSerializer 序列化方法第一个入参是:java.sql.Date
CalendarSerializer 序列化方法第一个入参是:Calendar
DateSerializer 序列化方法第一个入参是:Date
1.8 字符串的序列化器
注意:StringSerializer虽然入参是:Object,但是底层进行String的转型,所以入参只能是String类型
而ToStringSerializer底层使用的是toString()方法,所以他可以把任何类型进行 “字符串的序列化”
比如有一个Integer 类型属性,我想把他序列化成字符串,那么我们就可以使用这个序列化器
StringSerializer 序列化方法第一个入参是:Object ,底层方法是:gen.writeString((String) value);
ToStringSerializer 序列化方法第一个入参是:Object ,底层方法是:gen.writeString(value.toString());;
后端把Long类型的数据传给前端,前端可能会出现精度丢失的情况。例如:201511200001725439这样一个Long类型的整数,传给前端后会变成201511200001725440
原因:201511200001725439超过js中Number类型长度
解决方法一:在后台将这个Long类型的字段转换成String类型的,风险比较大。
解决方法二:@JsonSerialize(using= ToStringSerializer.class)
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class Dog {
@JsonSerialize(using= ToStringSerializer.class)
private Long number;
//注意被转换的字段必须是包装类类型,否则会转换失败
}
1.9 枚举的序列化器
EnumSerializer 默认使用的是name(),你可以通过启用SerializationFeature.WRITE_ENUMS_USING_TO_STRING来让它使用toString()
1.10 其他类型的序列化器
ByteBufferSerializer 序列化方法第一个入参是: ByteBuffer
ClassSerializer 序列化方法第一个入参是: Class<?> value,
FileSerializer 序列化方法第一个入参是: File
UUIDSerializer 序列化方法第一个入参是: UUID
InetSocketAddressSerializer 序列化方法第一个入参是: InetSocketAddress
InetAddressSerializer 序列化方法第一个入参是: InetAddress
1.11 对象的序列化器
它可以序列化映射的Java对象JSON对象输出
BeanSerializer 序列化方法第一个入参是:Object bean
JsonValueSerializer 它是一个比较特殊的序列化器:用于序列化标注有@JsonValue注解的对象
@JsonValue这个注解在自定义enum类型序列化时特别有用。
因为一般情况下我们只希望enum类型序列化出去的那个我们自定义的属性:int值,而不是它的ordinal()或者name()
public enum MyEnum {
NAME("标记","A");
MyEnum(String sign,String type){
this.sign = sign;
this.type = type;
}
@JsonValue
private String sign;
private String type;
}
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class Person {
private String name ;
private MyEnum myEnum;
}
JsonMapper jsonMapper = new JsonMapper();
Person person = new Person("小王",MyEnum.NAME);
System.out.println(jsonMapper.writeValueAsString(person));
不加@JsonValue,运行序列化程序输出:{“name”:“小王”,“myEnum”:“NAME”}
加@JsonValue,运行序列化程序输出:{“name”:“小王”,“myEnum”:“标记”}
1.12 自定义序列化器
public class MySerializer extends StdSerializer<Date> {
public static final SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public MySerializer(){
super(Date.class);
}
@Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) {
jsonGenerator.writeString(format.format(date));
}
}
再使用@JsonSerialize(using = MySerializer.class)标注对应属性即可
二、jackson序列化的控制
枚举
SerializationFeature
可以控制序列化
时的一些特征
2.1 json输出格式:是否使用根名称root name进行包裹
- 相关属性;
WRAP_ROOT_VALUE(false)
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class Person {
private String name;
private Integer age;
}
默认配置测试案例
public class App {
public static void main( String[] args ) throws IOException {
JsonMapper jsonMapper = new JsonMapper();
//jsonMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
Person person = new Person("YoutBatman", 18);
System.out.println(jsonMapper.writeValueAsString(person));
}
}
控制台输出:{"name":"YoutBatman","age":18}
开启WRAP_ROOT_VALUE
,控制台输出:{"Person":{"name":"YoutBatman","age":18}}
tip: 99%情况下不要开启这个属性,除非为了兼容JAXB标准的@XmlRootElement.name xml解析
2.2 json输出格式:是否采用缩进输出
- 相关属性;INDENT_OUTPUT(false)
public class App {
public static void main( String[] args ) throws IOException {
JsonMapper jsonMapper = new JsonMapper();
//jsonMapper.enable(SerializationFeature.INDENT_OUTPUT);
Person person = new Person("YoutBatman", 18);
System.out.println(jsonMapper.writeValueAsString(person));
}
}
默认配置,控制台输出:{"name":"YoutBatman","age":18}
开启INDENT_OUTPUT
,控制台输出:
{
"name" : "YoutBatman",
"age" : 18
}
说明:若开启了此特征,会采用com.fasterxml.jackson.core.PrettyPrinter进行输出,而Jackson默认使用的是DefaultPrettyPrinter这个实现类处理的
2.3 访问器和空对象对于json序列化的影响
- 相关属性;FAIL_ON_EMPTY_BEANS(true)
// 注意这个Bean的特点:属性均为private,并且没有提供public属性或者get方法,所以它是没有访问器的
@NoArgsConstructor
@AllArgsConstructor
@Setter
@ToString
public class Person {
private String name;
private Integer age;
}
访问器:默认配置测试案例
public class App
{
public static void main( String[] args ) throws IOException {
JsonMapper jsonMapper = new JsonMapper();
//jsonMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
Person person = new Person("YoutBatman", 11);
System.out.println(jsonMapper.writeValueAsString(person));
}
}
默认,控制台输出:com.fasterxml.jackson.databind.exc.InvalidDefinitionException
禁用FAIL_ON_EMPTY_BEANS
,控制台输出:{}
如果把
age
属性改为public
,且不禁用FAIL_ON_EMPTY_BEANS
,控制台输出:{"age":11}
为什么?
因为把属性变为public,那么Jackson就可以访问到,这个属性了。同理,如果private到属性有get方法,那么我们也可以访问到这个属性。
空对象:默认配置测试案例
public class App{
public static void main( String[] args ) throws IOException {
JsonMapper jsonMapper = new JsonMapper();
// jsonMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
System.out.println(jsonMapper.writeValueAsString(new Object()))
}
}
默认,控制台输出:com.fasterxml.jackson.databind.exc.InvalidDefinitionException
禁用FAIL_ON_EMPTY_BEANS
,控制台输出:{}
- 如果不该配置,我们可以把空对象改为null,就不会错误了
JsonMapper jsonMapper = new JsonMapper();
System.out.println(jsonMapper.writeValueAsString(null));
注意:在这里new Person()不是空对象,空对象指的是无任何属性的类的实例。
2.4 对@JsonUnwrapped对支持
- 相关属性: FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS(true),
@JsonUnwrapped
对作用是使属性扁平化
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
class Person {
private String name ;
private Dog dog;
}
默认配置测试案例
public void fun1() throws JsonProcessingException {
JsonMapper jsonMapper = new JsonMapper();
Dog dog = new Dog();
dog.setName("大黄");
Person person = new Person("YourBatman",dog);
System.out.println(jsonMapper.writeValueAsString(person));
}
控制台输出:{"name":"YourBatman", "dog":{"name":"大黄"}}
- 若在Person属性dog头上标注上这么一个注解
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class Person {
private String name;
@JsonUnwrapped
private Dog dog;
}
再次运行程序,输出为{"name":"YourBatman", "name":"大黄"}
。可以看到Dog的属性被平铺下来了。
细心的你会发现出现了两个name属性,所以一般情况下建议写上前缀,形如这样:@JsonUnwrapped(prefix = "dog")
,
这样再次运行就输出为:{"name":"YourBatman","dogname":"大黄"}
2.5 否按照时间戳写出
- 相关属性; WRITE_DATES_AS_TIMESTAMPS(true)
JsonMapper jsonMapper = new JsonMapper();
// jsonMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
System.out.println(jsonMapper.writeValueAsString(new Date()));
默认,控制台输出:1581435981449
关闭配置,控制台输出:“2020-02-11T15:46:50.932+0000”
注意:在没有导入对JSR310支持的模块之前,Jackson对JSR310的时间的序列化方式是一致的,此特性只会影响到它对Date类型的处理,对LocalDateTime等JSR310时间类无效
导入Jackson对JSR310支持的包,并注册
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
JsonMapper jsonMapper = new JsonMapper();
jsonMapper.registerModule(new JavaTimeModule());//注册JSR310支持类,必须要,否则导入的JSR310包,就白加了
//jsonMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
//这里以LocalDateTime为例子
System.out.println("LocalDateTime: "+jsonMapper.writeValueAsString(LocalDateTime.now()));
如果不注册JSR310支持类,不管是否关闭配置,程序都输出:
{"dayOfYear":43,"dayOfWeek":"WEDNESDAY","year":2020,"month":"FEBRUARY","nano":289000000,"monthValue":2,"dayOfMonth":12,"hour":16,"minute":59,"second":54,"chronology":{"id":"ISO","calendarType":"iso8601"}}
注册JSR310支持类,默认配置输出:[2020,2,12,17,0,25,546000000]
注册JSR310支持类,关闭配置输出:2020-02-13T18:10:58.695
2.6 是否把char[]数组也当做数组序列化
相关属性:WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS(false)
JsonMapper jsonMapper = new JsonMapper();
char[] chars = new char[]{'a','b','c'};
//jsonMapper.enable(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS);
System.out.println(jsonMapper.writeValueAsString(chars));
默认配置输出:“abc”
开启配置输出:[“a”,“b”,“c”]
2.7 序列化枚举是否使用toString()方法。默认是name()方法
相关属性: WRITE_ENUMS_USING_TO_STRING(false)
public enum MyEnum {
NAME;
@Override
public String toString() {
return "MyEnum{}";
}
}
JsonMapper jsonMapper = new JsonMapper();
//jsonMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
System.out.println(jsonMapper.writeValueAsString(MyEnum.NAME));
默认配置输出:“NAME”
开启配置输出:“MyEnum{}”
2.8 对集合的处理
相关属性:WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED(false),
//jsonMapper.enable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
List<Integer> list1 = Arrays.asList(1);
List<Integer> list2 = Arrays.asList(1,2);
System.out.println(jsonMapper.writeValueAsString(list1));
System.out.println(jsonMapper.writeValueAsString(list2));
默认输出:
[1]
[1,2]
若开启此特征。输出
1
[1,2]
2.9 是否根据Map的key帮你排序
相关属性: ORDER_MAP_ENTRIES_BY_KEYS(false)
如果你已经是SortedMap,那么Jackson就不会再帮你排了
JsonMapper jsonMapper = new JsonMapper();
Map<Integer,String> map = new HashMap<>();
map.put(21,"空");
map.put(11,"空");
map.put(3,"空");
map.put(110,"空");
//jsonMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS);
System.out.println(jsonMapper.writeValueAsString(map));
默认输出:{"3":"空","21":"空","11":"空","110":"空"}
若开启此特征。输出:{"3":"空","11":"空","21":"空","110":"空"}