Jackson之ObjectMapper对象的使用


文章目录

    • 1 简介
    • 2 Jackson的数据绑定
    • 3 Jackson ObjectMapper对象示例
    • 4 反序列化
      • ObjectMapper从JSON属性匹配到Java属性的过程
      • 从JSON字符串读取Java对象
      • 从JSON Reader对象读取Java对象
      • 从JSON文件读取Java对象
      • 从URL获取JSON数据读取Java对象
      • 从Java InputStream获取JSON数据读取Java对象
      • 从字节数组获取JSON数据读取Java对象
      • 从JSON数组字符串读取Java对象数组
      • 从JSON数组字符串读取Java List对象
      • 从JSON字符串读取Java Map对象
      • 特殊情况
        • 忽略Java对象没有的JSON属性
        • JSON属性值为NULL且对应Java原生类型产生的异常
      • 定制反序列化过程
    • 5 序列化
      • 把对象序列化成JSON
      • 自定义序列化过程
    • 6 Jackson的日期格式化
      • Date到long
      • Date到String
    • 7 Jackson的树模型
      • Jackson树模型示例
      • JsonNode类

1 简介

Jackson ObjectMapper类(com.fasterxml.jackson.databind.ObjectMapper)是使用Jackson解析JSON最简单的方法。Jackson ObjectMapper可以从字符串、流或文件解析JSON,并创建Java对象或对象图来表示已解析的JSON。将JSON解析为Java对象也称为从JSON反序列化Java对象
Jackson ObjectMapper也可以从Java对象创建JSON. 从Java对象生成JSON的过程也被称为序列化Java对象到JSON
Jackson对象映射器(Object Mapper)可以把JSON解析为用户自定义类对象, 或者解析为JSON内置的树模型的对象(详见下文)

2 Jackson的数据绑定

ObjectMapper对象定义位于Jackson Databind项目中, 所以应用中要添加相关项目路径或依赖

<dependency>
  <groupId>com.fasterxml.jackson.coregroupId>
  <artifactId>jackson-coreartifactId>
  <version>2.9.6version>
dependency>

<dependency>
  <groupId>com.fasterxml.jackson.coregroupId>
  <artifactId>jackson-annotationsartifactId>
  <version>2.9.6version>
dependency>

<dependency>
  <groupId>com.fasterxml.jackson.coregroupId>
  <artifactId>jackson-databindartifactId>
  <version>2.9.6version>
dependency>

3 Jackson ObjectMapper对象示例

返回目录
一个简单的例子:

public class Car {
    private String brand = null;
    private int doors = 0;

    public String getBrand() { return this.brand; }
    public void   setBrand(String brand){ this.brand = brand;}

    public int  getDoors() { return this.doors; }
    public void setDoors (int doors) { this.doors = doors; }
}
...........................................................
ObjectMapper objectMapper = new ObjectMapper();
String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
try {
    Car car = objectMapper.readValue(carJson, Car.class);
    System.out.println("car brand = " + car.getBrand());
    System.out.println("car doors = " + car.getDoors());
} catch (IOException e) {
    e.printStackTrace();
}

如例所见, readValue()方法的第一个参数是JSON数据源(字符串, 流或者文件), 第二个参数是解析目标Java类, 这里传入的是Car.class

4 反序列化

ObjectMapper从JSON属性匹配到Java属性的过程

返回目录
要想从JSON正确的读取到Java对象, 那么了解Jackson是怎么从JSON对象映射到Java对象就非常重要
默认情况下, Jackson映射一个JSON对象的属性到Java对象, 是用JSON属性的名字在Java对象中查找匹配的getter/setter方法. Jackson移除了getter/setter方法名中的get/set字符部分, 并把方法名剩余字符的第一个字符小写, 得到的就是JSON属性名.
在上一节的例子中, JSON属性的名称是brand, 匹配了Java类中名为getBrand()/setBrand()getter/setter方法. JSON属性的名称是engineNumber, 匹配了getEngineNumber()/setEngineNumber()
如果想用其他方法来匹配JSON对象属性和Java对象属性, 可以自定义序列化/反序列化过程, 或者使用其他的Jackson注解

从JSON字符串读取Java对象

返回目录
从JSON字符串读取Java对象非常简单, 前面的例子已经展示了具体过程. JSON字符串作为第一个参数传递给ObjectMapper.readValue()方法, 参见前例

从JSON Reader对象读取Java对象

返回目录
也可以通过Reader实例从JSON中读取一个Java对象

ObjectMapper objectMapper = new ObjectMapper();
String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 4 }";
Reader reader = new StringReader(carJson);
Car car = objectMapper.readValue(reader, Car.class);

