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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章