gson遷移到jackson

概述

gson和jackson在使用上是很相似的,不論是直接和POJO互相轉換,還是逐個屬性解析Json文本,基本上沒有什麼差別。

但是由於對屬性名解析策略上有所不同,在遇到isXxx或者hasXxx形式的getter方法時就有差別了。

對於isXxx形式,gson生成的json會是isXxx或者is_xxx形式;而jackson生成的會是xxx形式,也就是自動刪除了is。

對於hasXxx形式,gson生成的是xxx形式;而jackson會忽略掉這個屬性,因爲jackson只識別getXxx和isXxx形式的getter方法。

如果,希望jackson能夠識別hasXxx形式,那麼需要給它加上@JsonProperty(“hasXxx”)註解。

當然了,一般情況下,我們用eclipse自帶的getter、setter創建工具,是不會出現hasXXX這樣的getter方法的。

jackson想指定屬性名字轉換成json的樣式可以這樣做:
1.使用@JsonProperty註解,指定字段名;
2.給ObjectMapper指定PropertyNamingStrategy,這種方式可以生成駝峯式、小寫+橫線連字符式、小寫+下劃線連字符式等多種形式;
3.使用自定義的PropertyNamingStrategy實現字段名轉換。

@JsonProperty註解方式

需要在特殊的字段上都加上註解:

@JsonProperty("hasChecked")
private boolean hasChecked;

@JsonProperty("isMyFault")
private boolean isMyFault;

指定PropertyNamingStrategy

jackson默認輸出的是駝峯形式字段名,可以通過指定PropertyNamingStrategy,輸出小寫+橫線連字符式、小寫+下劃線連字符式的字段名:

mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);

使用自定義的PropertyNamingStrategy

上邊說到了從gson轉到jackson時,isXxx類型的字段名輸出不一致。

如果恰好你們的項目裏面也遇到這樣的問題,可以用@JsonProperty的方式明確指定字段名,如果字段多的話就是稍微麻煩點,問題也不大。

但是如果像我們的項目一樣,就慘了,我們系統提供pc的spring mvc服務,你知道spring mvc默認使用的是jackson返回json數據的。

然而,我們的項目同時提供內部使用的rest接口,接口返回的json是用gson返回的(自己封裝了一個netty框架)。數據Model共享,導致同一個字段在pc端返回的需要是駝峯形式,在內部rest接口返回小寫+下劃線方式。

可想而知,用@JsonProperty是不行的了,不能做到兩方兼顧。

這時候就可以使用第三種方式,自定義PropertyNamingStrategy來實現了:

mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
            @Override
            public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
                String input = defaultName;
                if(method.getName().startsWith("is")){
                    input = method.getName();
                }

                if (input == null) return input; 
                int length = input.length();
                StringBuilder result = new StringBuilder(length * 2);
                int resultLength = 0;
                boolean wasPrevTranslated = false;
                for (int i = 0; i < length; i++)
                {
                    char c = input.charAt(i);
                    if (i > 0 || c != '_') // skip first starting underscore
                    {
                        if (Character.isUpperCase(c))
                        {
                            if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
                            {
                                result.append('_');
                                resultLength++;
                            }
                            c = Character.toLowerCase(c);
                            wasPrevTranslated = true;
                        }
                        else
                        {
                            wasPrevTranslated = false;
                        }
                        result.append(c);
                        resultLength++;
                    }
                }
                return resultLength > 0 ? result.toString() : input;
            }
        });

20170825補充

現實場景一般來說會複雜一些,我們遇到的情況如下:

public class EclipseAutoMethodName {
    private boolean isOutlets;
    private boolean overSea;

    public boolean isOutlets() {
        return isOutlets;
    }
    public void setOutlets(boolean isOutlets) {
        this.isOutlets = isOutlets;
    }

    public boolean isOverSea() {
        return overSea;
    }
    public void setOverSea(boolean overSea) {
        this.overSea = overSea;
    }
}

其中的getter和setter方法都是eclipse自動生成的,gson的處理方法是field優先。它會解析成:

{"isOutlets":true,"overSea":false}

jackson不指定PropertyNamingStrategy的情況下會解析成:

{"outlets":true,"overSea":false}

也就是,會自動擦除is。

如果使用本文中給的PropertyNamingStrategy實現,那麼會被jackson解析成:

{"isOutlets":true,"isOverSea":false}

因爲jackson是Getter方法優先,所以,如果你的歷史代碼中有這種字段名以is開頭,也有非is開頭的boolean屬性的時候,唯一的處理方法就是把以is開頭的boolean屬性的getter方法前加get,如下:

public class EclipseAutoMethodName {
    private boolean isOutlets;
    private boolean overSea;

    public boolean getIsOutlets() {
        return isOutlets;
    }
    public void setOutlets(boolean isOutlets) {
        this.isOutlets = isOutlets;
    }

    public boolean isOverSea() {
        return overSea;
    }
    public void setOverSea(boolean overSea) {
        this.overSea = overSea;
    }
}
發佈了141 篇原創文章 · 獲贊 140 · 訪問量 68萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章