jackson json序列化实现首字母大写,第二个字母需小写

 更新时间:2021年06月29日 11:30:06   作者:二奎  
这篇文章主要介绍了jackson json序列化实现首字母大写,第二个字母需小写方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

jackson json序列化首字母大写,第二个字母需小写

有这样一个类:

@Setter
@Getter
@JsonNaming(value = PropertyNamingStrategy.UpperCamelCaseStrategy.class)
public class Student {
    private String bName;
}

序列化后,希望首字母大写,如下面的测试代码:

@Test
    public void contextLoads() throws IOException {
        Student test = new Student();
        test.setBName("234234");
        String s = objectMapper.writeValueAsString(test);
        Assert.assertEquals("{\"BName\":\"234234\"}", s);
    }

可实际运行后,结果与希望不一样:

org.junit.ComparisonFailure:

Expected :{"BName":"234234"}

Actual :{"Bname":"234234"}

jackson在序列化时把第二个大写字母n转成了小写,这是为什么呢?

以下是跟踪源码的过程:

直接找到:com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector#collectAll这个方法:

执行完_addFields(props)方法后:

执行完_addMethods(props)方法后:

一个是bName,一个是bname;

第一个bName取的是字段的名称,

第二个bname是取的它的set方法:

public static String okNameForIsGetter(AnnotatedMethod am, String name,
            boolean stdNaming)
    {
        if (name.startsWith("is")) { // plus, must return a boolean
            Class<?> rt = am.getRawType();
            if (rt == Boolean.class || rt == Boolean.TYPE) {
                return stdNaming
                        ? stdManglePropertyName(name, 2)
                        : legacyManglePropertyName(name, 2);
            }
        }
        return null;
    }

根据stdNaming来决定这个name是以什么标准输出,默认的是false;

stdManglePropertyName 就是原始输出。

legacyManglePropertyName 就是规范输出。

下面的代码就是规范输出:

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();
    }

主要逻辑在for循环中,去除set后,第一个字母小写,

第二字母小写后,与第二个字母比较,如果都是小写,则直接接上,返回,

如果第二字母大写,就如我们的这种情况,就以小写的情况,接上,再去找下一个字母,直到找到小写字母为止。

意思就是为了满足驼峰命名规则,要规范输出。

如果我们的字段命名正如它的规范的话,props是只有一条记录的,因为:名称相同,就不插入了,由于咱们的名称不同,所以就有两条记录。

protected POJOPropertyBuilder _property(Map<String, POJOPropertyBuilder> props,
            String implName)
    {
        POJOPropertyBuilder prop = props.get(implName);
        if (prop == null) {
            prop = new POJOPropertyBuilder(_config, _annotationIntrospector, _forSerialization,
                    PropertyName.construct(implName));
            props.put(implName, prop);
        }
        return prop;
    }

可是我们输出中只有一条,没有bName这条,

其实在是这里把第一条删除了。因为:

这些属性为空,导致这个字段不可见:

protected void _removeUnwantedProperties(Map<String, POJOPropertyBuilder> props)
    {
        Iterator<POJOPropertyBuilder> it = props.values().iterator();
        while (it.hasNext()) {
            POJOPropertyBuilder prop = it.next();
            // First: if nothing visible, just remove altogether
            if (!prop.anyVisible()) {
                it.remove();
                continue;
            }
            // Otherwise, check ignorals
            if (prop.anyIgnorals()) {
                // first: if one or more ignorals, and no explicit markers, remove the whole thing
                if (!prop.isExplicitlyIncluded()) {
                    it.remove();
                    _collectIgnorals(prop.getName());
                    continue;
                }
                // otherwise just remove ones marked to be ignored
                prop.removeIgnored();
                if (!prop.couldDeserialize()) {
                    _collectIgnorals(prop.getName());
                }
            }
        }
    }

只剩第二记录bname,再首字母大写,所以就是Bname了。

解决方案:

第一个就是JsonProperty

@Setter
@Getter
@JsonNaming(value = PropertyNamingStrategy.UpperCamelCaseStrategy.class)
public class Student {
    @JsonProperty("BName")
    private String bName;
}

测试结果如下:

org.junit.ComparisonFailure:

Expected :{"BName":"234234"}

Actual :{"Bname":"234234","BName":"234234"}

虽然生成了BName,但是Bname仍在(加了JsonProperty就visable了)。

第二个就是配置

objectMapper的MapperFeature.USE_STD_BEAN_NAMIN如上文提到了,非规范化输出。

如下代码:

@Test
    public void contextLoads() throws IOException {
        Student test = new Student();
        test.setBName("234234");
        objectMapper.configure(MapperFeature.USE_STD_BEAN_NAMING, true);
        String s = objectMapper.writeValueAsString(test);
        Assert.assertEquals("{\"BName\":\"234234\"}", s);
    }

第三个方案:重写PropertyNamingStrategy:

@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);
    }

修改objectMapper的配置,要注意对其他功能的影响。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • JWT 设置token过期时间无效的解决

    JWT 设置token过期时间无效的解决

    这篇文章主要介绍了JWT 设置token过期时间无效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • Java集合之LinkedHashSet详解

    Java集合之LinkedHashSet详解

    这篇文章主要介绍了Java集合之LinkedHashSet详解,LinkedHashSet是HashSet的子类,实现了Set接口,LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表,需要的朋友可以参考下
    2023-09-09
  • SpringBoot实现发送邮件任务

    SpringBoot实现发送邮件任务

    这篇文章主要为大家详细介绍了SpringBoot实现发送邮件任务,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-02-02
  • Java设计模块系列之书店管理系统单机版(二)

    Java设计模块系列之书店管理系统单机版(二)

    这篇文章主要为大家详细介绍了Java单机版的书店管理系统设计模块和思想第二章,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • 详谈@Cacheable不起作用的原因:bean未序列化问题

    详谈@Cacheable不起作用的原因:bean未序列化问题

    这篇文章主要介绍了@Cacheable不起作用的原因:bean未序列化问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • 使用Spring启动时运行自定义业务

    使用Spring启动时运行自定义业务

    这篇文章主要介绍了使用Spring启动时运行自定义业务的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • SpringBoot项目中使用缓存Cache的正确方法分享

    SpringBoot项目中使用缓存Cache的正确方法分享

    缓存可以通过将经常访问的数据存储在内存中,减少底层数据源如数据库的压力,从而有效提高系统的性能和稳定性。本文就来讲讲SpringBoot项目中使用缓存Cache的正确姿势吧
    2023-04-04
  • Spring的Bean注入解析结果BeanDefinition详解

    Spring的Bean注入解析结果BeanDefinition详解

    这篇文章主要介绍了Spring的Bean注入解析结果BeanDefinition详解,BeanDefinition描述了一个bean实例,拥有属性值、构造参数值和具体实现的其他信息,其是一个bean的元数据,xml中配置的bean元素会被解析成BeanDefinition对象,需要的朋友可以参考下
    2023-12-12
  • Spring Boot使用JSR-380进行校验的示例

    Spring Boot使用JSR-380进行校验的示例

    这篇文章主要介绍了Spring Boot使用JSR-380进行校验,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • Spring Boot 实现Redis分布式锁原理

    Spring Boot 实现Redis分布式锁原理

    这篇文章主要介绍了Spring Boot实现Redis分布式锁原理,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
    2022-08-08

最新评论