SpringBoot之返回JSON问题

标签: Springboot 分类: Java 创建时间:2020-02-08 14:03:39 更新时间:2025-01-17 10:39:22

前言

使用SpringBoot查询数据库,数据库中字段是首字母大写,实体类中也是使用了首字母大写,但是返回前端的数据json字符串时,出现了大写字母变为小写字母的情况。比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//获取全部站点信息
@Autowired
private MediumRepo mediumRepo;
@Autowired
private GroupInfoRepo groupInfoRepo;

@RequestMapping(value = "siteList")
public JSONObject siteList(){
JSONObject result=new JSONObject();
try{

// 获取全部介质
List<Medium> mediumList=mediumRepo.findAll();
JSONObject mediumObj=new JSONObject();
JSONObject tempObj=new JSONObject();
mediumList.stream().forEach(medium -> {
mediumObj.put(medium.getMediumType().toString(),medium);
});
result.put("mediumList",mediumObj);

// 获取全部分组
List<GroupInfo> groupInfos=groupInfoRepo.findAll();
JSONObject groupObj=new JSONObject();
groupInfos.stream().forEach(groupInfo -> {
groupObj.put(groupInfo.getGroupId().toString(),groupInfo.getGroupName());
});
result.put("groupList",groupObj);


}catch (Exception e){
logger.error("siteList",e);
}
return result;
}

最后返回给前台的json字符串清一色的首字母变成了小写。

[解决方式]

1.在属性上添加@JsonProperty注解

在属性上添加@JsonProperty注解,在set和get方法上添加@JsonIgnore,@JsonProperty和@JsonIgnore一起使用,只使用一个无效。

1
2
3
4
5
6
7
8
9
10
11
@JsonProperty(value = "MediumStr")
private String MediumStr;

@JsonIgnore
public String getMediumStr() {
return MediumStr;
}

public void setMediumStr(String mediumStr) {
MediumStr = mediumStr;
}
参考文章:
【1】.Java中JSONField字段大小写失效的问题解决 (这里有一个就是使用了set函数,没发现什么区别)
【2】.Springboot @JSONField返回json首字母大写不生效 (这里只使用了一个@JsonProperty)

2.直接将@JsonProperty注解到get,set方法上

1
2
3
4
@JsonProperty(value = "MediumStr")
public String getMediumStr() {
return MediumStr;
}

3.使用FastJSON替换默认的json转换器

根据参考文章4和5,还可以使用FastJSON替换默认的json转换器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.bibichuan.phemsjava.boot;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Iterator;
import java.util.List;

@Configuration
public class WebMvcCon implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT","PATCH")
.maxAge(3600);
}

// 配置JSON解析
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 替换默认的Jackson转换器
Iterator<HttpMessageConverter<?>> iterator = converters.iterator();
while(iterator.hasNext()){
HttpMessageConverter<?> converter = iterator.next();
if(converter instanceof MappingJackson2HttpMessageConverter){
iterator.remove();
}
}
/*
1.需要先定义一个convert转换消息的对象;
2.添加fastjson的配置信息,比如是否要格式化返回的json数据
3.在convert中添加配置信息
4.将convert添加到converters中
*/
//1.定义一个convert转换消息对象
FastJsonHttpMessageConverter fastConverter=new FastJsonHttpMessageConverter();
//2.添加fastjson的配置信息,比如:是否要格式化返回json数据
FastJsonConfig fastJsonConfig=new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
SerializerFeature.PrettyFormat
);
fastConverter.setFastJsonConfig(fastJsonConfig);
converters.add(fastConverter);
}
//  打印转换器
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> messageConverter : converters) {
System.out.println(messageConverter);
}
}
}

问题:

(1) Spring Boot配置FastJson报错’Content-Type’ cannot contain wildcard type ‘*’

