jackson转换json大写_jackson json序列化 首字母大写 第二个字母需小写

在Jackson序列化过程中遇到首字母大写但第二个字母变为小写的问题。通过分析源码,发现是Jackson遵循驼峰命名规则进行规范输出。解决方案包括使用@JsonProperty注解指定字段名,设置MapperFeature.USE_STD_BEAN_NAMING为true,或自定义PropertyNamingStrategy重写方法以达到期望效果。
摘要由CSDN通过智能技术生成

有这样一个类:

@Setter

@Getter

@JsonNaming(value= PropertyNamingStrategy.UpperCamelCaseStrategy.class)public classStudent {privateString bName;

}

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

@Testpublic void contextLoads() throwsIOException {

Student test= newStudent();

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这个方法:

a46d7046abeab4931bfd2bbca07ad9e1.png

执行完_addFields(props)方法后:

b972701887472a6719bde64cdbe89a78.png

执行完_addMethods(props)方法后:

32f2361b8d8433fa3aeea76776f4a63e.png

一个是bName,一个是bname;

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

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

public staticString okNameForIsGetter(AnnotatedMethod am, String name,booleanstdNaming)

{if (name.startsWith("is")) { //plus, must return a boolean

Class> rt =am.getRawType();if (rt == Boolean.class || rt ==Boolean.TYPE) {returnstdNaming? stdManglePropertyName(name, 2)

: legacyManglePropertyName(name,2);

}

}return null;

}

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

stdManglePropertyName 就是原始输出。

legacyManglePropertyName 就是规范输出。

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

protected static String legacyManglePropertyName(final String basename, final intoffset)

{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) {returnbasename.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);

}returnsb.toString();

}

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

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

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

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

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

protected POJOPropertyBuilder _property(Mapprops,

String implName)

{

POJOPropertyBuilder prop=props.get(implName);if (prop == null) {

prop= newPOJOPropertyBuilder(_config, _annotationIntrospector, _forSerialization,

PropertyName.construct(implName));

props.put(implName, prop);

}returnprop;

}

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

ea81fc519e7d608d77aa5eda7323a511.png

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

f62aad13c8735accbbc4f23f09df355c.png

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

protected void _removeUnwantedProperties(Mapprops)

{

Iterator 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 classStudent {

@JsonProperty("BName")privateString bName;

}

测试结果如下:

org.junit.ComparisonFailure:

Expected :{"BName":"234234"}

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

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

第二个就是配置objectMapper的MapperFeature.USE_STD_BEAN_NAMIN 如上文提到了,非规范化输出。

如下代码:

@Testpublic void contextLoads() throwsIOException {

Student test= newStudent();

test.setBName("234234");

objectMapper.configure(MapperFeature.USE_STD_BEAN_NAMING,true);

String s=objectMapper.writeValueAsString(test);

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

}

第三个方案:重写PropertyNamingStrategy:

@Testpublic void contextLoads() throwsIOException {

Student test= newStudent();

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

objectMapper.setPropertyNamingStrategy(newPropertyNamingStrategy() {private static final long serialVersionUID = 1L;//反序列化时调用

@Overridepublic String nameForSetterMethod(MapperConfig>config,

AnnotatedMethod method, String defaultName) {return method.getName().substring(3);

}//序列化时调用

@Overridepublic String nameForGetterMethod(MapperConfig>config,

AnnotatedMethod method, String defaultName) {return method.getName().substring(3);

}

});

String s=objectMapper.writeValueAsString(test);

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

}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值