一个叫木头,一个叫马尾

Jackson处理JSON序列化的一个小技巧

*本文针对的是Java语言及Jackson库,其它语言或库中应该也有类似的技巧。

出于一些原因,我们偶尔需要往数据库中插入序列化后的JSON数据:

在把该类数据取出,返回给前端时,我发现大家通常的做法如下:

  1. 要么当作字符串取出,让前端自己去解析。

后端返回给前端如下json数据:

{
  "ageNature""[{\"ageId\":54,\"isChecked\":true}]"
}

前端拿到数据后手工解析(因为 ageNature 只是个字符串):

const data = ...;
const ageNature = JSON.parse(data.ageNature);
  1. 要么后端先进行反序列化操作,再返回给前端:

后端:

Bean bean = ...;
// 手工解析
bean.ageNature = JSON.parse(ageNature);
return bean;

前端收到的数据,因为ageNature已经是对象了,可以直接使用:

{
  "ageNature": [
    {
      "ageId"54,
      "isChecked"true
    }
  ]
}


方式一增加了前端的工作量,返回的JSON文本需要他们手工解析。我们尽量要方便前端,给他们的数据让他们拿来即用。尤其是当业务涉及多端时(web/native),可避免重复性的解析工作。

方式二,增加后端的工作量只是其一,关键是反序列化之后,数据返回给前端之前,其实还要经过一步序列化(将Java Bean序列化为JSON返回给前端)。这意味着先前的解析变得多余了。我们知道,无论是序列化还是反序列化,通常都涉及到反射的调用,而这将损失掉部分性能

所以终究的问题是,后端从db中取出JSON文本后,能否在不反序列化的条件下,在返回给前端时,前端看到的仍是JSON对象,而不是字符串。?

答案是可以!

多亏了@JsonRawValue注解。它可以以 as is 的形式将注解的字段原封不动地返回给前端。即,若字段值是[1,2,3],则返回[1,2,3],而不是返回"[1,2,3]"[1,2,3]是JSON数组,而"[1,2,3]"仅仅是JSON字符串。

实战演示

我们给出如下方法:

@GetMapping("raw_json")
User raw() {
    return new User();
}

@Data
class User {
    String name = "Whatever";

    @JsonRawValue
    String ageNature = "[{\"ageId\":54,\"isChecked\":true}]";
}

因为ageNature字段加了@JsonRawValue注解,所以访问该方法后,返回如下信息:

{
  "name""Whatever",
  "ageNature": [
    {
      "ageId"54,
      "isChecked"true
    }
  ]
}

如果不加该注解,我们将得到:

{
  "name""Whatever",
  "friendIds""[{\"ageId\":54,\"isChecked\":true}]"
}

注意二者的区别。


与Swagger的整合

由于我们上例中User.ageNature被定义成了String类型,Swagger将做如下显示:

显然这不是我们想要的,我们想显示出ageNature真实的结构信息。其实借助于@ApiModelProperty,我们是可以达到目的的:

// 先定义一个封装ageNature的类
@Data
public class AgeNature {
    int ageId;
    boolean isChecked;
}

@Data
class User {
    String name = "Whatever";

    // 再利用 dataType 配置该类
    @ApiModelProperty(dataType = "[Lyour.package.AgeNature;")
    @JsonRawValue
    String ageNature = "[{\"ageId\":54,\"isChecked\":true}]";
}

要注意的是,dataType数组是用Reflection Notation去标注的。后者详细的用法见这里(点我)

标注完成后,再次检查 swagger,我们得到了我们想要的:


总结

JSON数据从存储、取出、到返回给客户端,这其中涉及的转换,有部分是可以避免的。本文介绍了@JsonRawValue这一利器,也许对大家有帮助。