文章
问答
冒泡
jackson的用法拆解

目前的业务系统中,前后端数据交互,或者基于http的微服务之间通信,大多是基于json的数据格式。
当前用的比较多是就是jackson,这里我们就主要介绍jackson的用法。
https://github.com/FasterXML/jackson/

添加依赖

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

1. 基本的序列化与反序列化

public class JacksonTest {

    private final static ObjectMapper mapper;

    static {
        mapper = initMapper();
    }

    public static ObjectMapper initMapper() {
        ObjectMapper mapper = new ObjectMapper();
        return mapper;
    }

    @Test
    public void test1() throws JsonProcessingException {
        Person person = createPerson();
        String jsonString = mapper.writeValueAsString(person);
        System.out.println(jsonString);
        String jsonStr = "{\"name\":\"小李\",\"age\":19}";
        Person person1 = mapper.readValue(jsonStr,Person.class);
        System.out.println(person1);
    }

    public Person createPerson() {
        return Person.builder().name("小王").age(12).build();
    }

    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Person {
        private String name;
        private Integer age;
    }

}
0

2. json字符串中的属性比bean中的多

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Person {
    private String name;
    private Integer age;
}
@Test
public void test2() throws JsonProcessingException {
    String jsonStr = "{\"name\":\"小李\",\"age\":19,\"gender\":\"MAN\"}";
    Person person = mapper.readValue(jsonStr,Person.class);
    System.out.println(person);
}


报错如下

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "gender" (class com.moensun.commons.core.test.JacksonTest$Person), not marked as ignorable (2 known properties: "name", "age"])
at [Source: (String)"{"name":"小李","age":19,"gender":"MAN"}"; line: 1, column: 33] (through reference chain: com.moensun.commons.core.test.JacksonTest$Person["gender"])


解决方案:
在Bean上添加注解 @JsonIgnoreProperties(ignoreUnknown = true)

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Person {
    private String name;
    private Integer age;
}


给mapper增加配置 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

public static ObjectMapper initMapper() {
    ObjectMapper mapper = new ObjectMapper();
   mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
   return mapper;
}

3. Date 类型的格式化
jackson中Date的类型默认是序列化成时间戳的,如果要生成格式化的时间字符串,可以通过以下方式实现
 1. 在字段上添加注解 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Person {
    private String name;
    private Integer age;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date date;
}


2. 通过mapper全局配置

ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
return mapper;

4.  当序列化或者反序列化的对应处理类不存在的情况
自定义处理类,主要是继承 JsonSerializer 和 JsonDeserializer ,以Instant 为例,默认是jackson 是不支持Instant序列化的。我们可以自定义一个序列化和反序列化对象

public class InstantSerializer extends JsonSerializer<Instant> {
    @Override
    public void serialize(Instant value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (Objects.nonNull(value)) {
            gen.writeNumber(value.toEpochMilli());
        }
    }
}

public class InstantDeserializer extends JsonDeserializer<Instant> {
    @Override
    public Instant deserialize(JsonParser p, DeserializationContext ctx) throws IOException, JsonProcessingException {
        if (StringUtils.isNoneBlank(p.getText()) && p.getLongValue() > 0) {
            return Instant.ofEpochMilli(p.getLongValue());
        } else {
            return null;
        }
    }
}


在字段上添加注解

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Person {
      private String name;
      private Integer age;
      @JsonSerialize(using = InstantSerializer.class)
      @JsonDeserialize(using = InstantDeserializer.class)
      private Instant instant;
}


2. 通过注册module ,添加处理类