从JSON文件读取Java对象

返回目录
也可以通过FileReader实例从读取JSON(替换上例中的StringReader即可), 当然, 也可以直接使用File对象

ObjectMapper objectMapper = new ObjectMapper();
File file = new File("data/car.json");
Car car = objectMapper.readValue(file, Car.class);

从URL获取JSON数据读取Java对象

返回目录
可以通过URL(java.net.URL)获取JSON数据后读取Java对象

ObjectMapper objectMapper = new ObjectMapper();
URL url = new URL("file:data/car.json");
Car car = objectMapper.readValue(url, Car.class);

这个例子使用了文件URL, 当然也可以使用HTTP URL

从Java InputStream获取JSON数据读取Java对象

返回目录

ObjectMapper objectMapper = new ObjectMapper();
InputStream input = new FileInputStream("data/car.json");
Car car = objectMapper.readValue(input, Car.class);

从字节数组获取JSON数据读取Java对象

返回目录

ObjectMapper objectMapper = new ObjectMapper();
String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
byte[] bytes = carJson.getBytes("UTF-8");
Car car = objectMapper.readValue(bytes, Car.class);

从JSON数组字符串读取Java对象数组

返回目录
ObjectMapper也可以从JSON数组字符串中读取一组Java对象

String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";
ObjectMapper objectMapper = new ObjectMapper();
Car[] cars2 = objectMapper.readValue(jsonArray, Car[].class);

注意Car类数组作为readValue()方法第二个参数的传入方式, 告诉ObjectMapper期望从JSON读取一组Car实例
当然了, JSON源不仅是字符串, 也可以是文件, URL, InputStream, Reader等等

从JSON数组字符串读取Java List对象

返回目录

String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";
ObjectMapper objectMapper = new ObjectMapper();
List<Car> cars1 = objectMapper.readValue(jsonArray, new TypeReference<List<Car>>(){});

注意传递给readValue()TypeReference类型参数. 这个参数告诉Jackson读取一"列"Car对象

  • Jackson通过反射来生成Java对象, 但是模板会擦除类型, 所以这里用TypeReference进行包装

从JSON字符串读取Java Map对象

返回目录
ObjectMapper可以从JSON字符串读取一个Java Map, 当你不知道要提取的JSON的格式的时候非常有用. 一般会把JSON对象读取到一个JavaMap对象中. 每一个JSON对象的属性都会变成JavaMap中的键值对.

String jsonObject = "{\"brand\":\"ford\", \"doors\":5}";
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> jsonMap = objectMapper.readValue(jsonObject, new TypeReference<Map<String,Object>>(){});

特殊情况

忽略Java对象没有的JSON属性

返回目录
有时候你要读取的JSON数据的属性要多于你的Java对象的属性, 默认情况下Jackson这时会抛出异常, 含义是无法在Java对象中找到未知属性XXX
但是, 我们有时候又需要允许JSON属性多于要产生的Java对象的属性. 比如, 你想从一个REST服务获取JSON数据, 但是它包含的内容远多于你所需要的. 这是, 通过配置Jackson的Feature使能可以让你忽略那些多余的属性

objectMapper.configure(
    DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

注意 : 也可以用ObjectMapper.enabled()/disabled()方法进行配置

JSON属性值为NULL且对应Java原生类型产生的异常

返回目录
可以配置ObjectMapperFeature, 使其在JSON字符串包含的属性值是null, 且该属性对应的Java对象的属性是原生类型(primitive type: int, long, float, double等)时, 反序列化失败抛出异常.
修改Car类的定义:

public class Car {
    private String brand = null;
    private int doors = 0;

    public String getBrand() { return this.brand; }
    public void   setBrand(String brand){ this.brand = brand;}

    public int  getDoors(){ return this.doors; }
    public void setDoors (int doors) { this.doors = doors; }
}

注意属性doors的类型是Java原生类型int(而不是object)
现在假设有一个JSON字符串要匹配Car类:

 "brand":"Toyota", "doors":null }

注意属性doors的值是null
Java中的原生数据类型的值不能是null. 所以ObjectMapper默认会忽略null值的原生类型的属性, 不过, 你也可以配置ObjectMapperFeature让它抛出异常, 如下:

ObjectMapper objectMapper = new ObjectMapper();

objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);

通过设置FAIL_ON_NULL_FOR_PRIMITIVES属性是true, 你会在试图把null值的JSON属性解析为Java原生类型属性时抛出异常,如下:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);
String carJson = "{ \"brand\":\"Toyota\", \"doors\":null }";
Car car = objectMapper.readValue(carJson, Car.class);

