译者注
原文
https://www.baeldung.com/jackson-object-mapper-tutorial
Demo
https://github.com/eugenp/tutorials/tree/master/jackson-simple
1. 前言
本文致力于帮助读者理解Jackson对象映射器(ObjectMapper),以及学习如何把对象序列化为JSON、如何把JSON反序列化成Java对象。
如果希望了解更多Jackson库的知识,请点击Jackson入门教程
2. 依赖
对于maven托管的项目,我们首先在pom.xml
中增加下面的依赖信息:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
这个依赖递归的将以下两个库添加到类路径中:
- jackson-annotations
- jackson-core
我们只需要使用Maven中心仓库的jackson-databind中的最新版本。
3. 使用对象映射器进行读写
我们从最简单的读写操作开始。
对象映射器的简单读取API是一个很好的切入点,我们可以用它来分析JSON内容,或者把JSON反序列化成Java对象。
另一方面,我们可以使用读取值API来把Java对象序列化成JSON格式的输出。
我们将会使用带有两个字段的“汽车”作为示例代码,来演示序列化和反序列化功能:
public class Car {
private String color;
private String type;
// standard getters setters
}
3.1 Java 对象到 JSON
先来看第一个示例,使用对象映射器的写方法来实现Java对象到JSON:
ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("yellow", "renault");
objectMapper.writeValue(new File("target/car.json"), car);
上面代码输出的信息将会是:
{"color":"yellow","type":"renault"}
对象映射器的writeValueAsString()
方法和writeValueAsBytes()
方法会从Java对象生成JSON,并且以字符串或二进制数组的方式返回生成的JSON:
String carAsString = objectMapper.writeValueAsString(car);
3.2 JSON到Java对象
在这个示例中,使用对象映射器类来把JSON字符串转化为Java对象:
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Car car = objectMapper.readValue(json, Car.class);
这个读方法也支持其他输入形式,如文件:
Car car = objectMapper.readValue(new File("src/test/resources/json_car.json"), Car.class);
或者URL:
Car car = objectMapper.readValue(new URL("file:src/test/resources/json_car.json"), Car.class);
3.3 JSON 到 Jackson JsonNode
除了转化为对象,我们也可以把JSON数据转化为JSON节点对象,这样就可以在不创建具体类的情况下,直接从某个JSON对象中获得某个字段的数据:
String json = "{ \"color\" : \"Black\", \"type\" : \"FIAT\" }";
JsonNode jsonNode = objectMapper.readTree(json);
String color = jsonNode.get("color").asText();
// Output: color -> Black
3.4 根据JSON数组字符串创建Java列表
我们可以使用类型引用(TypeReference),把JSON从数组格式转化为Java对象列表:
String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]";
List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>(){});
3.5 从JSON字符串创建Java映射(Map)
相似的,我们可以把JSON解析成一个Java映射:
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Map<String, Object> map = objectMapper.readValue(json, new TypeReference<Map<String,Object>>(){});
4. 高级特性
Jackson库的强大功能之一,就是高度自由定制的序列化和反序列化方法。
在这个版块,我们将会学习一些高级特性,它允许你输入(或输出)的JSON和生成(或消耗)的对象不同。
4.1 配置序列化和反序列化特性
当我们把JSON对象转化为Java类时,如果JSON字符串有一些新字段,在默认设置下会导致异常:
String jsonString = "{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" : \"1970\" }";
上面示例中的JSON字符串,如果使用默认的解析设置,在转化为Java对象Car时就会出现“未识别的属性异常”(UnrecognizedPropertyException
)。
通过配置方法,我们可以扩展默认的进程,实现忽略空字段。
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Car car = objectMapper.readValue(jsonString, Car.class);
JsonNode jsonNodeRoot = objectMapper.readTree(jsonString);
JsonNode jsonNodeYear = jsonNodeRoot.get("year");
String year = jsonNodeYear.asText();
另一个基于FAIL_ON_NULL_FOR_PRIMITIVES
的选项定义了“是否允许主键空值”:
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
相似地,FAIL_ON_NUMBERS_FOR_ENUM
选项控制“枚举值是否允许序列化、反序列化成数字”
objectMapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, false);
你可以在官方网站中找到序列化和反序列化的详尽表单
4.2 创建用户自定义的序列化器和反序列化器
对象映射器类的另一个重要特性是:允许注册一个用户自定义的序列化器和反序列化器。
自定义的序列化器和反序列化器在某些情况下非常有用,比如输入或输出的JSON和需要比序列化或反序列化的JAVA类结构不同。
下面是一个自定义JSON序列化器的例子:
public class CustomCarSerializer extends StdSerializer<Car> {
public CustomCarSerializer() {
this(null);
}
public CustomCarSerializer(Class<Car> t) {
super(t);
}
@Override
public void serialize(
Car car, JsonGenerator jsonGenerator, SerializerProvider serializer) {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("car_brand", car.getType());
jsonGenerator.writeEndObject();
}
}
这个自定义的序列化器可以像这样被调用:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module =
new SimpleModule("CustomCarSerializer", new Version(1, 0, 0, null, null, null));
module.addSerializer(Car.class, new CustomCarSerializer());
mapper.registerModule(module);
Car car = new Car("yellow", "renault");
String carJson = mapper.writeValueAsString(car);
此时,客户端将会收到类似这样的信息:
var carJson = {"car_brand":"renault"}
接下来,这是一个自定义JSON反序列化的例子:
public class CustomCarDeserializer extends StdDeserializer<Car> {
public CustomCarDeserializer() {
this(null);
}
public CustomCarDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Car deserialize(JsonParser parser, DeserializationContext deserializer) {
Car car = new Car();
ObjectCodec codec = parser.getCodec();
JsonNode node = codec.readTree(parser);
// try catch block
JsonNode colorNode = node.get("color");
String color = colorNode.asText();
car.setColor(color);
return car;
}
}
这个自定义的反序列化可以像这样被调用:
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
ObjectMapper mapper = new ObjectMapper();
SimpleModule module =
new SimpleModule("CustomCarDeserializer", new Version(1, 0, 0, null, null, null));
module.addDeserializer(Car.class, new CustomCarDeserializer());
mapper.registerModule(module);
Car car = mapper.readValue(json, Car.class);
4.3 处理日期格式
java.util.Date
这个类提供的默认的序列化器,在处理日期时会提供数字形式,例如时间戳(时间戳是一个数字,UTC时间是从1970年1月1日开始经过的毫秒数)。但是这种格式对于用户的可读性比较长,并且需要进一步转化格式才能变成用户可以阅读的格式。
我们把刚才用到的这个Car类,包装到一个带有datePurchased
属性的Request
类中(装饰器模式):
public class Request
{
private Car car;
private Date datePurchased;
// standard getters setters
}
为了控制字符串格式,比如设置为“yyyy-MM-dd HH:mm” (年-月-日 时:分),可以使用下面的代码:
ObjectMapper objectMapper = new ObjectMapper();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
objectMapper.setDateFormat(df);
String carAsString = objectMapper.writeValueAsString(request);
// output: {"car":{"color":"yellow","type":"renault"},"datePurchased":"2016-07-03 11:43 AM CEST"}
如果要了解更多使用Jackson序列化时间的方法,请点击链接
4.4 处理集合
DeserializationFeature
类提供了另一个很小但是很有用的特性,那就是允许从JSON数组生成我们想要的类型的集合。
例如,我们可以生成这样一个数组类型的结果:
String jsonCarArray =
"[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
Car[] cars = objectMapper.readValue(jsonCarArray, Car[].class);
// print cars
或者列表形式:
String jsonCarArray =
"[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]";
ObjectMapper objectMapper = new ObjectMapper();
List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>(){});
// print cars
这里有用Jackson处理集合的更多信息。
5. 结论
Jackson是一个成熟稳定的JSON序列化、反序列化的Java库。对象映射器API提供了直接的方式,去灵活的解析和生成JSON对象的方式。
本文讨论了一些知名度非常高的主要特性。
源代码在Github上提供。