Spring Boot中的JSON技术

Spring Boot中的JSON技术
一、前言平日里在项目中处理JSON一般用的都是阿里巴巴的Fastjson后来发现使用Spring Boot内置的Jackson来完成JSON的序列化和反序列化操作也挺方便。Jackson不但可以完成简单的序列化和反序列化操作也能实现复杂的个性化的序列化和反序列化操作。二、自定义ObjectMapper我们都知道在Spring中使用ResponseBody注解可以将方法返回的对象序列化成JSON比如RequestMapping(getuser)ResponseBodypublicUsergetUser(){UserusernewUser();user.setUserName(wno704);user.setBirthday(newDate());returnuser;}User类GetterSetterpublicclassUserimplementsSerializable{privatestaticfinallongserialVersionUID6222176558369919436L;privateStringuserName;privateintage;privateStringpassword;privateDatebirthday;}访问getuser页面输出{“userName”:“wno704”,“age”:0,“password”:null,bth:“2020-08-18T03:27:44.58700:00”}可看到时间默认以时间戳的形式输出如果想要改变这个默认行为我们可以自定义一个ObjectMapper来替代ConfigurationpublicclassJacksonConfig{BeanpublicObjectMappergetObjectMapper(){ObjectMappermappernewObjectMapper();mapper.setDateFormat(newSimpleDateFormat(yyyy-MM-dd HH:mm:ss));returnmapper;}}上面配置获取了ObjectMapper对象并且设置了时间格式。再次访问getuser页面输出{“userName”:“wno704”,“age”:0,“password”:null,bth:“2020-08-18 11:42:51”}三、序列化Jackson通过使用mapper的writeValueAsString方法将Java对象序列化为JSON格式字符串AutowiredObjectMappermapper;RequestMapping(serialization)ResponseBodypublicStringserialization(){try{UserusernewUser();user.setUserName(wno704);user.setBirthday(newDate());Stringstrmapper.writeValueAsString(user);returnstr;}catch(Exceptione){e.printStackTrace();}returnnull;}四、反序列化使用ResponseBody注解可以使对象序列化为JSON格式字符串除此之外Jackson也提供了反序列化方法。4.1 树遍历当采用树遍历的方式时JSON被读入到JsonNode对象中可以像操作XML DOM那样读取JSON。readTree方法可以接受一个字符串或者字节数组、文件、InputStream等 返回JsonNode作为根节点你可以像操作XML DOM那样操作遍历JsonNode以获取数据。比如AutowiredObjectMappermapper;RequestMapping(readjsonstring)ResponseBodypublicStringreadJsonString(){try{Stringjson{\name\:\wno704\,\age\:28,\hobby\:{\first\:\sleep\,\second\:\eat\}};JsonNodenodethis.mapper.readTree(json);Stringnamenode.get(name).asText();intagenode.get(age).asInt();JsonNodehobbynode.get(hobby);Stringfirsthobby.get(first).asText();returnname age first;}catch(Exceptione){e.printStackTrace();}returnnull;}4.2 绑定对象我们也可以将Java对象和JSON数据进行绑定如下所示AutowiredObjectMappermapper;RequestMapping(readjsonasobject)ResponseBodypublicStringreadJsonAsObject(){try{Stringjson{\name\:\wno704\,\age\:26};Userusermapper.readValue(json,User.class);Stringnameuser.getUserName();intageuser.getAge();returnname age;}catch(Exceptione){e.printStackTrace();}returnnull;}五、Jackson注解Jackson包含了一些实用的注解5.1 JsonPropertyJsonlgnore作用在属性上用来忽略此属性。JsonIgnoreprivateStringpassword;再次访问getuser页面输出{“userName”:“wno704”,“bth”:“2018-04-02 10:45:34”}password属性已被忽略。5.2 JsonlgnoreJsonlgnore作用在属性上用来忽略此属性。JsonIgnoreprivateStringpassword;再次访问getuser页面输出{“userName”:“wno704”,“age”:0,“bth”:“2018-04-02 10:40:45”}5.3 JsonIgnorePropertiesJsonIgnoreProperties忽略一组属性作用于类上比如JsonIgnoreProperties({ “password”, “age” })。JsonIgnoreProperties({password,age})publicclassUserimplementsSerializable{...}再次访问getuser页面输出{“userName”:“wno704”,“bth”:“2018-04-02 10:45:34”}5.4 JsonFormatJsonFormat用于日期格式化如JsonFormat(patternyyyy-MM-dd HH:mm:ss)privateDatebirthday;5.5 JsonNamingJsonNaming用于指定一个命名策略作用于类或者属性上。Jackson自带了多种命名策略你可以实现自己的命名策略比如输出的key 由Java命名方式转为下面线命名方法 —— userName转化为user-name。JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)publicclassUserimplementsSerializable{...}再次访问getuser页面输出{“user_name”:“wno704”,“bth”:“2018-04-02 10:52:12”}5.6 JsonSerializeJsonSerialize指定一个实现类来自定义序列化。类必须实现JsonSerializer接口代码如下publicclassUserSerializerextendsJsonSerializerUser{Overridepublicvoidserialize(Useruser,JsonGeneratorgenerator,SerializerProviderprovider)throwsIOException,JsonProcessingException{generator.writeStartObject();generator.writeStringField(user-name,user.getUserName());generator.writeEndObject();}}上面的代码中我们仅仅序列化userName属性且输出的key是user-name。 使用注解JsonSerialize来指定User对象的序列化方式JsonSerialize(usingUserSerializer.class)publicclassUserimplementsSerializable{...}再次访问getuser页面输出{“user-name”:“wno704”}5.7 JsonDeserializeJsonDeserialize用户自定义反序列化同JsonSerialize 类需要实现JsonDeserializer接口。publicclassUserDeserializerextendsJsonDeserializerUser{OverridepublicUserdeserialize(JsonParserparser,DeserializationContextcontext)throwsIOException,JsonProcessingException{JsonNodenodeparser.getCodec().readTree(parser);StringuserNamenode.get(user-name).asText();UserusernewUser();user.setUserName(userName);returnuser;}}使用注解JsonDeserialize来指定User对象的序列化方式JsonDeserialize(usingUserDeserializer.class)publicclassUserimplementsSerializable{...}测试AutowiredObjectMappermapper;RequestMapping(readjsonasobject)ResponseBodypublicStringreadJsonAsObject(){try{Stringjson{\user-name\:\wno704\};Userusermapper.readValue(json,User.class);Stringnameuser.getUserName();returnname;}catch(Exceptione){e.printStackTrace();}returnnull;}访问readjsonasobject页面输出wno7045.8 JsonViewJsonView作用在类或者属性上用来定义一个序列化组。 比如对于User对象某些情况下只返回userName属性就行而某些情况下需要返回全部属性。 因此User对象可以定义成如下publicclassUserimplementsSerializable{privatestaticfinallongserialVersionUID6222176558369919436L;publicinterfaceUserNameView{};publicinterfaceAllUserFieldViewextendsUserNameView{};JsonView(UserNameView.class)privateStringuserName;JsonView(AllUserFieldView.class)privateintage;JsonView(AllUserFieldView.class)privateStringpassword;JsonView(AllUserFieldView.class)privateDatebirthday;...}User定义了两个接口类一个为userNameView另外一个为AllUserFieldView继承了userNameView接口。这两个接口代表了两个序列化组的名称。属性userName使用了JsonView(UserNameView.class)而剩下属性使用了JsonView(AllUserFieldView.class)。Spring中Controller方法允许使用JsonView指定一个组名被序列化的对象只有在这个组的属性才会被序列化代码如下JsonView(User.UserNameView.class)RequestMapping(getuser)ResponseBodypublicUsergetUser(){UserusernewUser();user.setUserName(wno704);user.setAge(26);user.setPassword(123456);user.setBirthday(newDate());returnuser;}访问getuser页面输出{“userName”:“wno704”}如果将JsonView(User.UserNameView.class)替换为JsonView(User.AllUserFieldView.class)输出{“userName”:“wno704”,“age”:26,“password”:“123456”,“birthday”:“2018-04-02 11:24:00”}因为接口AllUserFieldView继承了接口UserNameView所以userName也会被输出。集合的反序列化在Controller方法中可以使用RequestBody将提交的JSON自动映射到方法参数上比如RequestMapping(updateuser)ResponseBodypublicintupdateUser(RequestBodyListUserlist){returnlist.size();}上面方法可以接受如下一个JSON请求并自动映射到User对象上[{“userName”:“wno704”,“age”:26},{“userName”:“scott”,“age”:27}]Spring Boot 能自动识别出List对象包含的是User类因为在方法中定义的泛型的类型会被保留在字节码中所以Spring Boot能识别List包含的泛型类型从而能正确反序列化。有些情况下集合对象并没有包含泛型定义如下代码所示反序列化并不能得到期望的结果。AutowiredObjectMappermapper;RequestMapping(customize)ResponseBodypublicStringcustomize()throwsJsonParseException,JsonMappingException,IOException{StringjsonStr[{\userName\:\wno704\,\age\:26},{\userName\:\scott\,\age\:27}];ListUserlistmapper.readValue(jsonStr,List.class);Stringmsg;for(Useruser:list){msguser.getUserName();}returnmsg;}访问customize控制台抛出异常java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.example.pojo.User这是因为在运行时刻泛型己经被擦除了不同于方法参数定义的泛型不会被擦除。为了提供泛型信息Jackson提供了JavaType 用来指明集合类型将上述方法改为AutowiredObjectMappermapper;RequestMapping(customize)ResponseBodypublicStringcustomize()throwsJsonParseException,JsonMappingException,IOException{StringjsonStr[{\userName\:\wno704\,\age\:26},{\userName\:\scott\,\age\:27}];JavaTypetypemapper.getTypeFactory().constructParametricType(List.class,User.class);ListUserlistmapper.readValue(jsonStr,type);Stringmsg;for(Useruser:list){msguser.getUserName();}returnmsg;}访问customize页面输出wno704scott。

最新新闻

日新闻

周新闻

月新闻