目前的业务系统中,前后端数据交互,或者基于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;
}
}
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);
}
对于 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();
}
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);
}
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);
}
贴一个基于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);
}
}
}