4. Jackson
写在开头,在MVC框架中,Spring Boot 内置了 Jackson 来完成JSON的序列化和反序列化。
- @ResponseBody 在Controller对应请求方法上就好了,自动将方法返回的对象序列化成JSON。
- 序列化、反序列化方式
Jackson是一个流行的高性能JavaBean到JSON的绑定工具,Jackson使用ObjectMapper类将POJO对象序列化成JSON字符串,也能将JSON字符串反序列化成POJO对象。
方式 | 说明 |
JsonParser | 采用JsonParser来解析JSON,解析结果是一串Tokens。 采用JSONGenerator来生成JSON,最底层方式。 |
树遍历 (Tree Traversing) | JSON被读入到JsonNode对象中,可以像操作XML DOM那样读取JSON |
DataBind | 数据绑定方式,最简单直接的一种方式。 有时候需要相关注解或序列化实现类来进行个性化序列化等操作 |
对应用程序来讲,最常用的方式是DataBind,也就是将POJO对象转换成JSON字符串,或者解析JSON字符串并映射到POJO对象上。
那么,如果没有现成的POJO对象做数据绑定的时候,也可以通过树遍历的方式来获取JSON数据。
- Jackson 树遍历(Tree Traversing)
树遍历方式通常适合没有POJO对象的JSON。
@Autowired ObjectMapper mapper;
@GetMapping("/readtree.json")
public @ResponseBody String readtree() throws JsonProcessingException, IOException {
String json = "{\"name\":\"zhangsan\", \"id\":10}";
// JSON序列化
JsonNode node = mapper.readTree(json);
// 获取序列化结果
String name = node.get("name").asText();
int id = node.get("id").asInt();
return "name" + name + ",id" + id;
}
readTree 方法可以接受一个字符串或者字节数组、文件、inputStream等,返回JsonNode作为根结点。
关于读取数据,JsonNode支持以下方法读取序列化后的JSON数据:
- axXXX:for example,asText、asBoolean、asInt etc… 读取JsonNode对应的值。
- isArray:判断JsonNode是否是数组,如果是数组,则可以调用 get(i) 来进行遍历。
- get(“key”):获取当前节点的子节点,返回JsonNode。
注:JSON规范要求key是字符串,且要使用双引号。
- ObjectMapper 序列化和反序列化
- 序列化 POJO to JSON
User user = mapper.readValue(json, User.class);
- 反序列化 JSON to POJO
String json = mapper.writeValueAsString(user);
- 树模型和数据绑定都是基于流失操作完成的,即通过JsonParser类解析JSON,形成JsonToken流。
- JsonParser 的解析结果包含了一些列JsonToken,JsonToken是一个枚举类型。列举常用的常量:
枚举常量 | 说明 |
START_OBJECT | { |
END_OBJECT | } |
START_ARRAY | [ |
END_ARRAY | ] |
FIELD_NAME | JSON key |
VALUE_STRING | JSON value 字符串类型 |
VALUE_NUMBER_INT | JSON value 整型 |
序列化简易流程:
// 模拟JSON数据,Map形式
String json = "{\"name\":\"zhangsan\"}";
// 定义暂存数据变量
String key = null, value = null;
/**
* 开始解析JSON
*/
JsonFactory f = mapper.getFactory();
JsonParser parser = f.createParser(json);
// { -> START_OBJECT,忽略第一个Token
JsonToken token = parser.nextToken();
token = parser.nextToken();
// JSON key -> FIELD_NAME 保存至暂存数据变量key中
if(token == JsonToken.FIELD_NAME) {
key = parser.getCurrentName();
}
token = parser.nextToken();
// JSON value -> VALUE_STRING 保存至暂存数据变量value中
value = parser.getValueAsString();
// 关闭 parser
parser.close();
return key + " : " + value;
判断Token类型后,通过getValueAsXXX获取其值,XXX是其值的类型。
反序列化简易流程:
JsonFactory f = mapper.getFactory();
// 输出到 StringWriter
StringWriter sw = new StringWriter();
JsonGenerator g = f.createGenerator(sw);
// 输出 {
g.writeStartObject();
// 输出数据
g.writeStringField(key, value);
// 输出 }
g.writeEndObject();
// 关闭 StringWriter
g.colse();
retrun sw.toString();
- Jackson 常用注解
注解 | 说明 | 实例 |
@JsonProperty | 作用在属性上,为JSON Key指定别名 | @JsonProperty(“userName”) private String name; |
@JsonIgnore | 作用在属性上,用来忽略此属性 | @JsonIgnore private String password; |
@JsonIgnoreProperties | 作用于类上,忽略一组属性 | @JsonIgnoreProperties({“id”, “phone”}) public static class SamplePojo{…} |
@JsonFormat | 日期格式化 | @JsonFormat(pattern = “yyyy-MM-dd”) private Date date; |
补充不常用注解:
@JsonSerialize 指定一个实现类来自定义序列化,类必须继承JsonSerializer
public static class UserSerializer extends JsonSerializer<User> {
@Override
public void serialize(User user, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("user_name", user.getName());
jsonGenerator.writeEndObject();
}
}
JsonGenerator 对象是 Jackson 底层的序列化实现,上述代码中对name属性做了序列化,输出key形式上变更为:user_name。
使用@JsonSerialize注解需要制定对象的序列化方式,例如:
@JsonSerialize(using = UserSerializer.class)
public class User {
...
}
同样的,自定义反序列化,需要通过反序列化实现类继承JsonDeserializer,使用@JsonDeserialize注解制定对象反序列化方式。
@JsonView,作用在类或者属性上,用来序列化组。Spring MVC的Controller方法可以使用同样的@JsonView来序列化属于本组的设置。
对于User对象,有些情况下只需要返回id属性,有些情况需要返回id和name,因此User对象可以定义为如下:
public class User {
public interface IdView {};
public interface IdNameView extends IdView {};
@JsonView(IdView.class)
private Integer id;
@JsonView(IdNameView.class)
private String name;
@JsonIgnore
BigDecimal salary;
}
User定义了两个接口类,IdView、IdNameView(继承IDView接口)。这两个接口代表了两个序列化组的名称。
Spring MVC 的Controller方法运行使用@JsonView()指定一个序列化组名,被序列化的对象只有在该序列组中的属性才会被序列化。
@JsonView(User.IdView.class)
@RequestMapping("/id.json")
public @ResponseBody User queryIds() {
User user = new User();
user.setId(1);
user.setName("hello");
return user;
}
上述代码,只会输出id属性内容。更换成@JsonView(User.IdNameView.class)将输出id和name,因为组名IdView是IdNameView的父类。
- 集合的反序列化
Spring MVC的Controller方法中,可以对参数列表使用@RequestBody注解,将提交的JSON自动映射到方法参数上。
注意是@RequestBody
,是@RequestBody
,是@RequestBody
,不是@ResponseBody
。
@RequestMapping("/addUser.json")
public @ResponseBody String addUser(@RequestBody User user) {
...
return user.toString();
}
通过Postman进行接口调用的时候需要注意:接收的是JSON请求