抛出的异常信息如下:

Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException:
    Cannot map `null` into type int
    (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)
 at [Source: (String)
    "{ "brand":"Toyota", "doors":null }"; line: 1, column: 29] (through reference chain: jackson.Car["doors"])

定制反序列化过程

返回目录

有时候, 我们需要用不同于ObjectMapper默认的方式来反序列化JSON字符串到一个Java对象. 这时, 可以添加一个自定义序列化器(custom deserializer)到ObjectMapper, 让它可以按照你设定的方式进行反序列化.
下面是用ObjectMapper注册和使用自定义反序列化器的示例:

String json = "{ \"brand\" : \"Ford\", \"doors\" : 6 }";
//定义一个模型,该模型使用`CarDeserializer`作为反序列化器
SimpleModule module = new SimpleModule("CarDeserializer", new Version(3, 1, 8, null, null, null));
//指定反序列化器作用的Java类(Car类)
module.addDeserializer(Car.class, new CarDeserializer(Car.class));

ObjectMapper mapper = new ObjectMapper();
//为ObjectMapper添加一个序列化/反序列化的模型
mapper.registerModule(module);
//反序列化Car类
Car car = mapper.readValue(json, Car.class);

下面是这里的反序列化器CarDeserializer的定义:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;

public class CarDeserializer extends StdDeserializer<Car> {

    public CarDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Car deserialize(JsonParser parser, DeserializationContext deserializer) throws IOException {
        Car car = new Car();
        while(!parser.isClosed()){
            JsonToken jsonToken = parser.nextToken();

            if(JsonToken.FIELD_NAME.equals(jsonToken)){
                String fieldName = parser.getCurrentName();
                System.out.println(fieldName);

                jsonToken = parser.nextToken();

                if("brand".equals(fieldName)){
                    car.setBrand(parser.getValueAsString());
                } else if ("doors".equals(fieldName)){
                    car.setDoors(parser.getValueAsInt());
                }
            }
        }
        return car;
    }
}

5 序列化

把对象序列化成JSON

返回目录
ObjectMapper实例也可以用来从一个对象生成JSON数据. 可以使用下列方法:

  • writeValue()
  • writeValueAsString()
  • writeValueAsBytes()
    下面是一个把Car对象序列化为JSON的例子:
ObjectMapper objectMapper = new ObjectMapper();

Car car = new Car();
car.brand = "BMW";
car.doors = 4;

objectMapper.writeValue(new FileOutputStream("data/output-2.json"), car);

这里首先创建了一个ObjectMapper实例和一个Car实例, 然后调用ObjectMapperwriteValue()方法把Car实例转换为JSON后输出到FileOutputStream
writeValueAsString()writeValueAsBytes()也可以从对象生成JSON, 并且返回一个StringByte数组的JSON, 如下:

ObjectMapper objectMapper = new ObjectMapper();

Car car = new Car();
car.brand = "BMW";
car.doors = 4;

String json = objectMapper.writeValueAsString(car);
System.out.println(json);

输出结果是:

{"brand":"BMW","doors":4}

自定义序列化过程

返回目录
有时候你又不想用Jackson默认的序列化过程把一个Java对象变成JSON. 比如, 你可能在JSON中使用和Java对象不同的属性名称, 或者你想完全忽略某些属性
Jackson可以为ObjectMapper设置一个自定义序列化器(custom serializer) 这个序列化器会注册为序列化某个实际的类, 然后在ObjectMapper执行序列化时调用, 比如序列化Car对象
下例展示了如何为Car类注册一个自定义序列化器:

//用Car类初始化序列化器CarSerializer 
CarSerializer carSerializer = new CarSerializer(Car.class);
ObjectMapper objectMapper = new ObjectMapper();
//新建一个序列化模型, 序列化器使用CarSerializer类
SimpleModule module = new SimpleModule("CarSerializer", new Version(2, 1, 3, null, null, null));
//注册carSerializer用于序列化Car
module.addSerializer(Car.class, carSerializer);

objectMapper.registerModule(module);

Car car = new Car();
car.setBrand("Mercedes");
car.setDoors(5);

String carJson = objectMapper.writeValueAsString(car);

输出结果是:

{"producer":"Mercedes","doorCount":5}

下面是CarSerializer类定义:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;

public class CarSerializer extends StdSerializer<Car> {

