当前位置 : 首页 » 文章分类 :  开发  »  Jackson

Jackson

Jackson笔记

https://github.com/FasterXML/jackson


ObjectMapper 配置

关闭 FAIL_ON_EMPTY_BEANS

jackson 序列化 bean 时,遇到 null 默认会报错,关闭此属性即可。
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);

关闭 FAIL_ON_UNKNOWN_PROPERTIES

JSON 字符串中含有我们并不需要的字段,那么当对应的实体类中不含有该字段时,会抛出一个异常,此设置不抛异常
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
等于
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

JsonInclude.Include.NON_NULL

设置 Jackson 序列化时只包含不为空的字段
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);


TypeReference 泛型反序列化

fastjson 和 Jackson 都提供了用于处理泛型反序列化的类 TypeReference, 用于将json串直接反序列化为具体类型,如果不使用 TypeReference 会反序列化为一堆map

例如

// 定义一个通用的 包裹单个bean的 http响应结构,具体的bean类型是泛型
public class CommonBeanResponse<T> {
    private T data;

    @JsonProperty("request_id")
    private String requestId;

    @JsonProperty("server_time")
    private long serverTime;

    @JsonProperty("result_code")
    private String resultCode;

    private String message;

    public T getData() { return data; }
    public void setData(T data) { this.data = data; }
    // 其他 getter  setter 省略
    ...

    public static void main(String[] args) {
      CommonBeanResponse<UserBean> responseSrc = new CommonBeanResponse<>();
      responseSrc.setData(new UserBean());
      String jsonStr = objectMapper.writeValueAsString(responseSrc);

      // 正确,可反序列化为具体的 UserBean
      CommonBeanResponse<UserBean> responseDst = objectMapper.readValue(jsonStr, new TypeReference<CommonBeanResponse<UserBean>>() {});

      // 错误,反序列化后 data域是一堆 k-v map
      CommonBeanResponse<UserBean> responseDst2 = objectMapper.readValue(jsonStr, CommonBeanResponse.class);
    }
}

再比如直接读取到 List

List<UserBean> userBeans = getUserBeanList();
String jsonStr = objectMapper.writeValueAsString(userBeans);
List beanList  = objectMapper.readValue(jsonStr, new TypeReference<List<UserBean>>() {});

为什么使用TypeReference (解释原理)
https://yq.aliyun.com/articles/609441

TypeReference – 让Jackson Json在List/Map中识别自己的Object
https://blog.csdn.net/ssjiang/article/details/7769525

alibaba/fastjson - TypeReference
https://github.com/alibaba/fastjson/wiki/TypeReference


Jackson常用注解

@JsonProperty 字段与成员变量映射

用于属性,把属性的名称序列化时转换为另外一个名称。示例:
@JsonProperty(“birth_date”)
private Date birthDate;

@JsonProperty可以指定字段的命名(还可以指定这个字段需要参与序列化和反序列化)。
@JsonProperty.value:指定的字段名字
@JsonProperty.index:指定顺序,默写数据格式是基于顺序(JSON不是这种数据格式)
@JsonProperty.defaultValue:默认值。注意:这个属性目前为止并没有被core和data-bind使用;制备一些扩展模块使用。

Jackson 框架的高阶应用
https://www.ibm.com/developerworks/cn/java/jackson-advanced-application/index.html


@JsonNaming 序列化命名策略

@JsonNaming 注解用来指定属性序列化使用的命名策略,覆盖默认实现。可以通过 value 属性指定策略,包括自定义策略。

除了默认的 LOWER_CAMEL_CASE 机制外,Jackson 还提供了四种内置命名策略:
KebabCaseStrategy “Lisp” 风格,采用小写字母、连字符作为分隔符,例如 “lower-case” 或 “first-name”
LowerCaseStrategy 所有的字母小写,没有分隔符,例如 lowercase
SnakeCaseStrategy 所有的字母小写,下划线作为名字之间分隔符,例如 snake_case.
UpperCamelCaseStrategy 所有名字(包括第一个字符)都以大写字母开头,后跟小写字母,没有分隔符,例如 UpperCamelCase

例如常用的 驼峰 转 下划线分隔
单个类的注解命名

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class MyClass {

}

全局命名
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)

注意:
1、如果同时设置了全局规则和某个类的命名规则,类的命名规则会覆盖全局设置。
1、如果同时设置类的命名规则 和使用 @JsonProperty 在字段上指定序列化名称,则 @JsonProperty 覆盖类上的命名规则。

Jackson 属性自定义命名策略
https://mlog.club/article/5953


@JsonSerialize 自定义序列化器

枚举序列化为小写

使用示例如下:

@JsonInclude(Include.NON_NULL)
public class Bean {
  @JsonSerialize(using = EnumLowerCaseSerializer.class)
  @JsonProperty("inviter_identity")
  private InviterIdentity inviterIdentity;
}

