写BUG的派大星

Patrick Star

  • 首页
  • 归档

  • 搜索
设计模式 Gis Kafka Druid 微信小程序 Java 开源项目源码 物体识别 机器学习 Mybatis 微服务 Feign OpenVPN CSS Streamsets CDH SpringCloud SpringBoot maven 分布式 Shell Tree Linux js WebSocket 多线程 集群 Hadoop 大数据 JDK ElasticSearch MySQL 数据库 Redis Http Nginx

SpringBoot中@RequestBody的对象属性第二个字母大写,反序列化失败

发表于 2020-08-18 | 分类于 Java | 0 | 阅读次数 1627

问题复现

假设实体类和Controller分别如下:
image.png
image.png

然后在postman中发送一个请求:
image.png
可以看到返回的是null,而且在后台的debug中也能看到pId在入参之后已经是null了,并不是return时产生的问题。
image.png

产生原因

在测试中发现,如果第二个字母大写,则会导致反序列化失败,该属性变为null。

具体是原因是SpringBoot默认使用Jackson进行json的序列化与反序列化,代码如下:

protected static String legacyManglePropertyName(final String basename, final int offset)
    {
        final int end = basename.length();
        if (end == offset) { // empty name, nope
            return null;
        }
        // next check: is the first character upper case? If not, return as is
        char c = basename.charAt(offset);
        char d = Character.toLowerCase(c);
        
        if (c == d) {
            return basename.substring(offset);
        }
        // otherwise, lower case initial chars. Common case first, just one char
        StringBuilder sb = new StringBuilder(end - offset);
        sb.append(d);
        int i = offset+1;
        for (; i < end; ++i) {
            c = basename.charAt(i);
            d = Character.toLowerCase(c);
            if (c == d) {
                sb.append(basename, i, end);
                break;
            }
            sb.append(d);
        }
        return sb.toString();
    }

Jackson在反序列化时主要依靠对象的setter方法。例如sName属性,实际需要调用的是setSName,在最后的for循环中,第二字母小写后,与第二个字母比较,如果都是小写,则直接拼接上并返回,
如果第二字母大写,就拼接上转为小写的结果,再去找下一个字母,直到找到小写字母为止。
这里也不是故意留下bug,而是为了满足驼峰命名规则,要规范输出,换言之,对于像pId,sName这种命名,它不认为这是一个有get/set方法的属性。
我们平时都知道,在JavaBean规范中,第一个字母必须要小写,但是很多时候不知道,第二个字母也需要小写,而造成一些影响,比如取到null,导致空指针异常。

解决方法

方法一

修改属性名称,将名称修改为符合规范的,也就是前两个字母均为小写的命名。但是如果半路发现这个问题,可能改动比较大,又涉及到前端的修改,可以采用方法二。

方法二

在字段前加入@JsonProperty

这种方法是比较推荐的一种方法,即你不认为这是一个属性,那么明确地我告诉你这是一个属性,避免歧义。

将实体类代码修改为如下再进行尝试:

@Data
public class TestEntity {
    @JsonProperty("pId")
    private String pId;

    private String qid;
}

image.png

已经可以正常进行数据转换。

方法三

重写ObjectMapper中的转换方法PropertyNamingStrategy,这种方法比较不推荐,因为有可能会对其他用到ObjectMapper的地方产生不可预估的影响。
具体方式如下:

@Test
    public void contextLoads() throws IOException {

        Student test = new Student();
        test.setBName("234234");
        //objectMapper.configure(MapperFeature.USE_STD_BEAN_NAMING, true);

        objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
            private static final long serialVersionUID = 1L;
            // 反序列化时调用
            @Override
            public String nameForSetterMethod(MapperConfig<?> config,
                                              AnnotatedMethod method, String defaultName) {
                return method.getName().substring(3);
            }
            // 序列化时调用
            @Override
            public String nameForGetterMethod(MapperConfig<?> config,
                                              AnnotatedMethod method, String defaultName) {
                return method.getName().substring(3);
            }
        });


        String s = objectMapper.writeValueAsString(test);

        Assert.assertEquals("{\"BName\":\"2342344\"}", s);

    }
  • 本文作者: Patrick
  • 本文链接: https://www.write1bug.cn/archives/springboot中requestbody的对象属性第二个字母大写反序列化失败
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# 设计模式 # Gis # Kafka # Druid # 微信小程序 # Java # 开源项目源码 # 物体识别 # 机器学习 # Mybatis # 微服务 # Feign # OpenVPN # CSS # Streamsets # CDH # SpringCloud # SpringBoot # maven # 分布式 # Shell # Tree # Linux # js # WebSocket # 多线程 # 集群 # Hadoop # 大数据 # JDK # ElasticSearch # MySQL # 数据库 # Redis # Http # Nginx
解决MySQL"SELECT list is not in GROUP BY"
Java 将Bytes为单位的数据切换为合适的单位
  • 文章目录
  • 站点概览
Patrick

Patrick

不是在改BUG,就是在写BUG。

52 日志
9 分类
36 标签
RSS
E-mail
Creative Commons
© 2018 — 2023 Patrick
人生如逆旅|我亦是行人
鲁ICP备18043140号-1