Jackson之ObjectMapper对象的使用

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项目中, 所以应用中要添加相关项目路径或依赖

 
  1. <dependency>

  2. <groupId>com.fasterxml.jackson.core</groupId>

  3. <artifactId>jackson-core</artifactId>

  4. <version>2.9.6</version>

  5. </dependency>

  6. <dependency>

  7. <groupId>com.fasterxml.jackson.core</groupId>

  8. <artifactId>jackson-annotations</artifactId>

  9. <version>2.9.6</version>

  10. </dependency>

  11. <dependency>

  12. <groupId>com.fasterxml.jackson.core</groupId>

  13. <artifactId>jackson-databind</artifactId>

  14. <version>2.9.6</version>

  15. </dependency>

3 Jackson ObjectMapper对象示例

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

 
  1. public class Car {

  2. private String brand = null;

  3. private int doors = 0;

  4. public String getBrand() { return this.brand; }

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

  6. public int getDoors() { return this.doors; }

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

  8. }

  9. ...........................................................

  10. ObjectMapper objectMapper = new ObjectMapper();

  11. String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";

  12. try {

  13. Car car = objectMapper.readValue(carJson, Car.class);

  14. System.out.println("car brand = " + car.getBrand());

  15. System.out.println("car doors = " + car.getDoors());

  16. } catch (IOException e) {

  17. e.printStackTrace();

  18. }

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

反序列化

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

 
  1. ObjectMapper objectMapper = new ObjectMapper();

  2. String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 4 }";

  3. Reader reader = new StringReader(carJson);

  4. Car car = objectMapper.readValue(reader, Car.class);

从JSON文件读取Java对象

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

 
  1. ObjectMapper objectMapper = new ObjectMapper();

  2. File file = new File("data/car.json");

  3. Car car = objectMapper.readValue(file, Car.class);

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

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

 
  1. ObjectMapper objectMapper = new ObjectMapper();

  2. URL url = new URL("file:data/car.json");

  3. Car car = objectMapper.readValue(url, Car.class);

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

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

返回目录

 
  1. ObjectMapper objectMapper = new ObjectMapper();

  2. InputStream input = new FileInputStream("data/car.json");

  3. Car car = objectMapper.readValue(input, Car.class);

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

返回目录

 
  1. ObjectMapper objectMapper = new ObjectMapper();

  2. String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";

  3. byte[] bytes = carJson.getBytes("UTF-8");

  4. Car car = objectMapper.readValue(bytes, Car.class);

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

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

 
  1. String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";

  2. ObjectMapper objectMapper = new ObjectMapper();

  3. Car[] cars2 = objectMapper.readValue(jsonArray, Car[].class);

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

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

返回目录

 
  1. String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";

  2. ObjectMapper objectMapper = new ObjectMapper();

  3. 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中的键值对.

 
  1. String jsonObject = "{\"brand\":\"ford\", \"doors\":5}";

  2. ObjectMapper objectMapper = new ObjectMapper();

  3. 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使能可以让你忽略那些多余的属性

 
  1. objectMapper.configure(

  2. DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

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

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

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

 
  1. public class Car {

  2. private String brand = null;

  3. private int doors = 0;

  4. public String getBrand() { return this.brand; }

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

  6. public int getDoors(){ return this.doors; }

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

  8. }

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

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

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

 
  1. ObjectMapper objectMapper = new ObjectMapper();

  2. objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);

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

 
  1. ObjectMapper objectMapper = new ObjectMapper();

  2. objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);

  3. String carJson = "{ \"brand\":\"Toyota\", \"doors\":null }";

  4. Car car = objectMapper.readValue(carJson, Car.class);

抛出的异常信息如下:

 
  1. Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException:

  2. Cannot map `null` into type int

  3. (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)

  4. at [Source: (String)

  5. "{ "brand":"Toyota", "doors":null }"; line: 1, column: 29] (through reference chain: jackson.Car["doors"])

定制反序列化过程

返回目录

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

 
  1. String json = "{ \"brand\" : \"Ford\", \"doors\" : 6 }";

  2. //定义一个模型,该模型使用`CarDeserializer`作为反序列化器

  3. SimpleModule module = new SimpleModule("CarDeserializer", new Version(3, 1, 8, null, null, null));

  4. //指定反序列化器作用的Java类(Car类)

  5. module.addDeserializer(Car.class, new CarDeserializer(Car.class));

  6. ObjectMapper mapper = new ObjectMapper();

  7. //为ObjectMapper添加一个序列化/反序列化的模型

  8. mapper.registerModule(module);

  9. //反序列化Car类

  10. Car car = mapper.readValue(json, Car.class);

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

 
  1. import com.fasterxml.jackson.core.JsonParser;

  2. import com.fasterxml.jackson.core.JsonToken;

  3. import com.fasterxml.jackson.databind.DeserializationContext;

  4. import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

  5. import java.io.IOException;

  6. public class CarDeserializer extends StdDeserializer<Car> {

  7. public CarDeserializer(Class<?> vc) {

  8. super(vc);

  9. }

  10. @Override

  11. public Car deserialize(JsonParser parser, DeserializationContext deserializer) throws IOException {

  12. Car car = new Car();

  13. while(!parser.isClosed()){

  14. JsonToken jsonToken = parser.nextToken();

  15. if(JsonToken.FIELD_NAME.equals(jsonToken)){

  16. String fieldName = parser.getCurrentName();

  17. System.out.println(fieldName);

  18. jsonToken = parser.nextToken();

  19. if("brand".equals(fieldName)){

  20. car.setBrand(parser.getValueAsString());

  21. } else if ("doors".equals(fieldName)){

  22. car.setDoors(parser.getValueAsInt());

  23. }

  24. }

  25. }

  26. return car;

  27. }

  28. }

