Springboot 中的 JSON 应用

589 阅读5分钟

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"}

可以看到orderIdstatus 这两个属性被忽略了。

@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);
            }

        }
    }
}