1. 概述

本教程将介绍Jackson的ObjectMapper类,学习如何在JSON字符串和Java对象之间相互转换。

想要学习更多Jackson相关知识,请访问Jackson 系列教程

2. Maven 依赖

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

此依赖将引入下面2个库:

  1. jackson-annotations
  2. jackson-core

3. ObjectMapper读写操作

使用ObjectMapper的readValue方法,可以把JSON字符串转换为Java对象。

同样,使用writeValue方法,可以将Java对象序列化为JSON。

整篇文章中,我们将使用下面的Car类作为序列化和反序列化的对象:

    public class Car {

        private String color;
        private String type;
    
        // standard getters setters
    }

3.1. Java 对象转 JSON

第一个序列化例子,我们将使用writeValue方法把Java对象转换为JSON:

ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("yellow", "renault");
objectMapper.writeValue(new File("target/car.json"), car);

上面文件输出结果为:

    {"color":"yellow","type":"renault"}

如果用ObjectMapper的writeValueAsStringwriteValueAsBytes方法,返回的是JSON字符串和byte数组。

    String carAsString = objectMapper.writeValueAsString(car);

3.2. JSON 转 Java对象

下面例子中,将JSON字符串转为Java对象:

    String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
    Car car = objectMapper.readValue(json, Car.class);    

readValue()同时支持其他输入类型,比如JSON文件。

    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 转 JsonNode

另外,可以将JSON解析为JsonNode对象,从节点中读取数据:

String json = "{ \"color\" : \"Black\", \"type\" : \"FIAT\" }";
JsonNode jsonNode = objectMapper.readTree(json);
String color = jsonNode.get("color").asText();
// Output: color -> Black

3.4. JSON数组转Java List

我们可以使用TypeReference将数组形式的JSON解析为Java List对象:

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 Map对象:

String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Map<String, Object> map 
  = objectMapper.readValue(json, new TypeReference<Map<String,Object>>(){});

4. 高级功能

Jackson 最大的优势之一是高度可定制,下面我们将介绍Jackson的高级用法。

4.1. 序列化和反序列化配置

默认情况下,如果JSON字符串中有Java类中未定义的字段,反序列化时会抛异常。

String jsonString 
  = "{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" : \"1970\" }";

例如上面的JSON中包含 Car 类中未定义的 year 字段,会导致解析时出现 UnrecognizedPropertyException 异常。

我们可以通过 configure 方法,告诉Jackson忽略未知字段。

    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,设置是否允许基础类型字段为null:

    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. 自定义序列化和反序列化器

另一个重要功能是,ObjectMapper 支持我们自定义序列化和反序列化过程。

下面是一个自定义序列化器的例子:

    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);

序列化后的JSON输出如下:

    var carJson = {"car_brand":"renault"}

下面是反序列化的的示例:

    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. 处理日期格式

Date 类型默认序列化为时间戳(1970到现在的秒数),这不方便人类读取,下面我们转换为人类可读的格式显示。

    public class Request 
    {
        private Car car;
        private Date datePurchased;
    
        // standard getters setters
    }

使用下面的代码片段,将Date转为 yyyy-MM-dd HH:mm a z 格式:

    ObjectMapper objectMapper = new ObjectMapper();
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
    objectMapper.setDateFormat(df);
    String carAsString = objectMapper.writeValueAsString(request);
    // 输出: {"car":{"color":"yellow","type":"renault"},"datePurchased":"2016-07-03 11:43 AM CEST"}

有关Date序列化更多高级用法请阅读这篇文章.

4.4. 处理Collection类型

反序列化为数组:

    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

或者 List:

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是一个健壮成熟的Java JSON序列化/反序列化库。ObjectMapper API提供了一种简单灵活的方式来解析和序列化JSON。本文讨论了Jackson最热门的主要功能。

本文源代码,可以从GitHub上获取。