SimpleModule module = new SimpleModule();
module.addSerializer(Instant.class,new InstantSerializer());
module.addDeserializer(Instant.class,new InstantDeserializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
return mapper;
@Test
public void test4() throws JsonProcessingException {
    Person person = createPerson();
    String jsonString = mapper.writeValueAsString(person);
    System.out.println(jsonString);
    String jsonStr = "{\"name\":\"小李\",\"age\":19,\"instant\":1658053120673}";
    Person person1 = mapper.readValue(jsonStr,Person.class);
    System.out.println(person1);
}
0


对于 Instant ,可以通过 jsr310 的包来处理

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.13.3</version>
</dependency>

 

SimpleModule module = new SimpleModule();
module.addSerializer(Instant.class,InstantSerializer.INSTANCE);
module.addDeserializer(Instant.class, InstantDeserializer.INSTANT);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
return mapper;

5. 当属性名称与字符串中的不一致的情况
通过 @JsonProperty(value = "openid") 指定

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Person {
    private String name;
    private Integer age;
    @JsonProperty(value = "openid")
    private String openId;
}
@Test
public void test5() throws JsonProcessingException {
    Person person = createPerson();
    String jsonString = mapper.writeValueAsString(person);
    System.out.println(jsonString);
    String jsonStr = "{\"name\":\"小李\",\"age\":19,\"openid\":\"12345\"}";
    Person person1 = mapper.readValue(jsonStr,Person.class);
    System.out.println(person1);
}


public Person createPerson() {
    return Person.builder().name("小王").age(12).openId("1234").build();
}
0

6. 集合类型的转换

  • 通过 TypeReference
  • 通过 JavaType
@Test
public void test6() throws JsonProcessingException {
    Person person = createPerson();
    String jsonString = mapper.writeValueAsString(Lists.newArrayList(person));
    System.out.println(jsonString);
    String jsonStr = "[{\"name\":\"小李\",\"age\":19}]";
    List<Person> person1 = mapper.readValue(jsonStr, new TypeReference<List<Person>>() {
    });
    System.out.println(person1);
    JavaType javaType = mapper.getTypeFactory().constructCollectionType(List.class,Person.class);
    List<Person> person2 = mapper.readValue(jsonStr, javaType);
    System.out.println(person2);
}
0

7. map类型转换

  • 通过 TypeReference
  • 通过 JavaType
@Test
public void test7() throws JsonProcessingException {
    Person person = createPerson();
    Map<String,Person> personMap = new HashMap<>();
    personMap.put("123",person);
    String jsonString = mapper.writeValueAsString(personMap);
    System.out.println(jsonString);
    String jsonStr = "{\"123\":{\"name\":\"小李\",\"age\":19}}";
    Map<String,Person> person1 = mapper.readValue(jsonStr, new TypeReference<HashMap<String,Person>>() {
    });
    System.out.println(person1);
    JavaType javaType = mapper.getTypeFactory().constructMapType(HashMap.class,String.class,Person.class);
    Map<String,Person> person2 = mapper.readValue(jsonStr, javaType);
    System.out.println(person2);
}

 

0

 

贴一个基于jackson的json 工具类

@Slf4j
public class JsonUtils {
    private final static ObjectMapper mapper;

    static {
        mapper = initMapper();
    }

    public static ObjectMapper initMapper() {
        SimpleModule module = new SimpleModule();
        module.addSerializer(Instant.class, InstantSerializer.INSTANCE);
        module.addDeserializer(Instant.class, InstantDeserializer.INSTANT);
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.registerModule(module);
        return mapper;
    }

    public static JavaType constructType(Class<?> clazz) {
        return mapper.constructType(clazz);
    }

    public static JavaType constructCollectionType(Class<? extends Collection> collectionClass,
                                                   Class<?> elementClass) {
        return mapper.getTypeFactory().constructCollectionType(collectionClass, elementClass);
    }

    public static JavaType constructMapType(Class<? extends Map> mapClass, Class<?> keyClass,
                                            Class<?> valueClass) {
        return mapper.getTypeFactory().constructMapType(mapClass, keyClass, valueClass);
    }


    public static String toJsonString(Object object) {
        try {
            return mapper.writeValueAsString(object);
        } catch (IOException ex) {
            log.error(ex.getMessage(), ex);
            throw new JsonException(ex.getMessage());
        }
    }

    public static <T> T parse(String jsonString, Class<T> clazz) {
        if (StringUtils.isBlank(jsonString)) {
            return null;
        }
        try {
            return mapper.readValue(jsonString, clazz);
        } catch (IOException ex) {
            log.error(ex.getMessage(), ex);
            throw new JsonException(ex.getMessage());
        }
    }

    @SuppressWarnings(value = "unchecked")
    public static <T> T parse(String jsonString, JavaType javaType) {
        if (StringUtils.isBlank(jsonString)) {
            return null;
        }

        try {
            return (T) mapper.readValue(jsonString, javaType);
        } catch (IOException ex) {
            log.error(ex.getMessage(), ex);
            throw new JsonException(ex.getMessage());
        }
    }

    public static <T> Collection<T> parseCollection(String jsonString, Class<T> clazz) {
        JavaType javaType = constructCollectionType(Collection.class, clazz);
        try {
            return mapper.readValue(jsonString, javaType);
        } catch (IOException ex) {
            log.error(ex.getMessage(), ex);
            throw new JsonException(ex.getMessage());
        }
    }

    public static <T> List<T> parseList(String jsonString, Class<T> clazz) {
        JavaType javaType = constructCollectionType(List.class, clazz);
        try {
            return mapper.readValue(jsonString, javaType);
        } catch (IOException ex) {
            log.error(ex.getMessage(), ex);
            throw new JsonException(ex.getMessage());
        }
    }

    public static <T> Set<T> parseSet(String jsonString, Class<T> clazz) {
        JavaType javaType = constructCollectionType(Set.class, clazz);
        try {
            return mapper.readValue(jsonString, javaType);
        } catch (IOException ex) {
            log.error(ex.getMessage(), ex);
            throw new JsonException(ex.getMessage());
        }
    }

    /**
     * 当JSON里只含有Bean的部分屬性時,更新一個已存在Bean,只覆蓋該部分的屬性.
     */
    public void update(Object object, String jsonString) {
        try {
            mapper.readerForUpdating(object).readValue(jsonString);
        } catch (IOException e) {
            log.error("update json string:" + jsonString + " to object:" + object + " error.", e);
        }
    }

}
jackson

关于作者

落雁沙
非典型码农
获得点赞
文章被阅读