Spring Boot中的JSON技术

平日里在项目中处理JSON一般用的都是阿里巴巴的Fastjson,后来发现使用Spring Boot内置的Jackson来完成JSON的序列化和反序列化操作也挺方便。Jackson不但可以完成简单的序列化和反序列化操作,也能实现复杂的个性化的序列化和反序列化操作。

自定义ObjectMapper

我们都知道,在Spring中使用@ResponseBody注解可以将方法返回的对象序列化成JSON,比如:

  1. @RequestMapping("getuser")
    @ResponseBody
    public User getUser() {
      User user = new User();
      user.setUserName("mrbird");
      user.setBirthday(new Date());
      return user;
    }

User类:

  1. public class User implements Serializable {
      private static final long serialVersionUID = 6222176558369919436L;
       
      private String userName;
      private int age;
      private String password;
      private Date birthday;
      ...
    }

访问getuser页面输出:

  1. {"userName":"aaaa","age":0,"password":null,"birthday":1522634892365}

可看到时间默认以时间戳的形式输出,如果想要改变这个默认行为,我们可以自定义一个ObjectMapper来替代:

  1. import java.text.SimpleDateFormat;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    import com.fasterxml.jackson.databind.ObjectMapper;

    @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对象,并且设置了时间格式。再次访问getuser,页面输出:

  1. {"userName":"mrbird","age":0,"password":null,"birthday":"2018-04-02 10:14:24"}

序列化

Jackson通过使用mapper的writeValueAsString方法将Java对象序列化为JSON格式字符串:

  1. @Autowired
    ObjectMapper mapper;

    @RequestMapping("serialization")
    @ResponseBody
    public String serialization() {
      try {
          User user = new User();
          user.setUserName("mrbird");
          user.setBirthday(new Date());
          String str = mapper.writeValueAsString(user);
          return str;
      } catch (Exception e) {
          e.printStackTrace();
      }
      return null;
    }

反序列化

使用@ResponseBody注解可以使对象序列化为JSON格式字符串,除此之外,Jackson也提供了反序列化方法。

树遍历

当采用树遍历的方式时,JSON被读入到JsonNode对象中,可以像操作XML DOM那样读取JSON。比如:

  1. @Autowired
    ObjectMapper mapper;

    @RequestMapping("readjsonstring")
    @ResponseBody
    public String readJsonString() {
      try {
          String json = "{\"name\":\"mrbird\",\"age\":26}";
          JsonNode node = this.mapper.readTree(json);
          String name = node.get("name").asText();
          int age = node.get("age").asInt();
          return name + " " + age;
      } catch (Exception e) {
          e.printStackTrace();
      }
      return null;
    }

readTree方法可以接受一个字符串或者字节数组、文件、InputStream等, 返回JsonNode作为根节点,你可以像操作XML DOM那样操作遍历JsonNode以获取数据。

解析多级JSON例子:

  1. String json = "{\"name\":\"mrbird\",\"hobby\":{\"first\":\"sleep\",\"second\":\"eat\"}}";;
    JsonNode node = this.mapper.readTree(json);
    JsonNode hobby = node.get("hobby");
    String first = hobby.get("first").asText();

绑定对象

我们也可以将Java对象和JSON数据进行绑定,如下所示:

  1. @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;
    }

Jackson注解

Jackson包含了一些实用的注解:

@JsonProperty

@JsonProperty,作用在属性上,用来为JSON Key指定一个别名。

  1. @JsonProperty("bth")
    private Date birthday;

再次访问getuser页面输出:

  1. {"userName":"mrbird","age":0,"password":null,"bth":"2018-04-02 10:38:37"}

key birthday已经被替换为了bth。

@Jsonlgnore

@Jsonlgnore,作用在属性上,用来忽略此属性。

  1. @JsonIgnore
    private String password;

再次访问getuser页面输出:

  1. {"userName":"mrbird","age":0,"bth":"2018-04-02 10:40:45"}

password属性已被忽略。

@JsonIgnoreProperties

@JsonIgnoreProperties,忽略一组属性,作用于类上,比如JsonIgnoreProperties({ "password", "age" })。

  1. @JsonIgnoreProperties({ "password", "age" })
    public class User implements Serializable {
      ...
    }

再次访问getuser页面输出:

  1. {"userName":"mrbird","bth":"2018-04-02 10:45:34"}

@JsonFormat

@JsonFormat,用于日期格式化,如:

  1. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date birthday;

@JsonNaming

