news 2026/8/20 14:07:40

Spring Boot中的JSON技术

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot中的JSON技术

一、前言

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

二、自定义ObjectMapper

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

@RequestMapping("getuser")@ResponseBodypublicUsergetUser(){Useruser=newUser();user.setUserName("wno704");user.setBirthday(newDate());returnuser;}

User类:

@Getter@SetterpublicclassUserimplementsSerializable{privatestaticfinallongserialVersionUID=6222176558369919436L;privateStringuserName;privateintage;privateStringpassword;privateDatebirthday;}

访问getuser页面输出:

{“userName”:“wno704”,“age”:0,“password”:null,bth":“2020-08-18T03:27:44.587+00:00”}

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

@ConfigurationpublicclassJacksonConfig{@BeanpublicObjectMappergetObjectMapper(){ObjectMappermapper=newObjectMapper();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{Useruser=newUser();user.setUserName("wno704");user.setBirthday(newDate());Stringstr=mapper.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\"}}";JsonNodenode=this.mapper.readTree(json);Stringname=node.get("name").asText();intage=node.get("age").asInt();JsonNodehobby=node.get("hobby");Stringfirst=hobby.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}";Useruser=mapper.readValue(json,User.class);Stringname=user.getUserName();intage=user.getAge();returnname+" "+age;}catch(Exceptione){e.printStackTrace();}returnnull;}

五、Jackson注解

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

5.1 @JsonProperty

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

@JsonIgnoreprivateStringpassword;

再次访问getuser页面输出:

{“userName”:“wno704”,“bth”:“2018-04-02 10:45:34”}

password属性已被忽略。

5.2 @Jsonlgnore

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

@JsonIgnoreprivateStringpassword;

再次访问getuser页面输出:

{“userName”:“wno704”,“age”:0,“bth”:“2018-04-02 10:40:45”}

5.3 @JsonIgnoreProperties

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

@JsonIgnoreProperties({"password","age"})publicclassUserimplementsSerializable{...}

再次访问getuser页面输出:

{“userName”:“wno704”,“bth”:“2018-04-02 10:45:34”}

5.4 @JsonFormat

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

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")privateDatebirthday;

5.5 @JsonNaming

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

@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)publicclassUserimplementsSerializable{...}

再次访问getuser页面输出:

{“user_name”:“wno704”,“bth”:“2018-04-02 10:52:12”}

5.6 @JsonSerialize

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

publicclassUserSerializerextendsJsonSerializer<User>{@Overridepublicvoidserialize(Useruser,JsonGeneratorgenerator,SerializerProviderprovider)throwsIOException,JsonProcessingException{generator.writeStartObject();generator.writeStringField("user-name",user.getUserName());generator.writeEndObject();}}

上面的代码中我们仅仅序列化userName属性,且输出的key是user-name。 使用注解@JsonSerialize来指定User对象的序列化方式:

@JsonSerialize(using=UserSerializer.class)publicclassUserimplementsSerializable{...}

再次访问getuser页面输出:

{“user-name”:“wno704”}

5.7 @JsonDeserialize

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

publicclassUserDeserializerextendsJsonDeserializer<User>{@OverridepublicUserdeserialize(JsonParserparser,DeserializationContextcontext)throwsIOException,JsonProcessingException{JsonNodenode=parser.getCodec().readTree(parser);StringuserName=node.get("user-name").asText();Useruser=newUser();user.setUserName(userName);returnuser;}}

使用注解@JsonDeserialize来指定User对象的序列化方式:

@JsonDeserialize(using=UserDeserializer.class)publicclassUserimplementsSerializable{...}

测试:

@AutowiredObjectMappermapper;@RequestMapping("readjsonasobject")@ResponseBodypublicStringreadJsonAsObject(){try{Stringjson="{\"user-name\":\"wno704\"}";Useruser=mapper.readValue(json,User.class);Stringname=user.getUserName();returnname;}catch(Exceptione){e.printStackTrace();}returnnull;}

访问readjsonasobject,页面输出:

wno704

5.8 @JsonView

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

publicclassUserimplementsSerializable{privatestaticfinallongserialVersionUID=6222176558369919436L;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(){Useruser=newUser();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(@RequestBodyList<User>list){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}]";List<User>list=mapper.readValue(jsonStr,List.class);Stringmsg="";for(Useruser:list){msg+=user.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}]";JavaTypetype=mapper.getTypeFactory().constructParametricType(List.class,User.class);List<User>list=mapper.readValue(jsonStr,type);Stringmsg="";for(Useruser:list){msg+=user.getUserName();}returnmsg;}

访问customize,页面输出:wno704scott。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/20 14:06:19

英飞凌全新MEMS扫描仪:如何攻克AR眼镜与车载HUD的显示难题?

1. 从一块“会动的镜子”说起&#xff1a;MEMS扫描仪的核心是什么&#xff1f; 最近在整理手头的几个项目&#xff0c;发现无论是智能眼镜还是车载HUD&#xff0c;大家讨论的焦点都开始从“能不能显示”转向了“怎么显示得更好”。这背后绕不开一个关键器件&#xff1a;MEMS扫描…

作者头像 李华
网站建设 2026/8/20 14:00:10

免费查重和免费查AI率的网站能不能用?先看它收不收录你的论文!

免费查重和免费查AI率的网站能不能用&#xff1f;先看它收不收录你的论文&#xff01; 免费的能不能用&#xff0c;先问哪个问题&#xff1f; 不是准不准&#xff0c;是你的稿子会去哪里。 免费入口的成本要有人承担。有些是大厂拿它做产品入口&#xff0c;有些是靠后续的付…

作者头像 李华
网站建设 2026/8/20 13:59:59

基于Flutter与手机传感器的社交距离监测应用开发实战

1. 从“社交距离”到“个人安全伙伴”&#xff1a;一个创意的诞生最近几年&#xff0c;我们经历了一段特殊的时期&#xff0c;“社交距离”从一个公共卫生术语&#xff0c;变成了我们日常生活的一部分。虽然现在情况已经大为不同&#xff0c;但“保持安全距离”这个概念&#x…

作者头像 李华
网站建设 2026/8/20 13:56:10

纳米金刚石薄膜如何解决锂金属电池枝晶难题?斯坦福研究深度解析

1. 项目背景&#xff1a;为什么锂金属阳极是“圣杯”与“噩梦”的集合体&#xff1f; 如果你关注电池技术&#xff0c;一定听过“锂金属阳极”这个词。它被誉为下一代高能量密度电池的“圣杯”&#xff0c;但同时也是让无数科研人员和工程师头疼的“噩梦”。这听起来很矛盾&…

作者头像 李华