jackson|jackson json序列化实现首字母大写,第二个字母需小写
jackson json序列化首字母大写,第二个字母需小写
有这样一个类:
@Setter@Getter@JsonNaming(value = https://www.it610.com/article/PropertyNamingStrategy.UpperCamelCaseStrategy.class)public class Student {private String bName; }
序列化后,希望首字母大写,如下面的测试代码:
@Testpublic void contextLoads() throws IOException {Student test = new Student(); test.setBName("234234"); String s = objectMapper.writeValueAsString(test); Assert.assertEquals("{\"BName\":\"234234\"}", s); }
可实际运行后,结果与希望不一样:
org.junit.ComparisonFailure:jackson在序列化时把第二个大写字母n转成了小写,这是为什么呢?
Expected :{"BName":"234234"}
Actual :{"Bname":"234234"}
以下是跟踪源码的过程:
直接找到:com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector#collectAll这个方法:
文章图片
执行完_addFields(props)方法后:
【jackson|jackson json序列化实现首字母大写,第二个字母需小写】
文章图片
执行完_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 booleanClass> 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, nopereturn null; }// next check: is the first character upper case? If not, return as ischar 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 charStringBuilder 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 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 props){Iterator it = props.values().iterator(); while (it.hasNext()) {POJOPropertyBuilder prop = it.next(); // First: if nothing visible, just remove altogetherif (!prop.anyVisible()) {it.remove(); continue; }// Otherwise, check ignoralsif (prop.anyIgnorals()) {// first: if one or more ignorals, and no explicit markers, remove the whole thingif (!prop.isExplicitlyIncluded()) {it.remove(); _collectIgnorals(prop.getName()); continue; }// otherwise just remove ones marked to be ignoredprop.removeIgnored(); if (!prop.couldDeserialize()) {_collectIgnorals(prop.getName()); }}}}
只剩第二记录bname,再首字母大写,所以就是Bname了。
解决方案: 第一个就是JsonProperty
@Setter@Getter@JsonNaming(value = https://www.it610.com/article/PropertyNamingStrategy.UpperCamelCaseStrategy.class)public class Student {@JsonProperty("BName")private String bName; }
测试结果如下:
org.junit.ComparisonFailure:虽然生成了BName,但是Bname仍在(加了JsonProperty就visable了)。
Expected :{"BName":"234234"}
Actual :{"Bname":"234234","BName":"234234"}
第二个就是配置
objectMapper的MapperFeature.USE_STD_BEAN_NAMIN如上文提到了,非规范化输出。
如下代码:
@Testpublic 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:
@Testpublic 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; // 反序列化时调用@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的配置,要注意对其他功能的影响。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
推荐阅读
- 使用sqlalchemy|使用sqlalchemy orm 的model序列化,解决返回model的异常
- 331.|331. 验证二叉树的前序序列化
- WebAPI|WebAPI 多对多对象输出json异常()
- Python|Python 小项目2 使用JSON API并处理数据
- MySQL|MySQL 5.7 JSON特性支持
- 解析json用的Gson解析出现异常记录问题
- node使用JsonWebToken|node使用JsonWebToken 生成token,完成用户登录、登录检测
- python学习笔记-tip52(io编程--JSON)
- 循环遍历JSON数据
- Jquery表单序列化json+批量判断是否为空