【Jackson 】 - ObjectMapper对象详解

Jackson ObjectMapper对象详解

1.概述

本文将全面深入的学习Jackson ObjectMapper类,以及如何将Java对象序列化为JSON或者将JSON字符串反序列化为Java对象。

2.添加Jackson依赖

首先,在_pom.xml_中添加jackson依赖:

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

如果不是maven项目,需要将以下jar包添加到类依赖中:

  1. jackson-annotations-2.9.8.jar
  2. jackson-core-2.9.8.jar
  3. jackson-databind-2.9.8.jar

注意:2.9.x 版本存在安全漏洞,建议使用2.10.x 以上版本。

3.使用ObjectMapper进行读写

我们可以使用ObjectMapperreadValue 方法将JSON内容反序列化为Java对象。同样,我们可也以使用ObjectMapperwriteValue 方法将Java对象序列化为JSON。

我们将使用下面的Car类对象作为实例来序列化或反序列化:

public class Car {  
  
    private String name;  
    private String color;  
    // standard getters setters  
}  

3.1.Java对象转JSON

使用_ObjectMapper_类的_writeValue_方法将Java对象序列到JSON文件中:

File resultFile = new File("target/car.json");  
ObjectMapper objectMapper = new ObjectMapper();  
Car car = new Car("BMW", "Black");  
objectMapper.writeValue(resultFile, car);  

上述实例输出结果是:

{"name":"BMW","color":"Black"}  

如果希望将Java对象转换为字符串或字节数组,可以使用ObjectMapper类的writeValueAsStringwriteValueAsBytes方法:

String carAsString = objectMapper.writeValueAsString(car);  

3.2. JSON转Java对象

使用_ObjectMapper_类将JSON字符串转换为Java对象:

@Test  
public void jsonToJavaObject() throws Exception {  
    ObjectMapper objectMapper = new ObjectMapper();  
    String jsonString = "{\"name\":\"BMW\",\"color\":\"Black\"}";  
    Car car = objectMapper.readValue(jsonString, Car.class);  
    assertNotNull(car);  
    System.out.println(car);  
}  

readValue()函数还接受其他形式的输入,比如从JSON字符串的文件中读取数据:

@Test  
public void jsonFileToJavaObject() throws Exception {  
    File resource = new File("target/car.json");  
    ObjectMapper objectMapper = new ObjectMapper();  
    Car car = objectMapper.readValue(resource, Car.class);  
    System.out.println(car);  
}  

或从网络获取JSON字符串文件:

@Test  
public void fromURLToJavaObject() throws Exception {  
    URL resource = new URL("file:src/test/resources/json_car.json");  
  
    ObjectMapper objectMapper = new ObjectMapper();  
    Car car = objectMapper.readValue(resource, Car.class);  
    System.out.println(car);  
}  

3.3. JSON转JsonNode

可以将JSON解析为_JsonNode_对象,然后从JsonNode对象中获取数据:

@Test  
public void jsonToJsonNode() throws Exception {  
    ObjectMapper objectMapper = new ObjectMapper();  
    String jsonString = "{\"name\":\"BMW\",\"color\":\"Black\"}";  
    JsonNode jsonNode = objectMapper.readTree(jsonString);  
    assertNotNull(jsonNode);  
    System.out.println(jsonNode);  
    System.out.println(jsonNode.get("name"));  
    System.out.println(jsonNode.get("name").asText());  
}  
// Output:{"name":"BMW","color":"Black"}  “BMW" BMW  

3.4. JSON数组字符串解析为Java List

使用_TypeReference_将数组形式的JSON解析为Java List:

@Test  
public void jsonArrayToList() throws Exception {  
    String resourceJson = "[{ \"name\" : \"BMW\", \"color\" : \"Black\" }," +  
        " { \"name\" : \"toyota\", \"color\" : \"Red\" }]";  
  
    ObjectMapper objectMapper = new ObjectMapper();  
    List<Car> listCar = objectMapper.readValue(resourceJson, new TypeReference<List<Car>>() {  
  
    });  
    for (final Car car : listCar) {  
        System.out.println(car);  
    }  
}  

3.5. JSON字符串解析为Java Map

将JSON解析为Java Map

@Test  
public void jsonToMap() throws Exception {  
    String jsonString = "{\"name\":\"BMW\",\"color\":\"Black\"}";  
    ObjectMapper objectMapper = new ObjectMapper();  
    Map<String, Object> map = objectMapper.readValue(jsonString, new TypeReference<Map<String, Object>>() {  
    });  
    System.out.println(map);  
}  
//{name=BMW, color=Black}  

4.高级功能