@JsonNaming,用于指定一个命名策略,作用于类或者属性上。Jackson自带了多种命名策略,你可以实现自己的命名策略,比如输出的key 由Java命名方式转为下面线命名方法 —— userName转化为user-name。

  1. @JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
    public class User implements Serializable {
      ...
    }

再次访问getuser页面输出:

  1. {"user_name":"mrbird","bth":"2018-04-02 10:52:12"}

@JsonSerialize

@JsonSerialize,指定一个实现类来自定义序列化。类必须实现JsonSerializer接口,代码如下:

  1. 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对象的序列化方式:

  1. @JsonSerialize(using = UserSerializer.class)
    public class User implements Serializable {
      ...
    }

再次访问getuser页面输出:

  1. {"user-name":"mrbird"}

@JsonDeserialize

@JsonDeserialize,用户自定义反序列化,同@JsonSerialize ,类需要实现JsonDeserializer接口。

  1. 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对象的序列化方式:

  1. @JsonDeserialize (using = UserDeserializer.class)
    public class User implements Serializable {
      ...
    }

测试:

  1. @Autowired
    ObjectMapper mapper;

    @RequestMapping("readjsonasobject")
    @ResponseBody
    public String readJsonAsObject() {
      try {
          String json = "{\"user-name\":\"mrbird\"}";
          User user = mapper.readValue(json, User.class);
          String name = user.getUserName();
          return name;
      } catch (Exception e) {
          e.printStackTrace();
      }
      return null;
    }

访问readjsonasobject,页面输出:

  1. mrbird

@JsonView

@JsonView,作用在类或者属性上,用来定义一个序列化组。 比如对于User对象,某些情况下只返回userName属性就行,而某些情况下需要返回全部属性。 因此User对象可以定义成如下:

  1. 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)。

Spring中Controller方法允许使用@JsonView指定一个组名,被序列化的对象只有在这个组的属性才会被序列化,代码如下:

  1. @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;
    }

访问getuser页面输出:

  1. {"userName":"mrbird"}

如果将@JsonView(User.UserNameView.class)替换为@JsonView(User.AllUserFieldView.class),输出:

  1. {"userName":"mrbird","age":26,"password":"123456","birthday":"2018-04-02 11:24:00"}

因为接口AllUserFieldView继承了接口UserNameView所以userName也会被输出。

集合的反序列化

在Controller方法中,可以使用@RequestBody将提交的JSON自动映射到方法参数上,比如:

  1. @RequestMapping("updateuser")
    @ResponseBody
    public int updateUser(@RequestBody List<User> list){
      return list.size();
    }

上面方法可以接受如下一个JSON请求,并自动映射到User对象上:

  1. [{"userName":"mrbird","age":26},{"userName":"scott","age":27}]

Spring Boot 能自动识别出List对象包含的是User类,因为在方法中定义的泛型的类型会被保留在字节码中,所以Spring Boot能识别List包含的泛型类型从而能正确反序列化。

有些情况下,集合对象并没有包含泛型定义,如下代码所示,反序列化并不能得到期望的结果。

  1. @Autowired
  2. ObjectMapper mapper;
  3.  
  4. @RequestMapping("customize")
  5. @ResponseBody
  6. public String customize() throws JsonParseException, JsonMappingException, IOException {
  7. String jsonStr = "[{\"userName\":\"mrbird\",\"age\":26},{\"userName\":\"scott\",\"age\":27}]";
  8. List<User> list = mapper.readValue(jsonStr, List.class);
  9. String msg = "";
  10. for (User user : list) {
  11. msg += user.getUserName();
  12. }
  13. return msg;
  14. }

访问customize,控制台抛出异常:

  1. java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.example.pojo.User

这是因为在运行时刻,泛型己经被擦除了(不同于方法参数定义的泛型,不会被擦除)。为了提供泛型信息,Jackson提供了JavaType ,用来指明集合类型,将上述方法改为:

  1. @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;
    }

访问customize,页面输出:mrbirdscott。