    protected CarSerializer(Class<Car> t) {
        super(t);
    }
	@override
    public void serialize(Car car, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
            throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("producer", car.getBrand());
        jsonGenerator.writeNumberField("doorCount", car.getDoors());
        jsonGenerator.writeEndObject();
    }
}

注意 : serialize的第二个参数是一个JsonGenerator实例, 你可以使用该实例序列化一个对象, 这里是Car对象

6 Jackson的日期格式化

返回目录
默认情况下, Jackson会把一个java.util.Date对象序列化为一个long型值, 也就是从1970-01-1到现在的毫秒数, 当然, Jackson也支持把日期格式化为字符串.

Date到long

返回目录
首先看看Jackson默认的把Date序列化为long的过程, 如下是一个包含Date类型属性的Java类:

public class Transaction {
    private String type = null;
    private Date date = null;

    public Transaction() {
    }

    public Transaction(String type, Date date) {
        this.type = type;
        this.date = date;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}

ObjectMapper序列化Transaction类对象的过程和其他Java对象一样:

Transaction transaction = new Transaction("transfer", new Date());

ObjectMapper objectMapper = new ObjectMapper();
String output = objectMapper.writeValueAsString(transaction);

System.out.println(output);

序列化结果:

{"type":"transfer","date":1516442298301}

属性date被序列化为了一个long型整数

Date到String

返回目录
long型的序列化可读性很差, 因而Jackson提供了文本格式的日期序列化. 可以为ObjectMapper指定一个SimpleDateFormat实例来带格式提取Jackson日期

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
objectMapper2.setDateFormat(dateFormat);

String output2 = objectMapper2.writeValueAsString(transaction);
System.out.println(output2);

序列化结果:

{"type":"transfer","date":"2018-01-20"}

可以看到, 属性date被格式化为一个字符串

7 Jackson的树模型

返回目录
Jackson内置了一个树模型(tree model)可以用来表示一个JSON对象. 这个树模型非常有用, 比如你不知道收到的JSON数据的结构, 或者你不想新建一个Java类来表示这个JSON数据, 或者你想在使用或者转发JSON数据前对它进行操作.
Jackson的树模型由JsonNode类实现. 你可以用ObjectMapper实例把JSON解析为JsonNode模型, 就像反序列化出一个自定义类对象一样.
下面举例示范JsonNode的用法.

Jackson树模型示例

返回目录

String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
ObjectMapper objectMapper = new ObjectMapper();
try {
    JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);
} catch (IOException e) {
    e.printStackTrace();
}

这里, 我们用JsonNode.class代替了Car.class对JSON字符串进行解析.
ObjectMapper提供了更简单的方法得到JsonNode : readTree(), 该方法返回的就是一个JsonNode, 如下:

String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
ObjectMapper objectMapper = new ObjectMapper();
try {
    JsonNode jsonNode = objectMapper.readTree(carJson);
} catch (IOException e) {
    e.printStackTrace();
}

JsonNode类

返回目录
JsonNode提供了非常灵活和动态访问的方式, 可以像访问Java对象那样导航浏览JSON
如果把JSON解析为一个JsonNode实例(或一个JsonNode实例树), 就可以浏览JsonNode树模型, 如下例中用JsonNode访问JSON中的属性,数组,对象等等:

String carJson =
        "{ \"brand\" : \"Mercedes\", \"doors\" : 5," +
        "  \"owners\" : [\"John\", \"Jack\", \"Jill\"]," +
        "  \"nestedObject\" : { \"field\" : \"value\" } }";
ObjectMapper objectMapper = new ObjectMapper();
try {
    JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);

    JsonNode brandNode = jsonNode.get("brand");
    String brand = brandNode.asText();
    System.out.println("brand = " + brand);

    JsonNode doorsNode = jsonNode.get("doors");
    int doors = doorsNode.asInt();
    System.out.println("doors = " + doors);

    JsonNode array = jsonNode.get("owners");
    JsonNode jsonNode = array.get(0);
    String john = jsonNode.asText();
    System.out.println("john  = " + john);

    JsonNode child = jsonNode.get("nestedObject");
    JsonNode childField = child.get("field");
    String field = childField.asText();
    System.out.println("field = " + field);

} catch (IOException e) {
    e.printStackTrace();
}

上面的JSON中包含了一个名为owner的数组属性和一个名为nestedObject的对象属性
不管是访问一个属性, 还是数组, 还是内嵌的对象, 都可以用JsonNodeget()方法. 为get()传入一个字符串就可以访问一个JsonNode实例的一个属性, 传入一个索引就是访问JsonNode实例代表的数组, 索引代表了你要访问的数组元素位置.

你可能感兴趣的:(Java基础,Jackson)