Jackson库的最大优势之一就是高度可定制的序列化和反序列化。下面,我们将介绍一些高级功能。

4.1. 配置序列化或反序列化功能

将JSON对象转换为Java类时,如果JSON字符串中包含Java类中没有的字段,那么,默认解析过程将导致异常:

String jsonString = "{\"name\":\"BMW\",\"color\":\"Black\",\"price\":\"200000\"}";  

上面示例的默认解析过程中,将导致_UnrecognizedPropertyException_异常。

通过configure方法,我们可以扩展默认解析过程以忽略新字段,解决以上异常:

@Test  
public void failOnUnkownProperties() throws Exception {  
  
    String jsonCar = "{\"name\":\"BMW\",\"color\":\"Black\",\"price\":\"230000\"}";  
    ObjectMapper objectMapper = new ObjectMapper();  
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);  
    Car car = objectMapper.readValue(jsonCar, Car.class);  
    System.out.println(car);  
    JsonNode jsonNodeRoot = objectMapper.readTree(jsonCar);  
    JsonNode jsonNodePrice = jsonNodeRoot.get("price");  
    String price = jsonNodePrice.asText();  
    System.out.println(price);  
}  

另一个选项是基于_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);  

您可以在Jackson专辑中找到序列化和反序列化的相关文章。

4.2. 自定义序列化或反序列化

_ObjectMapper_类的另一个基本功能是能够自定义序列化器和反序列化器。当输入或输出的JSON结构和需要进行序列化或反序列化的Java类的结构不同时,自定义序列化器和反序列化器非常有用。

以下是自定义JSON序列化代码:

public class CustomCarSerializer extends StdSerializer<Car> {  
  
    public CustomCarSerializer() {  
        this(null);  
    }  
  
    public CustomCarSerializer(final Class<Car> t) {  
        super(t);  
    }  
  
    @Override  
    public void serialize( Car car,  JsonGenerator jsonGenerator,  SerializerProvider serializer) throws IOException {  
        jsonGenerator.writeStartObject();  
        jsonGenerator.writeStringField("name1: ", car.getName());  
        jsonGenerator.writeStringField("color1: ", car.getColor());  
        jsonGenerator.writeEndObject();  
    }  
}  

调用测试代码:

@Test  
public void customSerializer() throws Exception {  
    ObjectMapper mapper = new ObjectMapper();  
    SimpleModule serializerModule = new SimpleModule("CustomSerializer", new Version(1, 0, 0, null, null, null));  
    serializerModule.addSerializer(Car.class, new CustomCarSerializer());  
    mapper.registerModule(serializerModule);  
    Car car = new Car("BMW", "Black");  
    String carJson = mapper.writeValueAsString(car);  
    System.out.println(carJson);  
}  

输出结果:

{"name1: ":"BMW","color1: ":"Black"}  

以下是一个自定义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) throws IOException {  
        Car car = new Car();  
        ObjectCodec codec = parser.getCodec();  
        JsonNode node = codec.readTree(parser);  
        try {  
            JsonNode colorNode = node.get("color");  
            String color = colorNode.asText();  
            car.setColor(color);  
        } catch (final Exception e) {  
            Logger.debug("parse_exeption: unknown json.");  
        }  
        return car;  
    }  
}  

运行测试代码:

@Test  
public void customDeserializer() throws Exception {  
    ObjectMapper mapper = new ObjectMapper();  
    SimpleModule deserializerModule = new SimpleModule("CustomCarDeserializer", new Version(1, 0, 0, null, null, null));  
    deserializerModule.addDeserializer(Car.class, new CustomCarDeserializer());  
    mapper.registerModule(deserializerModule);  
    Car car = mapper.readValue(EXAMPLE_JSON, Car.class);  
    System.out.println(car);  
}  

4.3. 处理日期格式

java.util.Date的默认序列化产生一个数字,即epoch时间戳(自1970年1月1日以来,以毫秒为单位,UTC)。但这不是我们日常生活中可读的,需要进一步的转换才能按照我们可读的格式显示。

在这里我们新建一个Student类来演示日期格式处理:

public class Student {  
    Car car;  
    Date birthday;  
  
   //省略 getters setters  
}  

我们希望将日期格式设置为yyyy-MM-dd HH:mm:ss ,测试代码如下:

@Test  
public void dateFormat() throws Exception {  
    ObjectMapper objectMapper = new ObjectMapper();  
    Car car = new Car("BMW", "Black");  
    Student student = new Student();  
    student.setCar(car);  
    student.setBirthday(new Date());  
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    objectMapper.setDateFormat(df);  
    String carAsString = objectMapper.writeValueAsString(student);  
    System.out.println(carAsString);  
}  