Spring Boot中的JSON技术的更多相关文章

  1. Spring boot中自定义Json参数解析器

    转载请注明出处... 一.介绍 用过springMVC/spring boot的都清楚,在controller层接受参数,常用的都是两种接受方式,如下 /** * 请求路径 http://127.0. ...

  2. Spring Boot 之使用 Json 详解

    Spring Boot 之使用 Json 详解 简介 Spring Boot 支持的 Json 库 Spring Web 中的序列化.反序列化 指定类的 Json 序列化.反序列化 @JsonTest ...

  3. 【spring boot】spring boot中使用@RestController不起作用,不返回json,依旧去找访问接口的请求地址对应的页面

    问题描述: spring boot中使用@RestController不起作用,不返回json,依旧去找访问接口的请求地址对应的页面 表现结果: 1>使用postman测试接口,表现为返回是40 ...

  4. 解决spring boot中rest接口404,500等错误返回统一的json格式

    在开发rest接口时,我们往往会定义统一的返回格式,列如: { "status": true, "code": 200, "message" ...

  5. 在Spring Boot中整合Katharsis,来快速开发JSON API的Web应用

    1 简介 我们进行Web API开发的时候,经常会使用Json格式的消息体,而Json格式非常灵活,不同的人会有不同的设计风格和实现,而JSON API提供了一套标准.但它并不提供直接实现. Kath ...

  6. 初识在Spring Boot中使用JPA

    前面关于Spring Boot的文章已经介绍了很多了,但是一直都没有涉及到数据库的操作问题,数据库操作当然也是我们在开发中无法回避的问题,那么今天我们就来看看Spring Boot给我们提供了哪些疯狂 ...

  7. Spring Boot 中使用 jpa

    本文原文版权归 CSDN Hgihness 所有,此处为转载+技术收藏,如有再转请自觉于篇头处标明原文作者及出处,这是大家对作者劳动成果的自觉尊重!! 作者:Hgihness 原文:http://bl ...

  8. Spring Boot 之遇见JSON

    MVC框架中,Spring Boot内置了jackson来完成JSON的序列化和反序列化操作,并且,在与其他技术集成的时候,如Redis.MongoDB.Elasticsearch等对象序列化,都可使 ...

  9. Spring Boot中文文档(官方文档翻译 基于1.5.2.RELEASE)

    作者:Phillip Webb, Dave Syer, Josh Long, Stéphane Nicoll, Rob Winch, Andy Wilkinson, Marcel Overdijk, ...

  10. Spring Boot中的微信支付(小程序)

    前言 微信支付是企业级项目中经常使用到的功能,作为后端开发人员,完整地掌握该技术是十分有必要的. logo 一.申请流程和步骤 图1-1 注册微信支付账号 获取微信小程序APPID 获取微信商家的商户 ...

随机推荐

  1. 解决mysql使用sql文件不能还原数据库的问题

    来源:https://bbs.sangfor.com.cn/forum.php?mod=viewthread&tid=109605 解决ERROR 1231 (42000): Variable ...

  2. BIP拓展js的使用

    __app.define("common_VM_Extend.js", function () {   var selectData = null;   var common_VM ...

  3. SQL in查询字段为Guid拼接处理办法

    场景一:在我们写SQL脚本执行普通的Id 为Int,Long 类型查询为 譬如: select *from Table where id in (1,2,3); 场景二:SQL in 查询,当查询字段 ...

  4. 安装gitlab的总结

    安装过程参考文档 http://www.linuxe.cn/post-520.html https://www.bilibili.com/read/cv4602641 备注要点: 1.gitlab安装 ...

  5. APP数据指标体系分析

    一.APP的数据指标体系主要分为五个维度,包括用户规模与质量.参与度分析.渠道分析.功能分析和用户属性分析. 1.用户规模和质量 用户规模和质量是APP分析最重要的维度,其指标也是相对其他维度最多,数 ...

  6. 1.Easy Touch 3.1

    Easy Touch 3.1 Hedgehog Team(导入 Easy Touch 插件时自动在菜单栏) Extensions: 拓展 Adding a new joytick: 虚拟摇杆 Addi ...

  7. PAT-basic-1022 D进制的A+B java

    一.题目 输入两个非负 10 进制整数 A 和 B (≤230−1),输出 A+B 的 D (1<D≤10)进制数. 输入格式: 输入在一行中依次给出 3 个整数 A.B 和 D. 输出格式: ...

  8. Godot的场景树

    在Godot中,一个游戏的启动大致流程如下: 简而言之,Godot的main启动一个进程,加载所需的驱动设备(如渲染设备:GL/GLES/Vulkan等).音频设备,输入控制器设备等等:然后进入主循环 ...

  9. host头攻击

    一.问题源起 HTTP1.1规范中在增加了HOST头信息,用于存储服务端与客户端的路由信息. 例如: Request的Header里就有一个标名请求,它是用来记录服务端网站域名的. 如果前端或者后台正 ...

  10. MSSQL 查看数据库所有的触发器

    SELECT object_name(a.parent_obj) as [表名] ,a.name as [触发器名称] ,(case when b.is_disabled=0 then '启用' el ...