在WebMvcCon类中增加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 配置JSON解析
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Iterator<HttpMessageConverter<?>> iterator = converters.iterator();
while(iterator.hasNext()){
HttpMessageConverter<?> converter = iterator.next();
if(converter instanceof MappingJackson2HttpMessageConverter){
iterator.remove();
}
}
/*
1.需要先定义一个convert转换消息的对象;
2.添加fastjson的配置信息,比如是否要格式化返回的json数据
3.在convert中添加配置信息
4.将convert添加到converters中
*/
//1.定义一个convert转换消息对象
FastJsonHttpMessageConverter fastConverter=new FastJsonHttpMessageConverter();
//2.添加fastjson的配置信息,比如:是否要格式化返回json数据
// FastJsonConfig fastJsonConfig=new FastJsonConfig();
// fastJsonConfig.setSerializerFeatures(
// SerializerFeature.PrettyFormat
// );
// fastConverter.setFastJsonConfig(fastJsonConfig);
// converters.add(fastConverter);

//自定义配置...
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(SerializerFeature.QuoteFieldNames,
SerializerFeature.PrettyFormat,
SerializerFeature.WriteEnumUsingToString,
/*SerializerFeature.WriteMapNullValue,*/
SerializerFeature.WriteDateUseDateFormat,
SerializerFeature.DisableCircularReferenceDetect);

List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
supportedMediaTypes.add(MediaType.APPLICATION_PDF);
supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XML);
supportedMediaTypes.add(MediaType.IMAGE_GIF);
supportedMediaTypes.add(MediaType.IMAGE_JPEG);
supportedMediaTypes.add(MediaType.IMAGE_PNG);
supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
supportedMediaTypes.add(MediaType.TEXT_HTML);
supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
supportedMediaTypes.add(MediaType.TEXT_XML);
fastConverter.setSupportedMediaTypes(supportedMediaTypes);
fastConverter.setFastJsonConfig(config);


converters.add(fastConverter);
}

(2) 不知道为什么,还是返回的是小写字母。就算单独写一个Bean也是不行的。不知道什么原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.bibichuan.phemsjava.boot;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class MessageConverConf {
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
/**
* 1.先定义一个convert转换消息的对象
* 2.添加fastjson的配置信息,比如:是否要格式化返回的json数据
* 3.在convert中添加配置信息
* 4.将convert添加到converters当中
*/
// 1.先定义一个convert转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
// 2.添加fastjson的配置信息,比如:是否要格式化返回的json数据
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
SerializerFeature.PrettyFormat,
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullStringAsEmpty
);

// 处理中文乱码问题(不然出现中文乱码)
List<MediaType> fastMediaTypes = new ArrayList<MediaType>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastConverter.setSupportedMediaTypes(fastMediaTypes);

// 3.在convert中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);

return new HttpMessageConverters(fastConverter);
}
}

(3) 原来是要在@Entity注解的实体类的属性上使用 @JSONField(name=”SiteNo”) ,就可以实现了首字母大写。

进一步的问题,如果我不使用每一个属性都多加一个@JSONField注解的方式,实现直接的进行首字母大写转换。

(4) 使用@JsonProperty注解之后,虽然将返回的字段首字母进行了大小,但是出现新的问题,就是小写的字段还是存在,也就是返回了两个数据,一个大写,一个小写

【解决方法】
如果放到属性上不起作用,或者同时返回了大小写,那么就尝试放在get方法上

