在Spring Boot项目中, 很多场景是将一个实体对象返回为JSON格式的字串, 但是在返回时需要指定一个String类型的属性返回的是Boolean类型的值。
举例来说:定义了一个Animal类,该类有两个属性 name(动物的名字 )和extinct(该动物是否灭绝)。该类的定义如下:
public class Animal {
private String name;
private String extinct;
//属性setter、getter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getExtinct() {
return extinct;
}
public void setExtinct(String extinct) {
this.extinct = extinct;
}
}
定义一个REST类型的控制器类JsonController , 该类中有一个接口方法str2bool(),方法返回一个Animal对象,代码如下:
@RestController
@RequestMapping(value = "/restjson")
public class JsonController {
@GetMapping(value = "/str2bool")
public Animal str2bool() {
Animal tiger = new Animal();
tiger.setName("Tiger");
tiger.setExtinct("false");
return tiger;
}
}
以为以上控制器使用@RestController
注解,所以返回的是一个JSON字符串,启动项目之后,在浏览器中输入: http://localhost:8080/restjson/str2bool, 返回的页面如下:
注意以上返回的"false",在Spring Boot配合前端框架开发时, 比如Ext JS, 有时候对于返回的数据类型有特殊要求,比如某些组件(比如Checkbox)对数据类型是敏感的, 也就是说 "false"和 fasle是严格区分的, 虽然也可以通过前端进行类新的转换, 但如果后端服务直接返回相应的类型看上去更简单。
将实体类的String类型的属性值返回JSON的布尔类型的键值,首选想到的就是@JsonFormat这个注解,而且查看JsonFormat注解的相关说明,的确是有一个shape属性用来指定类型,于是乎在 extinct 属性上加上该注解:
@JsonFormat(shape=Shape.BOOLEAN)
private String extinct;
可是修改完成之后,重启运行, 没有效果, 返回的值依旧是"false", 不是false。 难道是不支持从字符串类型到布尔型的转换吗?
那整型的转换支持吗?使用Shape.NUMBER,
@JsonFormat(shape = Shape.NUMBER)
private String extinct;
在str2bool()方法中赋一个数字的字符串`tiger.setExtinct("123");` 之后发现,依旧无效。
@JsonFormat是否能解决问题? 结果是不行
百度乃至千度无果的状况下, 只能调试了。可问题是: 调试从何入口?于是思考起来:
Spring Boot 默认使用Jackson处理数据转换,而且Spring Boot默认提供了Jackon的消息转换器
这个消息转换器类是MappingJackson2HttpMessageConverter
,位于spring-web的包中,查看该类发现,只有两个构造方法和setter方法, 看起来处理方法是在父类中,果不其然,在父类AbstractJackson2HttpMessageConverter
中找到
writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
方法,这个方法调用的 objectWriter.writeValue(generator, value);
看起来就是在处理JOSN输出, 代码如下:
进一步追踪
serialize(JsonGenerator gen, Object value, DefaultSerializerProvider prov)
, 这个方法是序列化对象无疑。serializeValue(JsonGenerator gen, Object value)
方法。基于上面调试的过程和结论, 于是就有个想法: 是否有Serializer相关的注解,指定序列化器呢?
如果有,应该是位于Jackson的databind相关的包中,的确,在jackson-databind中找到了 JsonSerialize注解标签,该标签的using属性看起来是指定序列化器的类型。
于是, 有个假设是使用JsonSerialize注解使用BooleanSerializer作为序列化器是否可以呢?属性上加上如下注解:
@JsonSerialize(using = BooleanSerializer.class)
private String extinct;
No converter found for return value of type: class cn.osxm.springboot.model.Animal
大致意思没有Animal类的转化器。
Spring的数据绑定和转换器是对应的。这里就没有再细究,直接百度一下@JsonSerialize的用法,得到的结论是需要自定义序列器类, 于是定义一个试试, 这里自定义的序列化器的类是:StringToBoolSerializer,内容如下:
public class StringToBoolSerializer extends JsonSerializer<String> {
@Override
public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
jsonGenerator.writeBoolean(str.equalsIgnoreCase("true"));
}
}
对应,属性的注解修改如下:
@JsonSerialize(using = StringToBoolSerializer.class)
private String invisible;
重启运行, 可以了。目标达成。
这里纠结要转换的原因是这个问题出现在一个既有的项目中, 其实如果Java类的属性定义为boolean类型,则转换为JSON后的值就是Boolean类型了。(在笔者的既有项目中更换类型会导致启动失败)
private boolean extinct;
public boolean isExtinct() {
return extinct;
}
public void setExtinct(boolean extinct) {
this.extinct = extinct;
}
而且在boolean类型属性上使用@JsonFormat(shape = Shape.NUMBER)
注解也是有效的,false会被转换为 0 .
调试过程中这两个单词会高频出现:
Java对象的序列化与反序列化的概念如下:
在Java Web开发中, JSON数据格式是规范的前端数据格式,借由序列化相关的概念:
Java对象==》JSON对象, 称之为序列化
JSON对象==》Java对象, 称之为反序列化
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="MM-dd-yyyy")
基于以上分析, 本篇取以下标题最为合适:
Java序列化为JSON对象时,String类型属性值转换为Boolean类型的处理。