Jackson使用
如何使用 Jackson
学习任何一个技术,它是什么,怎么用,为什么?
这篇文档主要简单讲述如何使用 Jackson 进行序列化以及反序列化
如果需要快速的进行序列化或者反序列化,请用ctrl +F 进行搜索关键字。
Maven
- 在这里要说一下,Springboot中本身就依赖于Jackson,详情:https://www.bilibili.com/video/BV1kj411R7Wx?t=1.3
- note: 在这里还是把maven地址贴出来,但这三个依赖的版本一定要统一,当初笔者用时,databind版本比其他两个包高一级,在序列化和反序列化时都会出现失败。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
<!--对LocalDateTime等jdk8时间日期api的转化支持-->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.14.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.14.2</version>
</dependency>
<!--对数据进行XML序列化和反序列化-->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
代码展示
测试类
/**
* <P>
* Jackson测试类,添加了各种数据类型,以便于测试序列化和反序列化
* </P>
* @author unknown
* @since 2023/07/31 15:43
*/
@Data
public class Test {
private long longId;
private String stringTest;
private int intTest;
private double doubleTest;
private short shortTest;
private byte byteTest;
private char charTest;
private List<String> listStringTest;
/**
* 以下注解都是用于XML格式的序列化和反序列化,删除不影响JSON格式的序列化和反序列化
* 在javaTimeModule中自定义过相关类型的JOSN格式序列化和反序列化
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime localDateTimeTest;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date dateTest;
}
测试代码
/**
* @author unknown <P>
* @since 2023/07/31 03:38 <P>
* Jackson还支持CBOR,MessagePack,YAML等格式的序列化和反序列化,笔者暂时没有这个需求,所有不在此demo内举例
*/
public class JsonTest {
private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final XmlMapper xmlMapper = new XmlMapper();
static {
// 自定义序列化对象
JavaTimeModule javaTimeModule = new JavaTimeModule();
// LocalDateTime 序列化
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
// LocalDateTime 反序列化
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
/**
* 将在<P>globalConfigurationTest()</P>中诠释
*/
// 字段自动匹配,以对象字段为标准,JSON串种多余的字段舍弃
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 序列化后不包含null的字段
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
/*
// 将对象字段的驼峰转下划线
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
// 将对象字段的驼峰转下划线且全部字段转为大写
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_SNAKE_CASE);*/
/**
* <P>序列化美化输出</P>
* 此美化输出的作用体现于<P>每个字段占据一行</P>,类似在其他Json格式化网站一样的功能,方便阅读,生产线上不推荐此功能
*/
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
// 注册自定义序列化对象
objectMapper.registerModule(javaTimeModule);
}
/**
* <P>配置代码解释</P>
* Note: 在开启以下两个配置后,反序列化将会失效,待琢磨
* <p>
* 将对象字段的驼峰转下划线<P>
* objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
* <p>
* 将对象字段的驼峰转下划线且字段大写<P>
* objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_SNAKE_CASE);
*
* 相对于这种配置,最好是用在JavaBean上,因为大部分序列化都是正常格式,有部分特殊的对象可能需要字段格式转换
* @see com.example.blog.jacksontest.JacksonAnnotationEntityTest
*/
@Test
public void globalConfigurationTest() throws JsonProcessingException {
com.example.blog.jacksontest.Test test = new com.example.blog.jacksontest.Test();
test.setLongId(1L);
// test.setStringTest(DATE_TIME_FORMAT);
test.setIntTest(2);
test.setDoubleTest(3.0);
test.setShortTest((short) 44);
test.setByteTest((byte) 1128);
// test.setCharTest('A');
test.setLocalDateTimeTest(LocalDateTime.now());
test.setDateTest(new Date());
String string = objectMapper.writeValueAsString(test);
/**
* 将对象字段的驼峰转下划线且全部字段转为大写
* objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_SNAKE_CASE);
* {
* "LONG_ID" : 1,
* // String类型的字段注释掉,将不会序列化此字段 -> objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
* "INT_TEST" : 2,
* "DOUBLE_TEST" : 3.0,
* "SHORT_TEST" : 44,
* "BYTE_TEST" : 104,
* "CHAR_TEST" : "\u0000", char类型的字段也不注释掉,但此字段在java中有默认值
* "LOCAL_DATE_TIME_TEST" : "2023-08-01 03:17:23",
* "DATE_TEST" : "2023-07-31 19:17:23"
* }
*/
System.out.println(string);
// 添加多余字段Test测试-> objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
String deserialized = "{\"Test\":1,\"longId\":1,\"stringTest\":\"yyyy-MM-dd HH:mm:ss\",\"intTest\":2," +
"\"doubleTest\":3.0,\"shortTest\":-32768,\"byteTest\":104,\"charTest\":\"A\"," +
"\"localDateTimeTest\":\"2023-08-31 22:26:43\",\"dateTest\":\"2023-07-31 14:26:43\"}";
com.example.blog.jacksontest.Test deserializedTest = objectMapper.readValue(deserialized, com.example.blog.jacksontest.Test.class);
/**
* 多余的字段Test已被自动舍弃
* Test(longId=1, stringTest=yyyy-MM-dd HH:mm:ss, intTest=2, doubleTest=3.0, shortTest=-32768, byteTest=104,
* charTest=A, localDateTimeTest=2023-08-31T22:26:43, dateTest=Mon Jul 31 22:26:43 CST 2023)
*/
System.out.println(deserializedTest);
}
/**
* <P>JavaBean序列化JSON串</P>
*
* <p>LocalDateTime<p>
* 当对象序列化的时候,需要自定义JavaTimeModule的LocalDateTime格式并且注册JavaTimeModule:
* JavaTimeModule javaTimeModule = new JavaTimeModule();
* javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
* objectMapper.registerModule(javaTimeModule);
*
* <p>Date</p>
* 此属性序列化于反序列化时,需要在此对象字段加上@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss"),不推荐在JavaTimeModule中定义
* 因为SimpleDateFormat是线程不安全的,但是ObjectMapper对象是现场安全的,这样会导致ObjectMapper也是不安全的,错误的如下
* SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
* objectMapper.setDateFormat(dateFormat);
*/
@Test
public void javaBeanSerialize() throws JsonProcessingException {
com.example.blog.jacksontest.Test test = new com.example.blog.jacksontest.Test();
test.setLongId(1L);
test.setStringTest(DATE_TIME_FORMAT);
test.setIntTest(2);
test.setDoubleTest(3.0);
test.setShortTest((short) 44);
test.setByteTest((byte) 1128);
test.setCharTest('A');
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("hello");
arrayList.add("world");
test.setListStringTest(arrayList);
test.setLocalDateTimeTest(LocalDateTime.now());
test.setDateTest(new Date());
String string = objectMapper.writeValueAsString(test);
System.out.println(string);
}
/**
* <P>JavaBean转文件</P>
*/
@Test
public void javaBeanToFile() throws IOException {
com.example.blog.jacksontest.Test test = new com.example.blog.jacksontest.Test();
test.setLongId(1L);
test.setStringTest(DATE_TIME_FORMAT);
test.setIntTest(2);
test.setDoubleTest(3.0);
test.setShortTest((short) 44);
test.setByteTest((byte) 1128);
test.setCharTest('A');
test.setLocalDateTimeTest(LocalDateTime.now());
test.setDateTest(new Date());
//写到文件
objectMapper.writeValue(new File("src/main/resources/static/json.txt"), test);
//从文件中读取
com.example.blog.jacksontest.Test read = objectMapper.readValue(new File("/json.txt"), com.example.blog.jacksontest.Test.class);
System.out.println(read);
}
/**
* <P>javaBean转字节流</P>
*/
@Test
public void javaBeanToByte() throws IOException {
com.example.blog.jacksontest.Test test = new com.example.blog.jacksontest.Test();
test.setLongId(1L);
test.setStringTest(DATE_TIME_FORMAT);
test.setIntTest(2);
test.setDoubleTest(3.0);
test.setShortTest((short) 44);
test.setByteTest((byte) 1128);
test.setCharTest('A');
test.setLocalDateTimeTest(LocalDateTime.now());
test.setDateTest(new Date());
// 写为字节流
byte[] bytes = objectMapper.writeValueAsBytes(test);
// 从字节流读取
com.example.blog.jacksontest.Test read = objectMapper.readValue(bytes, com.example.blog.jacksontest.Test.class);
System.out.println(read);
}
/**
* <P>JavaBean序列化XML</P>
* 在序列化XML时,遇到了一个问题,也是Java8中LocalDateTime的问题,最后的解决办法是在字段上加上注解组局序列化
* @see com.example.blog.jacksontest.Test
* 查过了一些资料,Springboot中没有xmlMapper的依赖,而且不可以用xmlMapper注册JacksonXmlModule对象
* @see JacksonXmlModule
* 根据以上信息,大胆猜测需要在Springboot中配置XmlMapper的JavaBean,待实验
*/
@Test
public void javaBeanToXML() throws JsonProcessingException {
com.example.blog.jacksontest.Test test = new com.example.blog.jacksontest.Test();
test.setLongId(1L);
test.setStringTest(DATE_TIME_FORMAT);
test.setIntTest(2);
test.setDoubleTest(3.0);
test.setShortTest((short) 44);
test.setByteTest((byte) 1128);
test.setCharTest('A');
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("hello");
arrayList.add("world");
test.setListStringTest(arrayList);
test.setLocalDateTimeTest(LocalDateTime.now());
test.setDateTest(new Date());
JacksonXmlModule jacksonXmlModule = new JacksonXmlModule();
jacksonXmlModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
jacksonXmlModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
xmlMapper.registerModule(jacksonXmlModule);
String string = xmlMapper.writeValueAsString(test);
System.out.println(string);
}
/**
* <P>XML反序列化JavaBean</P>
* 在反序列化XML时,遇到了一个问题,也是Java8中LocalDateTime的问题,最后的解决办法是在字段上加上注解组局序列化
* @see com.example.blog.jacksontest.Test
* 查过了一些资料,Springboot中没有xmlMapper的依赖,而且不可以用xmlMapper注册JacksonXmlModule对象
* @see JacksonXmlModule
* 根据以上信息,大胆猜测需要在Springboot中配置XmlMapper的JavaBean,待实验
*/
@Test
public void xmlToJavaBean() throws JsonProcessingException {
String xml = "<Test><longId>1</longId><stringTest>yyyy-MM-dd HH:mm:ss</stringTest><intTest>2</intTest>" +
"<doubleTest>3.0</doubleTest><shortTest>44</shortTest><byteTest>104</byteTest><charTest>A</charTest>" +
"<listStringTest><listStringTest>hello</listStringTest><listStringTest>world</listStringTest></listStringTest>" +
"<localDateTimeTest>2023-08-01 23:10:26</localDateTimeTest><dateTest>2023-08-01 15:10:26</dateTest></Test>\n";
com.example.blog.jacksontest.Test javaBean = JsonTest.xmlMapper.readValue(xml, new TypeReference<com.example.blog.jacksontest.Test>() {
});
System.out.println(javaBean);
Jackson2ObjectMapperBuilder xmlMapperBuild = new Jackson2ObjectMapperBuilder().createXmlMapper(true);
}
/**
* <P>JSON串反序列化JavaBean</P>
*
* <p>LocalDateTime<p>
* 当对象序列化的时候,需要自定义JavaTimeModule的LocalDateTime格式并且注册JavaTimeModule:
* JavaTimeModule javaTimeModule = new JavaTimeModule();
* javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
* objectMapper.registerModule(javaTimeModule);
*
* <p>LocalDateTimeDate<p>
* <p>
* 此属性序列化于反序列化时,需要在此对象字段加上@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss"),不推荐在JavaTimeModule中定义和objectMapper设置
* 因为SimpleDateFormat是线程不安全的,但是ObjectMapper对象是现场安全的,这样会导致ObjectMapper也是不安全的,错误的如下
* SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
* objectMapper.setDateFormat(dateFormat);
*/
@Test
public void deserializeToJavaBean() throws JsonProcessingException {
String deserialized = "{\n" +
" \"longId\" : 1,\n" +
" \"stringTest\" : \"yyyy-MM-dd HH:mm:ss\",\n" +
" \"intTest\" : 2,\n" +
" \"doubleTest\" : 3.0,\n" +
" \"shortTest\" : 44,\n" +
" \"byteTest\" : 104,\n" +
" \"charTest\" : \"A\",\n" +
" \"listStringTest\" : [ \"hello\", \"world\" ],\n" +
" \"localDateTimeTest\" : \"2023-08-01 19:24:02\",\n" +
" \"dateTest\" : \"2023-08-01 11:24:02\"\n" +
"}";
com.example.blog.jacksontest.Test deserializeResult = objectMapper.readValue(deserialized, com.example.blog.jacksontest.Test.class);
System.out.println(deserializeResult);
}
/**
* <P>JSON串反序列化Map集合</P>
*/
@Test
public void deserializeToMap() throws JsonProcessingException {
String serialized = "{\"longId\":1,\"stringTest\":\"yyyy-MM-dd HH:mm:ss\",\"intTest\":2," +
"\"doubleTest\":3.0,\"shortTest\":-32768,\"byteTest\":104,\"charTest\":\"A\"," +
"\"localDateTimeTest\":\"2023-08-31 22:26:43\",\"dateTest\":\"2023-07-31 14:26:43\"}";
Map<String, String> map = objectMapper.readValue(serialized, new TypeReference<>() {
});
System.out.println(map);
}
/**
* <P>JSON串反序列化List集合</P>
* 注意: List的JSON串多了个中括号[]
*/
@Test
public void deserializeToList() throws JsonProcessingException {
String serialized = "[{\"longId\":1,\"stringTest\":\"yyyy-MM-dd HH:mm:ss\",\"intTest\":2," +
"\"doubleTest\":3.0,\"shortTest\":-32768,\"byteTest\":104,\"charTest\":\"A\"," +
"\"localDateTimeTest\":\"2023-08-31 22:26:43\",\"dateTest\":\"2023-07-31 14:26:43\"}]";
List<com.example.blog.jacksontest.Test> tests = objectMapper.readValue(serialized, new TypeReference<List<com.example.blog.jacksontest.Test>>() {
});
System.out.println(tests);
}
/**
* <P>JavaBean覆盖更新</P>
* <p>
* 对JavaBean字段属性的重写,如果后者有值,则用后者,没有则用前者,
*/
@Test
public void javaBeanOverrideTest() throws JsonProcessingException {
com.example.blog.jacksontest.Test Source = new com.example.blog.jacksontest.Test();
Source.setLongId(1L);
Source.setStringTest(DATE_TIME_FORMAT);
Source.setIntTest(1);
Source.setDoubleTest(1.0);
Source.setShortTest((short) 1);
Source.setByteTest((byte) 1);
Source.setLocalDateTimeTest(LocalDateTime.now());
Source.setDateTest(new Date());
com.example.blog.jacksontest.Test overrideField = new com.example.blog.jacksontest.Test();
overrideField.setLongId(2L);
overrideField.setStringTest(DATE_TIME_FORMAT);
overrideField.setIntTest(2);
overrideField.setDoubleTest(2.0);
overrideField.setShortTest((short) 2);
overrideField.setByteTest((byte) 2);
// 新增属性
overrideField.setCharTest('B');
overrideField.setLocalDateTimeTest(LocalDateTime.now());
overrideField.setDateTest(new Date());
com.example.blog.jacksontest.Test test = objectMapper.updateValue(Source, overrideField);
System.out.println(test);
}
/**
* <P>泛型的处理</P>
*/
@Test
public void genericTest() throws JsonProcessingException {
com.example.blog.jacksontest.Test test = new com.example.blog.jacksontest.Test();
test.setLongId(1L);
test.setStringTest(DATE_TIME_FORMAT);
test.setIntTest(2);
test.setDoubleTest(3.0);
test.setShortTest((short) 32768);
test.setByteTest((byte) 1128);
test.setCharTest('A');
test.setLocalDateTimeTest(LocalDateTime.now());
test.setDateTest(new Date());
// 数据封装返回类
Result<com.example.blog.jacksontest.Test> testResult = ResultBuilder.successResult(test);
// 序列化返回结果
String genericResult = objectMapper.writeValueAsString(testResult);
// 反序列化结果
Result<com.example.blog.jacksontest.Test> deserializerResult =
objectMapper.readValue(genericResult, new TypeReference<Result<com.example.blog.jacksontest.Test>>() {
});
System.out.println("带泛型的反序列化: " + deserializerResult);
com.example.blog.jacksontest.Test data = deserializerResult.getData();
System.out.println("数据反序列化对象: " + data);
}
/**
* <P>解析JSON字符串为JSON树模型</P>
* 当JSON串数据类型比较多内容复杂时,则可使用JSON树模型来灵活的获取所需的字段内容,如下面的JSON串中,我们只需要listStringTest这个数组
* <p>
* 在Jackson中提供了get、path、has等方法来获取或判断
*/
@Test
public void jsonTreeNode() throws JsonProcessingException {
String jsonString = "{\n" +
" \"longId\" : 1,\n" +
" \"stringTest\" : \"yyyy-MM-dd HH:mm:ss\",\n" +
" \"intTest\" : 2,\n" +
" \"doubleTest\" : 3.0,\n" +
" \"shortTest\" : 44,\n" +
" \"byteTest\" : 104,\n" +
" \"charTest\" : \"A\",\n" +
" \"listStringTest\" : [ \"hello\", \"world\" ],\n" +
" \"localDateTimeTest\" : \"2023-08-01 20:04:35\",\n" +
" \"dateTest\" : \"2023-08-01 12:04:35\"\n" +
"}";
JsonNode treeNode = objectMapper.readTree(jsonString);
// 单独获取某个字段
JsonNode longId = treeNode.path("longId");
System.out.println(longId);
// 获取数组内的数据
JsonNode listStringTest = treeNode.get("listStringTest");
for (JsonNode jsonNode : listStringTest) {
System.out.println(jsonNode);
}
}
/**
* <P>jackson注解测试</P>
* @see com.example.blog.jacksontest.JacksonAnnotationEntityTest
*/
@Test
public void jacksonAnnotationTest() throws JsonProcessingException {
JacksonAnnotationEntityTest jacksonAnnotationEntityTest = new JacksonAnnotationEntityTest();
jacksonAnnotationEntityTest.setLongId(1L);
jacksonAnnotationEntityTest.setStringTest(DATE_TIME_FORMAT);
jacksonAnnotationEntityTest.setIntTest(2);
jacksonAnnotationEntityTest.setDoubleTest(3.0);
jacksonAnnotationEntityTest.setShortTest((short) 44);
jacksonAnnotationEntityTest.setByteTest((byte) 1128);
jacksonAnnotationEntityTest.setCharTest('A');
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("hello");
arrayList.add("world");
jacksonAnnotationEntityTest.setBooleanTest(true);
jacksonAnnotationEntityTest.setListStringTest(arrayList);
jacksonAnnotationEntityTest.setLocalDateTimeTest(LocalDateTime.now());
jacksonAnnotationEntityTest.setDateTest(new Date());
String string = objectMapper.writeValueAsString(jacksonAnnotationEntityTest);
System.out.println(string);
}
}
注解测试
/**
* <P>Jackson注解序列化测试</P>
* @author unknown
* @since 2023/08/01 03:40
* 更多注解请参考: https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations
* <P>
* JsonIgnoreProperties("Field1","Field2")
* 多字段在序列化和反序列化时进行忽略,升级版的JsonIgnore
* <P>
* JsonIgnoreType
* 在序列化和反序列化时,会忽略此类型,例如有一个demo类,类中有成员属性JacksonAnnotationEntityTest,
* 那么在序列化和反序列化demo类时,将不会输出类中有成员属性JacksonAnnotationEntityTest
* <P>
* JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
*
*/
@Data
@JsonIgnoreProperties({"doubleTest","shortTest"})
//@JsonIgnoreType
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class JacksonAnnotationEntityTest {
private long longId;
/**
* <P</P>
* JsonProperty("xxx")
* 改变序列化后的字段名称,以括号内的名字取代原本字段名称
*/
@JsonProperty("xxx")
private String stringTest;
/**
* <P></P>
* JsonIgnore
* 此注解在序列化及反序列化的时候会忽略此字段
*/
@JsonIgnore
private int intTest;
private double doubleTest;
private boolean booleanTest;
private short shortTest;
private byte byteTest;
private char charTest;
private List<String> listStringTest;
private LocalDateTime localDateTimeTest;
private Date dateTest;
}