参考文章:
【1】.解决@JsonProperty不生效的问题
【2】.@JsonProperty 失效问题的排查 (这里提到了在引入fastjson时,如何使JsonProperty生效,一个就是使用启动类上添加@EnableWebMvc,一个就是禁用fastjson)
【3】.SpringBoot 返回Json实体类属性大小写问题 (如果注解放到属性上,则返回的时候既有大写也有小写。解决方法就是注解放到getter上)
【4】.SpringBoot解决驼峰命名 —返回Json实体类属性大小写问题 (这里也提到了返回大小写的问题,一个就是放到get上,一个就是使用fastjson进行序列化)
【5】.用jackson的@JsonProperty注解属性名,会多出一个字段
【6】.如何解决SpringBoot 返回Json类属性大小写 这里也是返回的大小写都存在,如果没有使用fastjson(阿里巴巴出品),使用@JsonProperty(“XXXX”)的注解方式可以解决问题,XXXX就是转换成Json的属性名,但注意,只有放在getter上才是有效的,直接在属性上加没效果.(这说明spring 默认的jackson类库处理是基于访问器。

4.Spring的PropertyNamingStrategy配置

随着进一步资料查询,可以配置命名策略,我发现了一个在application.yml中通过配置PropertyNamingStrategy方法。答案是有的。只需要配置application.yml内容:

1
2
3
spring:
jackson:
property-naming-strategy: UPPER_CAMEL_CASE

还有其他的一些命名策略:

(1) LOWER_CASE
Naming convention in which all words of the logical name are in lower case, and no separator is used between words.
(2) KEBAB_CASE
Naming convention used in languages like Lisp, where words are in lower-case letters, separated by hyphens.
(3) LOWER_CAMEL_CASE
Naming convention used in Java, where words other than first are capitalized and no separator is used between words.
(4) LOWER_CASE
Naming convention in which all words of the logical name are in lower case, and no separator is used between words.
(5) SNAKE_CASE
Naming convention used in languages like C, where words are in lower-case letters, separated by underscores.

有下面几种方式:
(1)属性名称:@JsonProperty
(2)预定义命名策略,只需要作用于某个类:@JsonNaming
(3)自定义命名策略:PropertyNamingStrategy

5.使用Bean注入的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.bibichuan.phemsjava.boot;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.io.IOException;

@Configuration
public class BeanConf {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder){
ObjectMapper objectMapper = builder.createXmlMapper(false).build();

// 通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行系列化
// Include.Include.ALWAYS 默认
// Include.NON_DEFAULT 属性为默认值不序列化
// Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的。这样对移动端会更省流量
// Include.NON_NULL 属性为NULL 不序列化
// objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
// objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

// 允许出现特殊字符和转义符
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
// 允许出现单引号
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// 字段保留,将null值转为""
objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>()
{
@Override
public void serialize(Object o, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider)
throws IOException
{
jsonGenerator.writeString("");
}
});
return objectMapper;
}
}
参考文章:
1.springboot 针对jackson配置
2.Jackson修改字段名和自定义命名策略
3.jackson根据属性名动态序列化对象字段
4.Jackson序列化方式实现数据字段脱敏(工厂模式 + 策略模式) 1.基于Jaskson 序列化:针对需要展示在前端的数据 通常我们都是对应一个VO对象,这里可以在将VO对象返回给前端序列化时进行数据的脱敏;2.基于Mybatis 的拦截器:对select 语句进行拦截数据脱敏,但是存在问题 在某些业务中对数据脱敏字段是需要进行逻辑业务处理的。

总结
基础知识还是不牢啊,总是出现问题再去找答案,非常的浪费时间,需要系统性,综合性的对SpringBoot进行学习,才能进行更好的开发。

6.@RequestBody属性名大写注入为空

参考文章:
【1】.spring中使用@RequestBody,对应的bean中属性名大写注入为空的解决方法 (也是通过在属性上使用@JsonProperty注解完成)

7.使用@JSONFiled

使用这个注解主要就是在使用FastJSON序列化的时候,出现首字母大写变小写的问题。在fastjson1.x的时候,在属性上注解@JSONField就可以指定序列化的时候的内容,但是在FastJSON2.x的时候,这个注解注解到属性上不再起作用了,必须要注解到getter方法上,这个暂时还没有解决

8.自定义命名策略

编写自定义命名策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CustomPropertyNamingStrategy extends PropertyNamingStrategies.NamingBase {
@Override
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName)
{
return translate(defaultName);
}

@Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
return translate(defaultName);
}


@Override
public String translate(String input){
return input;
}
}

ObjectMapper 注入自定义命名策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Configuration
public class SpringBeanConfiguration {

@Bean
// @ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// 命名策略
objectMapper.setPropertyNamingStrategy(new CustomPropertyNamingStrategy());
//处理bigDecimal
objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
//处理失败
objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, false);
//默认的处理日期时间格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
objectMapper.registerModule(javaTimeModule);

return objectMapper;
}
}
参考文章:
【1】.jackson自定义策略 一.属性名称@JsonProperty;二 .预定义命名策略PropertyNamingStrategy;三.自定义PropertyNamingStrategy
【2】.Jackson修改字段名和自定义命名策略 通过实现 PropertyNamingStrategyBase 进行自定义命名策略的实现

9.StdSerializer

使用 StdSerializer 注入自定义的序列化和反序列化的类,实现单独对某一个类进行特殊的序列化。

小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。