jackson之序列化

对象序列化会调对象的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":"空"}


版权声明:本文为qq_41071876原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。