输出结果如下:

{"car":{"name":"BMW","color":"Black"},"birthday":"2020-05-18 13:56:15"}  

要了解有关使用Jackson进行日期序列化的更多信息,请阅读我们更深入的文章。

4.4. 处理集合

DeserializationFeature类提供的另一个小而有用的功能是能够将JSON数组生成我们想要的集合类型。

例如,我们可以将结果生成为数组:

@Test  
public void jsonArrayToJavaArray() throws Exception {  
  
    String jsonArray = "[{ \"name\" : \"BMW\", \"color\" : \"Black\" }," +  
        " { \"name\" : \"toyota\", \"color\" : \"Red\" }]";  
  
    ObjectMapper objectMapper = new ObjectMapper();  
    objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);  
    Car[] cars = objectMapper.readValue(jsonArray, Car[].class);  
    System.out.println(car);  
}  

或者时List:

@Test  
public void jsonArrayToJavaList() throws Exception {  
  
    String jsonArray = "[{ \"name\" : \"BMW\", \"color\" : \"Black\" }," +  
        " { \"name\" : \"toyota\", \"color\" : \"Red\" }]";  
  
    ObjectMapper objectMapper = new ObjectMapper();  
    objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);  
    List<Car> listCar = objectMapper.readValue(jsonArray, new TypeReference<List<Car>>(){});  
    for (Car car : listCar) {  
        System.out.println(car);  
    }  
}

ObjectMapper 的构建器模式

到目前为止,我们已经了解了配置 ObjectMapper 实例的不同方法。在本节中, 我们将对 ObjectMapperBuilder 类进行原型设计,以创建 ObjectMapper 类的不可变实例。

ObjectMapperBuilder 类

让我们首先创建具有几个配置参数(即 enableIdentation、preserveOrder 和 dateFormat)的 ObjectMapperBuilder 类 :

public class ObjectMapperBuilder {
    private boolean enableIndentation;
    private boolean preserveOrder;
    private DateFormat dateFormat;
}

我们必须注意,ObjectMapper 实例有几种可能的配置。我们只关注对构建器进行原型设计的用例的可能配置的子集。
接下来,让我们添加一些方法,这些方法将允许我们在创建 ObjectMapper 类的实例时为构建器设置相应的配置属性:

ObjectMapperBuilder enableIndentation() {
    this.enableIndentation = true;
    return this;
}

ObjectMapperBuilder dateFormat() {
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
    simpleDateFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.of("Asia/Kolkata")));
    this.dateFormat = simpleDateFormat;
    return this;
}

ObjectMapperBuilder preserveOrder(boolean order) {
    this.preserveOrder = order;
    return this;
}

最后,让我们添加 build() 方法,以返回具有已配置参数的最终 ObjectMapper 实例 :

public ObjectMapper build() {
    ObjectMapper objectMapper = new ObjectMapper();

    objectMapper.configure(SerializationFeature.INDENT_OUTPUT, this.enableIndentation);
    objectMapper.setDateFormat(this.dateFormat);
    if (this.preserveOrder) {
        objectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS);
    }

    return objectMapper;
}

Builder 的实际应用

让我们首先在 ObjectMapperBuilder 类的帮助下创建 ObjectMapper 类的单个实例:

ObjectMapper mapper = new ObjectMapperBuilder()
  .enableIndentation()
  .dateFormat()
  .preserveOrder(true)
  .build();

现在,让我们定义 Car 类的实例及其序列化的 JSON 字符串:

Car givenCar = new Car("White", "Sedan");
String givenCarJsonStr = "{ \"color\" : \"White\", \"type\" : \"Sedan\" }";

接下来,让我们使用 mapper 对象反序列化 givenCarJsonStr:

Car actual = mapper.readValue(givenCarJsonStr, Car.class);
Assertions.assertEquals("White", actual.getColor());
Assertions.assertEquals("Sedan", actual.getType());

最后,让我们验证 Request 类实例的序列化流程 :

Request request = new Request();
request.setCar(givenCar);
Date date = new Date(1684909857000L);
request.setDatePurchased(date);

String actual = mapper.writeValueAsString(request);
String expected = "{\n" + "  \"car\" : {\n" + "    \"color\" : \"White\",\n" +
    "    \"type\" : \"Sedan\"\n" + "  },\n" + "  \"datePurchased\" : \"2023-05-24 12:00 PM IST\"\n" +
    "}";
Assertions.assertEquals(expected, actual);
posted @ 2023-01-30 17:01  明小子@  阅读(845)  评论(0)    收藏  举报