Spring Boot中的JSON技术
SpringBoot在处理对象的序列化和反序列的时候,底层用的是Jackson。来完成数据到实体类,实体类集合的映射。我们开发会指定 @RequestBody @ResponseBody 这样的注解。而了解Jackson框架,就显得很有必要了!
1、序列化
- Jackson通过使用mapper的
writeValueAsString()
方法,将Java对象序列化为JSON格式字符串。
/**
*底层通过objectMapper把JAVA对象变成JSON字符串输出给前端
* @param id
* @return
* @throws JsonProcessingException
*/
@GetMapping("findById2/{id}")
public void findById2(@PathVariable(name = "id")Long id, HttpServletResponse response) throws IOException {
UmsAdmin umsAdmin = adminService.findById(id);
String str = objectMapper.writeValueAsString(umsAdmin);
response.getWriter().println(str);
}
- 使用
@ResponseBody
注解可以使对象序列化为JSON格式字符串
@GetMapping("findById/{id}")
@ResponseBody
public UmsAdmin findById(@PathVariable(name = "id")Long id) {
UmsAdmin umsAdmin = adminService.findById(id);
System.out.println(umsAdmin);
return umsAdmin;
}
2、反序列化
- Jackson通过使用
readValue()
将Java对象和JSON数据进行绑定
@Autowired
ObjectMapper mapper;
@RequestMapping("readjsonasobject")
@ResponseBody
public String readJsonAsObject() {
try {
String json = "{\"name\":\"mrbird\",\"age\":26}";
User user = mapper.readValue(json, User.class);
String name = user.getUserName();
int age = user.getAge();
return name + " " + age;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
- 使用
@ResponseBody
注解可以使JSON格式字符串反序列化为JSON格式的对象
@GetMapping("/save")
public UmsAdmin save(@RequestBody UmsAdmin umsAdmin){
System.out.println(umsAdmin);
return umsAdmin;
}
3、他的注解
3.1、@JsonProperty
@JsonProperty
,作用在属性上,用来为JSON Key指定一个别名。
@JsonProperty("bth")
private Date birthday;
{"userName":"mrbird","age":0,"password":null,"bth":"2018-04-02 10:38:37"}
3.2、@Jsonlgnore
@Jsonlgnore
,作用在属性上,用来忽略此属性。
@JsonIgnore
private String password;
{"userName":"mrbird","age":0,"bth":"2018-04-02 10:40:45"}
password属性已被忽略。
3.3、@JsonIgnoreProperties
@JsonIgnoreProperties
,忽略一组属性,作用于类上,比如JsonIgnoreProperties({ "password", "age" })
。
@JsonIgnoreProperties({ "password", "age" })
public class User implements Serializable {
...
}
{"userName":"mrbird","bth":"2018-04-02 10:45:34"}
3.3、@JsonFormat
@JsonFormat
,用于日期格式化,如:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birthday;
3.4、@JsonNaming
@JsonNaming
,用于指定一个命名策略,作用于类或者属性上。Jackson自带了多种命名策略,你可以实现自己的命名策略,比如输出的key 由Java命名方式转为驼峰命名方式 —— userName转化为user-name。
@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
public class User implements Serializable {
...
{"user_name":"mrbird","bth":"2018-04-02 10:52:12"}
3.5、@JsonSerialize
@JsonSerialize
,指定一个实现类来自定义序列化的方式。类必须实现JsonSerializer
接口,代码如下:
import java.io.IOException;
import com.example.pojo.User;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class UserSerializer extends JsonSerializer<User> {
@Override
public void serialize(User user, JsonGenerator generator, SerializerProvider provider)
throws IOException, JsonProcessingException {
generator.writeStartObject();
generator.writeStringField("user-name", user.getUserName());
generator.writeEndObject();
}
}
上面的代码中我们仅仅序列化userName属性,且输出的key是user-name
。 使用注解@JsonSerialize
来指定User对象的序列化方式:
@JsonSerialize(using = UserSerializer.class)
public class User implements Serializable {
...
}
{"user-name":"mrbird"}
3.6、@JsonDeserialize
import java.io.IOException;
import com.example.pojo.User;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
public class UserDeserializer extends JsonDeserializer<User> {
@Override
public User deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
JsonNode node = parser.getCodec().readTree(parser);
String userName = node.get("user-name").asText();
User user = new User();
user.setUserName(userName);
return user;
}
}
使用注解@JsonDeserialize
来指定User对象的序列化方式:
@JsonDeserialize (using = UserDeserializer.class)
public class User implements Serializable {
...
}
@Autowired
ObjectMapper mapper;
@RequestMapping("readjsonasobject")
@ResponseBody
public String readJsonAsObject() {
try {
String json = "{\"user-name\":\"wangnaixing\"}";
User user = mapper.readValue(json, User.class);
String name = user.getUserName();
return name; //输出 wngnaixing
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
3.7、@JsonView
@JsonView
,作用在类或者属性上,用来定义一个序列化组。 比如对于User对象,某些情况下只返回userName属性就行,而某些情况下需要返回全部属性。 因此User对象可以定义成如下:
public class User implements Serializable {
private static final long serialVersionUID = 6222176558369919436L;
public interface UserNameView {};
public interface AllUserFieldView extends UserNameView {};
@JsonView(UserNameView.class)
private String userName;
@JsonView(AllUserFieldView.class)
private int age;
@JsonView(AllUserFieldView.class)
private String password;
@JsonView(AllUserFieldView.class)
private Date birthday;
...
}
User定义了两个接口类,一个为userNameView
,另外一个为AllUserFieldView
继承了userNameView
接口。这两个接口代表了两个序列化组的名称。属性userName使用了@JsonView(UserNameView.class)
,而剩下属性使用了@JsonView(AllUserFieldView.class)
。
@JsonView(User.UserNameView.class)
@RequestMapping("getuser")
@ResponseBody
public User getUser() {
User user = new User();
user.setUserName("mrbird");
user.setAge(26);
user.setPassword("123456");
user.setBirthday(new Date());
return user;
}
{"userName":"mrbird"}
如果将@JsonView(User.UserNameView.class)
替换为@JsonView(User.AllUserFieldView.class)
,输出:
{"userName":"mrbird","age":26,"password":"123456","birthday":"2018-04-02 11:24:00"}
因为接口AllUserFieldView
继承了接口UserNameView
所以userName也会被输出。
4、集合的反序列化
在Controller方法中,可以使用@RequestBody
将提交的JSON自动映射到方法参数上,比如:
@RequestMapping("updateuser")
@ResponseBody
public int updateUser(@RequestBody List<User> list){
return list.size();
}
上面方法可以接受如下一个JSON请求,并自动映射到User对象上:
[{"userName":"mrbird","age":26},{"userName":"scott","age":27}]
有些情况下,集合对象
并没有包含泛型定义,如下代码所示,反序列化并不能得到期望的结果
。
@Autowired
ObjectMapper mapper;
@RequestMapping("customize")
@ResponseBody
public String customize() throws JsonParseException, JsonMappingException, IOException {
String jsonStr = "[{\"userName\":\"mrbird\",\"age\":26},{\"userName\":\"scott\",\"age\":27}]";
List<User> list = mapper.readValue(jsonStr, List.class);
String msg = "";
for (User user : list) {
msg += user.getUserName();
}
return msg;
}
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.example.pojo.User
这是因为在运行时刻,泛型己经被擦除了(不同于方法参数定义的泛型,不会被擦除
)。为了提供泛型信息,Jackson提供了JavaType ,用来指明集合类型,将上述方法改为:
@Autowired
ObjectMapper mapper;
@RequestMapping("customize")
@ResponseBody
public String customize() throws JsonParseException, JsonMappingException, IOException {
String jsonStr = "[{\"userName\":\"mrbird\",\"age\":26},{\"userName\":\"scott\",\"age\":27}]";
JavaType type = mapper.getTypeFactory().constructParametricType(List.class, User.class);
List<User> list = mapper.readValue(jsonStr, type);
String msg = "";
for (User user : list) {
msg += user.getUserName();
}
return msg;
}