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内置的树模型的对象(详见下文)
ObjectMapper
对象定义位于Jackson Databind
项目中, 所以应用中要添加相关项目路径或依赖
com.fasterxml.jackson.core
jackson-core
2.9.6
com.fasterxml.jackson.core
jackson-annotations
2.9.6
com.fasterxml.jackson.core
jackson-databind
2.9.6
返回目录
一个简单的例子:
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
返回目录
要想从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字符串作为第一个参数传递给ObjectMapper.readValue()
方法, 参见前例
返回目录
也可以通过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);
返回目录
也可以通过FileReader
实例从读取JSON(替换上例中的StringReader
即可), 当然, 也可以直接使用File
对象
ObjectMapper objectMapper = new ObjectMapper();
File file = new File("data/car.json");
Car car = objectMapper.readValue(file, Car.class);
返回目录
可以通过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
返回目录
ObjectMapper objectMapper = new ObjectMapper();
InputStream input = new FileInputStream("data/car.json");
Car car = objectMapper.readValue(input, Car.class);
返回目录
ObjectMapper objectMapper = new ObjectMapper();
String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
byte[] bytes = carJson.getBytes("UTF-8");
Car car = objectMapper.readValue(bytes, Car.class);
返回目录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等等
返回目录
String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";
ObjectMapper objectMapper = new ObjectMapper();
List cars1 = objectMapper.readValue(jsonArray, new TypeReference>(){});
注意传递给readValue()
的TypeReference
类型参数. 这个参数告诉Jackson读取一"列"Car对象
TypeReference
进行包装返回目录ObjectMapper
可以从JSON字符串读取一个Java Map
, 当你不知道要提取的JSON的格式的时候非常有用. 一般会把JSON对象读取到一个JavaMap对象
中. 每一个JSON对象的属性都会变成JavaMap
中的键值对.
String jsonObject = "{\"brand\":\"ford\", \"doors\":5}";
ObjectMapper objectMapper = new ObjectMapper();
Map jsonMap = objectMapper.readValue(jsonObject, new TypeReference
忽略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原生类型产生的异常
返回目录
可以配置ObjectMapper
的Feature
, 使其在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
值的原生类型的属性, 不过, 你也可以配置ObjectMapper
的Feature
让它抛出异常, 如下:
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 {
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;
}
}
返回目录ObjectMapper
实例也可以用来从一个对象生成JSON数据. 可以使用下列方法:
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
实例, 然后调用ObjectMapper
的writeValue()
方法把Car
实例转换为JSON后输出到FileOutputStream
writeValueAsString()
和writeValueAsBytes()
也可以从对象生成JSON, 并且返回一个String
或Byte数组
的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 {
protected CarSerializer(Class 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
对象
返回目录
默认情况下, Jackson会把一个java.util.Date
对象序列化为一个long
型值, 也就是从1970-01-1
到现在的毫秒数, 当然, Jackson也支持把日期格式化为字符串.
返回目录
首先看看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
型整数
返回目录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
被格式化为一个字符串
返回目录
Jackson内置了一个树模型(tree model
)可以用来表示一个JSON对象. 这个树模型非常有用, 比如你不知道收到的JSON数据的结构, 或者你不想新建一个Java类来表示这个JSON数据, 或者你想在使用或者转发JSON数据前对它进行操作.
Jackson的树模型由JsonNode
类实现. 你可以用ObjectMapper
实例把JSON解析为JsonNode
模型, 就像反序列化出一个自定义类对象一样.
下面举例示范JsonNode
的用法.
返回目录
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
提供了非常灵活和动态访问的方式, 可以像访问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
的对象属性
不管是访问一个属性, 还是数组, 还是内嵌的对象, 都可以用JsonNode
的get()
方法. 为get()
传入一个字符串就可以访问一个JsonNode
实例的一个属性, 传入一个索引就是访问JsonNode
实例代表的数组, 索引代表了你要访问的数组元素位置.