Springboot 中的 JSON 应用
在阿里巴巴的FastJson逐步被淘汰的时候,springboot 自带Jackson自然的进入了我们的视野,其强大的功能和丰富的应用配置,开始逐步被我们所青睐。
自定义 ObjectMapper
我们都知道,在Spring中使用@ResponseBody注解可以将方法返回的对象序列化成JSON,比如:
@RequestMapping("/get-order")
@ResponseBody
public Order getOrder() {
Order order = new Order();
order.setOrderId(124656L);
return order;
}
Order类:
public class Order implements Serializable {
private static final long serialVersionUID = 661434701950670670L;
private Long orderId;
private Long userId;
private Long addressId;
private String status;
private Date createTime;
}
访问get-order页面输出:
{"orderId": 124656,"userId": 0,"addressId": 0,"status": "","createTime": "2021-12-24T02:36:30.336+00:00"}
可看到时间是默认的格式形式输出,如果想要改变这个默认行为,我们可以自定义一个ObjectMapper来替代:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.text.SimpleDateFormat;
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper getObjectMapper(){
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
return mapper;
}
}
上面配置获取了ObjectMapper对象,并且设置了时间格式。再次访问get-order,页面输出:
{
"orderId": 124656,
"userId": 0,
"addressId": 0,
"status": "",
"createTime": "2021-12-24 10:45:28"
}
序列化
Jackson通过使用mapper的writeValueAsString方法将Java对象序列化为JSON格式字符串:
@Autowired
ObjectMapper objectMapper;
@GetMapping("/serialization")
@ResponseBody
public String serialization() throws Exception {
Order order = new Order();
order.setOrderId(10L);
order.setUserId(10L);
order.setAddressId(10L);
order.setStatus("");
order.setCreateTime(new Date());
String string = objectMapper.writeValueAsString(order);
return string;
}
反序列化
Jackson通过使用mapper的readValue方法将JSON格式字符串反序列化为Java对象,也可以通过@ResponseBody进行反序列化。
@GetMapping("/deserializer")
@ResponseBody
public Order deserializer() throws Exception {
String json = "{\"orderId\": 124656,\"userId\": 0,\"addressId\": 0,\"status\": \"\",\"createTime\": \"2021-12-24 02:36:30\"}";
// 反序列化成JsonNode tree ,
JsonNode jsonNode = objectMapper.readTree(json);
// 获取某个属性
Long orderId = jsonNode.get("orderId").asLong();
// 也可以将Java对象和JSON数据进行绑定,进行反序列化
Order order = objectMapper.readValue(json, Order.class);
return order;
}
Jackson注解
Jackson包含了一些实用的注解:
@JsonProperty
@JsonProperty,作用在属性上,用来为JSON Key指定一个别名。
@JsonProperty("addr")
private Long addressId;
序列化后的结果:
{"orderId":10,"userId":10,"status":"","createTime":"2021-12-24 11:08:20","addr":10}
addressId已经被替换为了addr。
@JsonIgnore
@Jsonlgnore 作用在属性上,用来忽略此属性。
@JsonIgnore
private Long userId;
序列化后的结果:
{"orderId":10,"status":"","createTime":"2021-12-24 11:13:34","addr":10}
userId属性已被忽略。
@JsonIgnoreProperties
@JsonIgnoreProperties,忽略一组属性,作用于类上,比如JsonIgnoreProperties({ "orderId", "status" })。
@JsonIgnoreProperties(value = {"orderId","status"})
public class Order implements Serializable {
private static final long serialVersionUID = 661434701950670670L;
private Long orderId;
private Long userId;
private Long addressId;
private String status;
private Date createTime;
}
序列化后的结果:
{"userId":10,"addressId":10,"createTime":"2021-12-24 11:25:27"}
可以看到orderId 、status 这两个属性被忽略了。
@JsonFormat
@JsonFormat,用于日期格式化,如:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@JsonNaming
@JsonNaming,用于指定一个命名策略,作用于类或者属性上。Jackson自带了多种命名策略,你可以实现自己的命名策略,比如输出的key 由Java命名方式转为中划线命名方法 —— userId转化为user-id。
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
public class Order implements Serializable {
...
}
序列化后:
{"order-id":10,"user-id":10,"address-id":10,"status":"","create-time":"2021-12-24 11:34:50"}
@JsonSerialize
@JsonSerialize,指定一个实现类来自定义序列化。类必须实现JsonSerializer接口,代码如下:
import com.example.demo.entity.Order;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class OrderSerializer extends JsonSerializer<Order> {
@Override
public void serialize(Order value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeObjectField("user-id", value.getUserId());
gen.writeEndObject();
}
}
上面的代码中我们仅仅序列化userId属性,且输出的key是user-id。 使用注解@JsonSerialize来指定User对象的序列化方式:
@JsonSerialize(using = OrderSerializer.class)
public class Order implements Serializable {
...
}
序列化后结果:
{"user-id":13446}
@JsonDeserialize
@JsonDeserialize,用户自定义反序列化,同@JsonSerialize ,类需要实现JsonDeserializer接口。
import com.example.demo.entity.Order;
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;
import java.io.IOException;
public class OrderDeserializer extends JsonDeserializer<Order> {
@Override
public Order deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = parser.getCodec().readTree(parser);
Long userId = node.get("user-id").asLong();
Order order = new Order();
order.setUserId(userId);
return order;
}
}
使用注解@JsonDeserialize来指定Order对象的反序列化方式:
@JsonDeserialize (using = OrderDeserializer.class)
public class Order implements Serializable {
...
}
@JsonView
@JsonView,作用在类或者属性上,用来定义一个序列化组。 比如对于Order对象,某些情况下只返回userId属性就行,而某些情况下需要返回全部属性。 因此Order对象可以定义成如下:
public class Order implements Serializable {
private static final long serialVersionUID = 661434701950670670L;
public interface UserIdView {};
public interface AllFieldView extends UserIdView {};
@JsonView(AllFieldView.class)
private Long orderId;
@JsonView(UserIdView.class)
private Long userId;
@JsonView(AllFieldView.class)
private Long addressId;
@JsonView(AllFieldView.class)
private String status;
@JsonView(AllFieldView.class)
private Date createTime;
......
}
Order定义了两个接口类,一个为userIdView,另外一个为allFieldView继承了userIdView接口。这两个接口代表了两个序列化组的名称。属性userName使用了@JsonView(UserIdView.class),而剩下属性使用了@JsonView(AllFieldView.class)。
Spring中Controller方法允许使用@JsonView指定一个组名,被序列化的对象只有在这个组的属性才会被序列化,代码如下:
@JsonView(Order.UserIdView.class)
@GetMapping("/get-order")
@ResponseBody
public Order getOrder() {
Order order = new Order();
order.setOrderId(124656L);
order.setUserId(0L);
order.setAddressId(0L);
order.setStatus("");
order.setCreateTime(new Date());
return order;
}
序列化结果:
{
"userId": 0
}
如果将@JsonView(Order.UserIdView.class)替换为@JsonView(Order.AllFieldView.class),输出:
{
"orderId": 124656,
"userId": 0,
"addressId": 0,
"status": "",
"createTime": "2021-12-24 11:57:38"
}
因为接口AllFieldView继承了接口UserIdView所以userId也会被输出。
@RequestBody
@RequestBody 熟悉spring 框架的都知道,这个注解能自动的把参数反序列化到方法参数上,就不多说了。
@JsonComponent
@JsonComponent 是Spring boot的核心注解,使用@JsonComponent 之后就不需要手动将Jackson的序列化和反序列化手动加入ObjectMapper了。使用这个注解就够了。
SpringBoot 全局日期格式化(基于注解)
/**
* 全局日期格式化
*/
@JsonComponent
public class DateFormatConfig {
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 日期格式化
*/
public static class DateJsonSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(dateFormat.format(date));
}
}
/**
* 解析日期字符串
*/
public static class DateJsonDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
try {
return dateFormat.parse(jsonParser.getText());
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
}