其中
InviterIdentity是自定义枚举类
EnumLowerCaseSerializer是自定义的一个将枚举转化为name小写的转化器,继承自Jackson的JsonSerializer抽象类,重写了其中的serialize()方法

public class EnumLowerCaseSerializer extends JsonSerializer<Enum> {
    @Override
    public void serialize(Enum value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(value == null ? null : value.name().toLowerCase());
    }
}

Date序列化为时间戳秒

@JsonInclude(Include.NON_NULL)
public class Bean {
  // 创建时间
  @JsonProperty("create_time")
  @JsonSerialize(using = SecondSerializer.class)
  @JsonDeserialize(using = SecondDeserializer.class)
  private Date createTime;
}

其中的Date转换为时间戳秒的序列化类:

package com.masikkk.common.json;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.util.Date;

public class SecondSerializer extends JsonSerializer<Date> {
    @Override
    public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
        jsonGenerator.writeString(date.getTime() / 1000 + "");
    }
}

SpringMVC日期转换之JsonSerialize
https://blog.csdn.net/rendiyiforarchitect/article/details/8056514

自动给Long型字段加一个_str结尾的同值字符串字段

@JsonInclude(Include.NON_NULL)
public class Bean {
  // 用户UUID
  @JsonSerialize(using = LongUUIDSerializer.class)
  private Long uuid;
}

本来uuid是Long型的,可能出现在前后端传输中丢失精度,加上这个序列化类注解后,可自动生成一个名为 uuid_str 的字段,值是 uuid 对应的String类型。

其中的 LongUUIDSerializer 是自定义的序列化器,给注解的Long字段自动加一个 _str 结尾的字符串字段

package com.masikkk.json;

import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.base.GeneratorBase;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

public class LongUUIDSerializer extends JsonSerializer<Long> {
    // 新增json名称固定后缀
    public final static String jsonNameSuffix = "_str";

    @Override
    public void serialize(Long value, JsonGenerator jsonGenerator, SerializerProvider serializers)
            throws IOException, JsonProcessingException {
        jsonGenerator.writeNumber(value);
        if (jsonGenerator instanceof GeneratorBase) {
            String jsonPropertyName = ((GeneratorBase) jsonGenerator).getOutputContext().getCurrentName();
            jsonGenerator.writeStringField(jsonPropertyName.concat(jsonNameSuffix), String.valueOf(value));
        }
    }
}

@JsonDeserialize 自定义反序列化器

时间戳秒反序列化为Date

@JsonInclude(Include.NON_NULL)
public class Bean {
  // 创建时间
  @JsonProperty("create_time")
  @JsonSerialize(using = SecondSerializer.class)
  @JsonDeserialize(using = SecondDeserializer.class)
  private Date createTime;
}

其中的 SecondDeserializer 是时间戳秒转换为Date的反序列类:

package com.masikkk.common.json;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.util.Date;

public class SecondDeserializer extends JsonDeserializer<Date> {
    @Override
    public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        return new Date(Long.parseLong(jsonParser.getText()) * 1000);
    }
}

@JsonFormat 指定String日期格式

import com.fasterxml.jackson.annotation.JsonFormat;

@JsonProperty(“create_time”)
@JsonFormat(shape = Shape.STRING, pattern = “yyyy-MM-dd HH:mm:ss”)
private Date createTime;

@JsonProperty(“update_time”)
@JsonFormat(shape = Shape.STRING, pattern = “yyyy-MM-dd HH:mm:ss”)
private Date updateTime;


@JsonPropertyOrder 指定字段顺序

@JsonPropertyOrder({ “id”, “label”, “target”, “source”, “attributes” })
public class BeanClass{

}


@JsonCreator 注解反序列化方法

json反序列化为java对象时,该注解用于定义构造函数。当从json创建java时,@JsonCreator注解的构造函数被会调用,如果没有@JsonCreator注解,则默认调用java类的无参构造函数,此时,如果java类中只有有参构造函数,而无默认的无参构造函数,在反序列化时会抛出这样的异常:com.fasterxml.jackson.databind.JsonMappingException,所以,当我们不使用@JsonCreator指定反序列化的构造函数,而又在java类中重载了构造函数时,一定要记得编写类的无参构造函数。

@JsonCreator :反序列化时的构造方法,入参为对应该枚举的json值

public enum VehicleUserRole {

    UNKNOWN((byte) 0, "未知"),
    VehicleUserRole1((byte) 1, "1"),
    VehicleUserRole2((byte) 2, "2"),
    VehicleUserRole3((byte) 3, "3");

    private byte code;
    private String name;

    VehicleUserRole(byte code, String name) {
        this.code = code;
        this.name = name;
    }

    public byte getCode() {
        return code;
    }

    public String getName() {
        return name;
    }

    private static Map<Byte, VehicleUserRole> map = Maps.newHashMap();