5 序列化

把对象序列化成JSON

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

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

  2. Car car = new Car();

  3. car.brand = "BMW";

  4. car.doors = 4;

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

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

 
  1. ObjectMapper objectMapper = new ObjectMapper();

  2. Car car = new Car();

  3. car.brand = "BMW";

  4. car.doors = 4;

  5. String json = objectMapper.writeValueAsString(car);

  6. System.out.println(json);

输出结果是:

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

自定义序列化过程

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

 
  1. //用Car类初始化序列化器CarSerializer

  2. CarSerializer carSerializer = new CarSerializer(Car.class);

  3. ObjectMapper objectMapper = new ObjectMapper();

  4. //新建一个序列化模型, 序列化器使用CarSerializer类

  5. SimpleModule module = new SimpleModule("CarSerializer", new Version(2, 1, 3, null, null, null));

  6. //注册carSerializer用于序列化Car

  7. module.addSerializer(Car.class, carSerializer);

  8. objectMapper.registerModule(module);

  9. Car car = new Car();

  10. car.setBrand("Mercedes");

  11. car.setDoors(5);

  12. String carJson = objectMapper.writeValueAsString(car);

输出结果是:

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

下面是CarSerializer类定义:

 
  1. import com.fasterxml.jackson.core.JsonGenerator;

  2. import com.fasterxml.jackson.databind.SerializerProvider;

  3. import com.fasterxml.jackson.databind.ser.std.StdSerializer;

  4. import java.io.IOException;

  5. public class CarSerializer extends StdSerializer<Car> {

  6. protected CarSerializer(Class<Car> t) {

  7. super(t);

  8. }

  9. @override

  10. public void serialize(Car car, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)

  11. throws IOException {

  12. jsonGenerator.writeStartObject();

  13. jsonGenerator.writeStringField("producer", car.getBrand());

  14. jsonGenerator.writeNumberField("doorCount", car.getDoors());

  15. jsonGenerator.writeEndObject();

  16. }

  17. }

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

6 Jackson的日期格式化

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

Date到long

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

 
  1. public class Transaction {

  2. private String type = null;

  3. private Date date = null;

  4. public Transaction() {

  5. }

  6. public Transaction(String type, Date date) {

  7. this.type = type;

  8. this.date = date;

  9. }

  10. public String getType() {

  11. return type;

  12. }

  13. public void setType(String type) {

  14. this.type = type;

  15. }

  16. public Date getDate() {

  17. return date;

  18. }

  19. public void setDate(Date date) {

  20. this.date = date;

  21. }

  22. }

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

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

  2. ObjectMapper objectMapper = new ObjectMapper();

  3. String output = objectMapper.writeValueAsString(transaction);

  4. System.out.println(output);

序列化结果:

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

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

Date到String

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

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

  2. objectMapper2.setDateFormat(dateFormat);

  3. String output2 = objectMapper2.writeValueAsString(transaction);

  4. 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树模型示例

返回目录

 
  1. String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";

  2. ObjectMapper objectMapper = new ObjectMapper();

  3. try {

  4. JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);

  5. } catch (IOException e) {

  6. e.printStackTrace();

  7. }

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

 
  1. String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";

  2. ObjectMapper objectMapper = new ObjectMapper();

  3. try {

  4. JsonNode jsonNode = objectMapper.readTree(carJson);

  5. } catch (IOException e) {

  6. e.printStackTrace();

  7. }

JsonNode类

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

 
  1. String carJson =

  2. "{ \"brand\" : \"Mercedes\", \"doors\" : 5," +

  3. " \"owners\" : [\"John\", \"Jack\", \"Jill\"]," +

  4. " \"nestedObject\" : { \"field\" : \"value\" } }";

  5. ObjectMapper objectMapper = new ObjectMapper();

  6. try {

  7. JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);

  8. JsonNode brandNode = jsonNode.get("brand");

  9. String brand = brandNode.asText();

  10. System.out.println("brand = " + brand);

  11. JsonNode doorsNode = jsonNode.get("doors");

  12. int doors = doorsNode.asInt();

  13. System.out.println("doors = " + doors);

  14. JsonNode array = jsonNode.get("owners");

  15. JsonNode jsonNode = array.get(0);

  16. String john = jsonNode.asText();

  17. System.out.println("john = " + john);

  18. JsonNode child = jsonNode.get("nestedObject");

  19. JsonNode childField = child.get("field");

  20. String field = childField.asText();

  21. System.out.println("field = " + field);

  22. } catch (IOException e) {

  23. e.printStackTrace();

  24. }

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

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值