Spring Boot中的JSON技术
Spring Boot中的JSON技术
平日里在项目中处理JSON一般用的都是阿里巴巴的Fastjson,后来发现使用Spring Boot内置的Jackson来完成JSON的序列化和反序列化操作也挺方便。Jackson不但可以完成简单的序列化和反序列化操作,也能实现复杂的个性化的序列化和反序列化操作。
自定义ObjectMapper
我们都知道,在Spring中使用@ResponseBody注解可以将方法返回的对象序列化成JSON,比如:
@RequestMapping("getuser")
@ResponseBody
public User getUser() {
User user = new User();
user.setUserName("mrbird");
user.setBirthday(new Date());
return user;
}
User类:
public class User implements Serializable {
private static final long serialVersionUID = 6222176558369919436L;
private String userName;
private int age;
private String password;
private Date birthday;
...
}
访问getuser页面输出:
{"userName":"aaaa","age":0,"password":null,"birthday":1522634892365}
可看到时间默认以时间戳的形式输出,如果想要改变这个默认行为,我们可以自定义一个ObjectMapper来替代:
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,页面输出:
{"userName":"mrbird","age":0,"password":null,"birthday":"2018-04-02 10:14:24"}
序列化
Jackson通过使用mapper的writeValueAsString方法将Java对象序列化为JSON格式字符串:
@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。比如:
@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例子:
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数据进行绑定,如下所示:
@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指定一个别名。
@JsonProperty("bth")
private Date birthday;
再次访问getuser页面输出:
{"userName":"mrbird","age":0,"password":null,"bth":"2018-04-02 10:38:37"}
key birthday已经被替换为了bth。
@Jsonlgnore
@Jsonlgnore,作用在属性上,用来忽略此属性。
@JsonIgnore
private String password;
再次访问getuser页面输出:
{"userName":"mrbird","age":0,"bth":"2018-04-02 10:40:45"}
password属性已被忽略。
@JsonIgnoreProperties
@JsonIgnoreProperties,忽略一组属性,作用于类上,比如JsonIgnoreProperties({ "password", "age" })。
@JsonIgnoreProperties({ "password", "age" })
public class User implements Serializable {
...
}
再次访问getuser页面输出:
{"userName":"mrbird","bth":"2018-04-02 10:45:34"}
@JsonFormat
@JsonFormat,用于日期格式化,如:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birthday;
@JsonNaming
@JsonNaming,用于指定一个命名策略,作用于类或者属性上。Jackson自带了多种命名策略,你可以实现自己的命名策略,比如输出的key 由Java命名方式转为下面线命名方法 —— userName转化为user-name。
@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
public class User implements Serializable {
...
}
再次访问getuser页面输出:
{"user_name":"mrbird","bth":"2018-04-02 10:52:12"}
@JsonSerialize
@JsonSerialize,指定一个实现类来自定义序列化。类必须实现JsonSerializer接口,代码如下:
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对象的序列化方式:
@JsonSerialize(using = UserSerializer.class)
public class User implements Serializable {
...
}
再次访问getuser页面输出:
{"user-name":"mrbird"}
@JsonDeserialize
@JsonDeserialize,用户自定义反序列化,同@JsonSerialize ,类需要实现JsonDeserializer接口。
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对象的序列化方式:
@JsonDeserialize (using = UserDeserializer.class)
public class User implements Serializable {
...
}
测试:
@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,页面输出:
mrbird
@JsonView
@JsonView,作用在类或者属性上,用来定义一个序列化组。 比如对于User对象,某些情况下只返回userName属性就行,而某些情况下需要返回全部属性。 因此User对象可以定义成如下:
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指定一个组名,被序列化的对象只有在这个组的属性才会被序列化,代码如下:
@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页面输出:
{"userName":"mrbird"}
如果将@JsonView(User.UserNameView.class)替换为@JsonView(User.AllUserFieldView.class),输出:
{"userName":"mrbird","age":26,"password":"123456","birthday":"2018-04-02 11:24:00"}
因为接口AllUserFieldView继承了接口UserNameView所以userName也会被输出。
集合的反序列化
在Controller方法中,可以使用@RequestBody将提交的JSON自动映射到方法参数上,比如:
@RequestMapping("updateuser")
@ResponseBody
public int updateUser(@RequestBody List<User> list){
return list.size();
}
上面方法可以接受如下一个JSON请求,并自动映射到User对象上:
[{"userName":"mrbird","age":26},{"userName":"scott","age":27}]
Spring Boot 能自动识别出List对象包含的是User类,因为在方法中定义的泛型的类型会被保留在字节码中,所以Spring Boot能识别List包含的泛型类型从而能正确反序列化。
有些情况下,集合对象并没有包含泛型定义,如下代码所示,反序列化并不能得到期望的结果。
@Autowired
ObjectMapper mapper; @RequestMapping("customize")
@ResponseBody
public String customize() throws JsonParseException, JsonMappingException, IOException {
String jsonStr = "[{\"userName\":\"mrbird\",\"age\":26},{\"userName\":\"scott\",\"age\":27}]";
List<User> list = mapper.readValue(jsonStr, List.class);
String msg = "";
for (User user : list) {
msg += user.getUserName();
}
return msg;
}
访问customize,控制台抛出异常:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.example.pojo.User
这是因为在运行时刻,泛型己经被擦除了(不同于方法参数定义的泛型,不会被擦除)。为了提供泛型信息,Jackson提供了JavaType ,用来指明集合类型,将上述方法改为:
@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技术的更多相关文章
- Spring boot中自定义Json参数解析器
转载请注明出处... 一.介绍 用过springMVC/spring boot的都清楚,在controller层接受参数,常用的都是两种接受方式,如下 /** * 请求路径 http://127.0. ...
- Spring Boot 之使用 Json 详解
Spring Boot 之使用 Json 详解 简介 Spring Boot 支持的 Json 库 Spring Web 中的序列化.反序列化 指定类的 Json 序列化.反序列化 @JsonTest ...
- 【spring boot】spring boot中使用@RestController不起作用,不返回json,依旧去找访问接口的请求地址对应的页面
问题描述: spring boot中使用@RestController不起作用,不返回json,依旧去找访问接口的请求地址对应的页面 表现结果: 1>使用postman测试接口,表现为返回是40 ...
- 解决spring boot中rest接口404,500等错误返回统一的json格式
在开发rest接口时,我们往往会定义统一的返回格式,列如: { "status": true, "code": 200, "message" ...
- 在Spring Boot中整合Katharsis,来快速开发JSON API的Web应用
1 简介 我们进行Web API开发的时候,经常会使用Json格式的消息体,而Json格式非常灵活,不同的人会有不同的设计风格和实现,而JSON API提供了一套标准.但它并不提供直接实现. Kath ...
- 初识在Spring Boot中使用JPA
前面关于Spring Boot的文章已经介绍了很多了,但是一直都没有涉及到数据库的操作问题,数据库操作当然也是我们在开发中无法回避的问题,那么今天我们就来看看Spring Boot给我们提供了哪些疯狂 ...
- Spring Boot 中使用 jpa
本文原文版权归 CSDN Hgihness 所有,此处为转载+技术收藏,如有再转请自觉于篇头处标明原文作者及出处,这是大家对作者劳动成果的自觉尊重!! 作者:Hgihness 原文:http://bl ...
- Spring Boot 之遇见JSON
MVC框架中,Spring Boot内置了jackson来完成JSON的序列化和反序列化操作,并且,在与其他技术集成的时候,如Redis.MongoDB.Elasticsearch等对象序列化,都可使 ...
- Spring Boot中文文档(官方文档翻译 基于1.5.2.RELEASE)
作者:Phillip Webb, Dave Syer, Josh Long, Stéphane Nicoll, Rob Winch, Andy Wilkinson, Marcel Overdijk, ...
- Spring Boot中的微信支付(小程序)
前言 微信支付是企业级项目中经常使用到的功能,作为后端开发人员,完整地掌握该技术是十分有必要的. logo 一.申请流程和步骤 图1-1 注册微信支付账号 获取微信小程序APPID 获取微信商家的商户 ...
随机推荐
- java使用MVC编程模型实现1+到100图形界面
MVC概念 MVC编程模型是可以说从提出到现在经久不败,是一种先进的设计结构.能很好的体现个人分工,从而实现前后端分离. M(Model):模型:存储数据的对象.后端操作数据库的. V(View):视 ...
- PIL修改图像
PIL修改图像 像素:最小物理单元 pixel 分辨率:1024*980 可以表征 图像分辨率 或者350dpi 每英寸 350个dot表征分辨率 调整图像分辨率 from PIL import Im ...
- 修改 Ubuntu 的软件源
1.将 /etc/apt/ 路径下的 sources.list 的内容修改为如下内容(此内容为 Ubuntu Kylin 里面的内容,直接拿过来用,也可以用其它的国内的源). deb http://m ...
- jmeter 添加断言和查看断言结果
在对应的请求下添加响应断言,这里我们添加响应文本来作为检查点,来检查上面的这个请求是否成功 断言和断言结果是成对出现的,是为了检查我们添加的断言是否验证成功,如下图,如果成功,里面就会有对应的结果,且 ...
- AI大模型学习了解
# 百度文心 上线时间:2019年3月 官方介绍:https://wenxin.baidu.com/ 发布地点: 参考资料: 2600亿!全球最大中文单体模型鹏城-百度·文心发布 # 华为盘古 上线时 ...
- prometheus Alertmanager webhook
一.自定义邮件告警 二.使用docker部署微信机器人告警 1.制作镜像 2.启动容器和指定webhook容器 一.自定义邮件告警 在alertmanager服务的配置文件中指定自定义告警文件 # ...
- 瞬间并发测试-jmeter
测试需求:秒杀场景,瞬间并发.通常来说,JMeter的线程数即为并发的压力数,实际上JMeter在运行时,每个线程是独立的,虽然有100个线程,但这些线程并不是同时向服务器发送请求,JMeter要模拟 ...
- awk引用外部变量
test]# cat tmp.tmp120.4987 12.717858119.801948 13.38588119.424529 14.024871119.337438 15.070484119.2 ...
- TinyMapper使用 对象映射框架
方便的进行对象转换,仅作使用笔记 https://www.cnblogs.com/arvinzhang/p/8282922.html 例子: var result = new List<Equi ...
- Harbor离线安装
一.安装docker-compose 1-1. #安装方式一 curl -SL https://github.com/docker/compose/releases/download/v2.11.2/ ...