jackson json序列化實(shí)現(xiàn)首字母大寫(xiě),第二個(gè)字母需小寫(xiě)
jackson json序列化首字母大寫(xiě),第二個(gè)字母需小寫(xiě)
有這樣一個(gè)類(lèi):
@Setter @Getter @JsonNaming(value = PropertyNamingStrategy.UpperCamelCaseStrategy.class) public class Student { private String bName; }
序列化后,希望首字母大寫(xiě),如下面的測(cè)試代碼:
@Test public void contextLoads() throws IOException { Student test = new Student(); test.setBName("234234"); String s = objectMapper.writeValueAsString(test); Assert.assertEquals("{\"BName\":\"234234\"}", s); }
可實(shí)際運(yùn)行后,結(jié)果與希望不一樣:
org.junit.ComparisonFailure:
Expected :{"BName":"234234"}
Actual :{"Bname":"234234"}
jackson在序列化時(shí)把第二個(gè)大寫(xiě)字母n轉(zhuǎn)成了小寫(xiě),這是為什么呢?
以下是跟蹤源碼的過(guò)程:
直接找到:com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector#collectAll這個(gè)方法:
執(zhí)行完_addFields(props)方法后:
執(zhí)行完_addMethods(props)方法后:
一個(gè)是bName,一個(gè)是bname;
第一個(gè)bName取的是字段的名稱(chēng),
第二個(gè)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; }
根據(jù)stdNaming來(lái)決定這個(gè)name是以什么標(biāo)準(zhǔn)輸出,默認(rèn)的是false;
stdManglePropertyName 就是原始輸出。
legacyManglePropertyName 就是規(guī)范輸出。
下面的代碼就是規(guī)范輸出:
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循環(huán)中,去除set后,第一個(gè)字母小寫(xiě),
第二字母小寫(xiě)后,與第二個(gè)字母比較,如果都是小寫(xiě),則直接接上,返回,
如果第二字母大寫(xiě),就如我們的這種情況,就以小寫(xiě)的情況,接上,再去找下一個(gè)字母,直到找到小寫(xiě)字母為止。
意思就是為了滿足駝峰命名規(guī)則,要規(guī)范輸出。
如果我們的字段命名正如它的規(guī)范的話,props是只有一條記錄的,因?yàn)椋好Q(chēng)相同,就不插入了,由于咱們的名稱(chēng)不同,所以就有兩條記錄。
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; }
可是我們輸出中只有一條,沒(méi)有bName這條,
其實(shí)在是這里把第一條刪除了。因?yàn)椋?/p>
這些屬性為空,導(dǎo)致這個(gè)字段不可見(jiàn):
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,再首字母大寫(xiě),所以就是Bname了。
解決方案:
第一個(gè)就是JsonProperty
@Setter @Getter @JsonNaming(value = PropertyNamingStrategy.UpperCamelCaseStrategy.class) public class Student { @JsonProperty("BName") private String bName; }
測(cè)試結(jié)果如下:
org.junit.ComparisonFailure:
Expected :{"BName":"234234"}
Actual :{"Bname":"234234","BName":"234234"}
雖然生成了BName,但是Bname仍在(加了JsonProperty就visable了)。
第二個(gè)就是配置
objectMapper的MapperFeature.USE_STD_BEAN_NAMIN如上文提到了,非規(guī)范化輸出。
如下代碼:
@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); }
第三個(gè)方案:重寫(xiě)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; // 反序列化時(shí)調(diào)用 @Override public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) { return method.getName().substring(3); } // 序列化時(shí)調(diào)用 @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的配置,要注意對(duì)其他功能的影響。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
JWT 設(shè)置token過(guò)期時(shí)間無(wú)效的解決
這篇文章主要介紹了JWT 設(shè)置token過(guò)期時(shí)間無(wú)效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07SpringBoot實(shí)現(xiàn)發(fā)送郵件任務(wù)
這篇文章主要為大家詳細(xì)介紹了SpringBoot實(shí)現(xiàn)發(fā)送郵件任務(wù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02Java設(shè)計(jì)模塊系列之書(shū)店管理系統(tǒng)單機(jī)版(二)
這篇文章主要為大家詳細(xì)介紹了Java單機(jī)版的書(shū)店管理系統(tǒng)設(shè)計(jì)模塊和思想第二章,感興趣的小伙伴們可以參考一下2016-08-08詳談@Cacheable不起作用的原因:bean未序列化問(wèn)題
這篇文章主要介紹了@Cacheable不起作用的原因:bean未序列化問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01使用Spring啟動(dòng)時(shí)運(yùn)行自定義業(yè)務(wù)
這篇文章主要介紹了使用Spring啟動(dòng)時(shí)運(yùn)行自定義業(yè)務(wù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07SpringBoot項(xiàng)目中使用緩存Cache的正確方法分享
緩存可以通過(guò)將經(jīng)常訪問(wèn)的數(shù)據(jù)存儲(chǔ)在內(nèi)存中,減少底層數(shù)據(jù)源如數(shù)據(jù)庫(kù)的壓力,從而有效提高系統(tǒng)的性能和穩(wěn)定性。本文就來(lái)講講SpringBoot項(xiàng)目中使用緩存Cache的正確姿勢(shì)吧2023-04-04Spring的Bean注入解析結(jié)果BeanDefinition詳解
這篇文章主要介紹了Spring的Bean注入解析結(jié)果BeanDefinition詳解,BeanDefinition描述了一個(gè)bean實(shí)例,擁有屬性值、構(gòu)造參數(shù)值和具體實(shí)現(xiàn)的其他信息,其是一個(gè)bean的元數(shù)據(jù),xml中配置的bean元素會(huì)被解析成BeanDefinition對(duì)象,需要的朋友可以參考下2023-12-12Spring Boot使用JSR-380進(jìn)行校驗(yàn)的示例
這篇文章主要介紹了Spring Boot使用JSR-380進(jìn)行校驗(yàn),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03Spring?Boot?實(shí)現(xiàn)Redis分布式鎖原理
這篇文章主要介紹了Spring?Boot實(shí)現(xiàn)Redis分布式鎖原理,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-08-08