    static {
        for (VehicleUserRole value : VehicleUserRole.values()) {
            map.put(value.getCode(), value);
        }
        map = Collections.unmodifiableMap(map);
    }

    @JsonCreator
    public static VehicleUserRole getVehicleUserRole(String str) {
        if (StringUtils.isBlank(str)) {
            return UNKNOWN;
        }
        return VehicleUserRole.valueOf(str.trim().toUpperCase());
    }

    public static VehicleUserRole get(byte code) {
        return map.get(code);
    }
}

反序列化构造方法也可以这样写,更稳妥:

@JsonCreator
public static OperatorRole forValue(String nameString) {
    for (OperatorRole operatorRole : OperatorRole.values()) {
        if (operatorRole.name().equalsIgnoreCase(nameString)) {
            return operatorRole;
        }
    }
    return OperatorRole.UNKNOWN;
}

使用处:

@JsonSerialize(using = EnumLowerCaseSerializer.class)
@JsonProperty("vehicle_user_role")
private VehicleUserRole vehicleUserRole;

@JsonValue 注解序列化方法

序列化时,用来生成json值的方法

public enum InviterIdentity {
    UNKNOW((byte) 0, "未知身份"),
    InviterIdentity1((byte) 1, "1"),
    InviterIdentity2((byte) 2, "2"),
    InviterIdentity3((byte) 3, "3");

    private byte code;
    private String name;

    private static Map<Byte, InviterIdentity> codeMap = Maps.newHashMap();

    static {
        for (InviterIdentity inviterIdentity : InviterIdentity.values()) {
            codeMap.put(inviterIdentity.code, inviterIdentity);
        }
        codeMap = Collections.unmodifiableMap(codeMap);
    }

    InviterIdentity(byte code, String name) {
        this.code = code;
        this.name = name;
    }

    public Byte getCode() {
        return code;
    }

    public String getName() {
        return name;
    }

    @JsonValue
    public String getInviterIdentity() {
        return codeMap.get(code).toString().toLowerCase();
    }

    public static InviterIdentity getIdentityByCode(Byte code) {
        return codeMap.get(code);
    }
}

// 使用处
@JsonSerialize(using = EnumLowerCaseSerializer.class)
@JsonProperty("inviter_identity")
private InviterIdentity inviterIdentity;

Jackson 枚举序列化/反序列化
https://blog.csdn.net/z69183787/article/details/54292789


@JsonIgnore 单个字段过滤

@JsonIgnore 注解用来忽略某些字段,可以用在Field或者Getter方法上,用在Setter方法时,和Filed效果一样。这个注解只能用在POJO存在的字段要忽略的情况。

@JsonIgnoreProperties 字段过滤

比如要接收的json字段不固定,或者其中某些字段用不到,可以使用 @JsonIgnoreProperties 做字段过滤

在json转换成的实体类加注解 @JsonIgnoreProperties(ignoreUnknown = true),注意这是类级别的注解。将这个注解写在类上之后,就会忽略类中不存在的字段,达到按需接受的目的。

这个注解还可以指定要忽略的字段。使用方法如下:
@JsonIgnoreProperties({ “internalId”, “secretKey” })
指定的字段不会被序列化和反序列化。


@JsonInclude指定序列化哪些字段

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

只序列化非null元素
@JsonInclude(Include.NON_NULL)

只序列化非null和非””元素
@JsonInclude(Include.NON_EMPTY)


其他

字段名不确定如何解析?Map

Gson解析JSON中动态未知字段key的方法
https://blog.csdn.net/Chaosminds/article/details/49049455

java 解析不确定key的json
https://blog.csdn.net/qq_15058425/article/details/56834565

Android json解析动态获取key以及解析技巧
https://blog.csdn.net/u013072976/article/details/43561779

boolean isDone json序列化后成两个字段

// 是否从没购买过
@JsonProperty(“is_never_bought”)
private Boolean neverBought;

public Boolean getNeverBought() {return isNeverBought;}
public void setNeverBought(Boolean neverBought) {isNeverBought = neverBought;}

“never_bought”: true,
“is_never_bought”: true

解决方法,getNeverBought()改为getIsNeverBought()


Direct self-reference leading to cycle 循环自引用

Jackson序列化时报错:

failed to process json obj
com.fasterxml.jackson.databind.JsonMappingException: Direct self-reference leading to cycle (through reference chain:

原因是要序列化的bean中引用了自己,例如:

UserKafkaMessage message = new UserKafkaMessage();
message.setName("xxx");
... ...
message.setBefore(message); // 自己引用了自己

其中 before 字段是 UserKafkaMessage 类型的,本意是要存放user信息变更前的各个字段的值,没想到写代码时写错了直接把 message 自己的引用放进去了,导致出现序列化时 Direct self-reference leading to cycle 错误。


上一篇 Cassandra笔记

下一篇 接下来要学习的

阅读
评论
2,472
阅读预计11分钟
创建日期 2018-08-16
修改日期 2020-07-23
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论