`
后来我们都老了
  • 浏览: 34662 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Jackson序列化之自动检测

    博客分类:
  • java
阅读更多

一、背景

今天线上出现了一个问题,使用springMVC RestController接口返回json数据给客户端,发现其中某一个model中的所有属性,被序列化了两遍,并且一次是大写开头,一次是小写,部分结构如下:

                "promotionTags": [
                    {
                        "CornerRadius": 1,
                        "TitleFontSize": 10,
                        "Title": "返券",
                        "TitleColor": "#FF9900",
                        "Transparent": true,
                        "BackgroundColor": "#FF9900",
                        "Border": true,
                        "Transparent": true,
                        "Border": true,
                        "cornerRadius": 1,
                        "titleFontSize": 10,
                        "title": "返券",
                        "titleColor": "#FF9900",
                        "transparent": true,
                        "backgroundColor": "#FF9900",
                        "border": true,
                        "transparent": true,
                        "border": true
                    }
                ]

 model结构如下:

 

 

public class HotelLabelModel implements Serializable {

    private Double CornerRadius;

    private String BorderColor;

    private Integer TitleFontSize;

    private String Title;

    private String TitleColor;

    private Boolean Transparent;

    private String BackgroundColor;

    private Boolean Border;

    getter and setter ...
}

 对springMVC序列化做了简单的配置,如下:

public class HotelMappingJacksonHttpMessageConverter extends MappingJackson2HttpMessageConverter {

    public HotelMappingJacksonHttpMessageConverter() {
        super();
        this.setSelfConfiguration();
    }

    public HotelMappingJacksonHttpMessageConverter(ObjectMapper objectMapper) {
        super(objectMapper);
        this.setSelfConfiguration();
    }

    private void setSelfConfiguration() {
        // 任何属性可见
        super.getObjectMapper().setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
        // 过滤null
        super.getObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }
}

二、验证

问题比较容易还原,简单demo如下:

 

public class JacksonTest {

    public static void main(String[] args) throws JsonProcessingException {
        //name content age
        UserBean userBean = new UserBean("Li Lei", "I am Li Lei", 20);

        //jackson序列化
        ObjectMapper objectMapper = new ObjectMapper();
        //设置任何字段可见
        objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
        System.out.println(objectMapper.writeValueAsString(userBean));
    }
}
public class UserBean {

    private String Name;

    private String Content;

    private Integer Age;

    public UserBean(String name, String content, Integer age) {
        Name = name;
        Content = content;
        Age = age;
    }

    getter and setter ...
} 
输出结果:{"Name":"Li Lei","Content":"I am Li Lei","Age":20,"name":"Li Lei","content":"I am Li Lei","age":20}

跟踪源码,在类POJOPropertiesCollector中发现

protected void collectAll(){
        LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>();

        // First: gather basic data
        _addFields(props);
        _addMethods(props);
       ...
}

 属性序列化会判断属性的访问权限,在此就不大量的贴源码了

protected void _addFields(Map<String, POJOPropertyBuilder> props)
    {
            ...
            // having explicit name means that field is visible; otherwise need to check the rules
            boolean visible = (pn != null);
            if (!visible) {
                visible = _visibilityChecker.isFieldVisible(f);
            }
            ...
    }

 根据get/set方法来序列化

protected void _addMethods(Map<String, POJOPropertyBuilder> props)
    {
        final AnnotationIntrospector ai = _annotationIntrospector;
        
        for (AnnotatedMethod m : _classDef.memberMethods()) {
            
            int argCount = m.getParameterCount();
            if (argCount == 0) { // getters (including 'any getter')
            	_addGetterMethod(props, m, ai);
            } else if (argCount == 1) { // setters
            	_addSetterMethod(props, m, ai);
            } else if (argCount == 2) { // any getter?
                if (ai != null  && ai.hasAnySetterAnnotation(m)) {
                    if (_anySetters == null) {
                        _anySetters = new LinkedList<AnnotatedMethod>();
                    }
                    _anySetters.add(m);
                }
            }
        }
    }

 如下get方法,通过反射拿到方法名,截取get后面的名称toLowerCase后,作为序列化的name

 

public static String okNameForRegularGetter(AnnotatedMethod am, String name,
            boolean stdNaming)
    {
        if (name.startsWith("get")) {
            
            if ("getCallbacks".equals(name)) {
                if (isCglibGetCallbacks(am)) {
                    return null;
                }
            } else if ("getMetaClass".equals(name)) {
                // 30-Apr-2009, tatu: Need to suppress serialization of a cyclic reference
                if (isGroovyMetaClassGetter(am)) {
                    return null;
                }
            }
            return stdNaming
                    ? stdManglePropertyName(name, 3)
                    : legacyManglePropertyName(name, 3);
        }
        return null;
    } 
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();
    }

 

jackson是根据反射获取model的属性和get/set方法,序列化的顺序为字段、方法(get/set),默认只序列化public修饰的字段和public修饰的get/set方法。

所以上面输出结果输出了两遍的结论是第一遍输出的是属性的序列化内容,第二遍输出的是get方法的序列化内容。

定位到了问题,解决方案就比较简单了,方案有很多,如:换屏蔽掉get/set方法的序列化、换用Google的Gson序列化等等,为了减少风险,我选用了第一种方案。

demo修改如下:

 

public class JacksonTest {

    public static void main(String[] args) throws JsonProcessingException {
        UserBean userBean = new UserBean("Li Lei", "I am Li Lei", 20);

        //jackson序列化
        ObjectMapper objectMapper = new ObjectMapper();
        //屏蔽get方法的序列化
        objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
        //设置任何属性可见
        objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
        System.out.println(objectMapper.writeValueAsString(userBean));
    }
}
输出结果:{"Name":"Li Lei","Content":"I am Li Lei","Age":20}

 

 

三、解决方案

项目中修改springMVC的配置信息,如下:

 

public class HotelMappingJacksonHttpMessageConverter extends MappingJackson2HttpMessageConverter {

    public HotelMappingJacksonHttpMessageConverter() {
        super();
        this.setSelfConfiguration();
    }

    public HotelMappingJacksonHttpMessageConverter(ObjectMapper objectMapper) {
        super(objectMapper);
        this.setSelfConfiguration();
    }

    private void setSelfConfiguration() {
        // 任何属性可见
        super.getObjectMapper().setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
        // 屏蔽get方法
        super.getObjectMapper().setVisibility(PropertyAccessor.GETTER, Visibility.NONE);
        // 屏蔽null
        super.getObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }
}

 预发布环境测试通过,上线。

 

四、总结

jackson默认的检测机制如下:public修饰的字段->public修饰的getter/的setter,在使用jackson作为序列化工具的时候要注意属性和方法的修饰权限。

选型jackson是考虑稳定性和性能的平衡点,Fastjson bug比较多,gson非常强大但效率比较低,如对序列化没有特殊要求尽量选用gson或gson & jackson结合使用。

 

0
0
分享到:
评论

相关推荐

    Json反序列化

    3. 错误处理:反序列化过程可以自动检测并处理JSON格式错误,提供更好的错误反馈。 三、常见的JSON反序列化库 1. Java:Jackson、Gson、org.json等 2. Python:json模块、ujson、simplejson等 3. JavaScript:JSON....

    jackson注解包2.2.3.rar

    1. `@JsonAutoDetect`: 这个全局注解可以控制Jackson的行为,比如哪些字段应该被自动检测并序列化或反序列化。 2. `@JsonProperty`: 用于标记Java字段,指定其在JSON序列化和反序列化时的映射关系。你可以设置其...

    通过主动和被动扫描自动识别Java和.NET应用程序中的反序列化问题

    本文将深入探讨Java和.NET应用程序中的反序列化问题,并提供主动和被动扫描的方法来检测这些问题。 首先,理解反序列化漏洞的基础是关键。当一个应用程序接受来自不可信源的序列化数据时,如果没有正确地验证这些...

    shiro反序列化工具,加强版

    1. **使用安全的反序列化库**:如使用Jackson库时,可以启用`@JsonTypeInfo`和`@JsonSubTypes`注解来限制可反序列化的类型。 2. **过滤输入数据**:在接收反序列化的数据前,应先进行校验和过滤,确保数据符合预期...

    jackson-annotations-2.2.3.jar.zip

    4. `@JsonAutoDetect`: 设置默认的可见性级别,决定哪些字段、方法可以被Jackson自动检测并处理。 5. `@JsonTypeInfo` 和 `@JsonSubTypes`: 这两个注解用于处理多态性,帮助Jackson识别子类类型信息并在反序列化时...

    jackson的jar包

    5. **Jackson-module-afterburner**:这是一个性能优化模块,可以自动检测和处理常见的POJO(Plain Old Java Object)模式,提高序列化和反序列化的速度。 6. **Jackson-modules-java8**:对于Java 8的新特性,如...

    SSM框架json使用jackson

    `@JsonInclude`可以避免序列化null值,而`@JsonAutoDetect`定义了何时自动检测字段、方法和构造函数。 5. **`@JsonFormat`**:用于格式化日期和时间,可以指定特定的日期时间格式。 在SSM中,Jackson的使用通常...

    jackson2.5.0 jar

    例如,`@JsonAutoDetect`可以控制哪些字段和方法应该被自动检测,`@JsonProperty`用于指定字段与JSON属性的映射,`@JsonIgnore`则用于忽略特定字段不参与序列化或反序列化过程。 2. **jackson-core**: jackson-...

    jackson-2.11.0.zip

    例如,`@JsonProperty`用于指定属性与JSON字段的映射,`@JsonInclude`用于控制哪些属性应该被序列化,`@JsonAutoDetect`用于设置默认的可见性和存在检测策略等。通过这些注解,开发者可以精确控制JSON序列化过程,...

    jackson-jar包最新2.9.2版本

    例如,`@JsonInclude`控制哪些属性在序列化时应该包含,`@JsonAutoDetect`定义了哪些字段的访问级别会被Jackson检测,`@JsonFormat`用于格式化日期和时间等。 在实际开发中,这3个模块经常一起使用,以实现完整的...

    jackson-core-asl-1.9.13

    Jackson是Java领域中广泛使用的JSON处理库,它提供了一套高效、灵活的API来解析、生成、序列化和反序列化JSON数据。标题中的"jackson-core-asl-1.9.13"和"jackson-mapper-asl-1.9.13"是Jackson库的两个关键组件,...

    jackson-core-asl

    2. **自动类型检测**:Jackson能够自动检测JSON数据类型并映射到对应的Java类型,比如将JSON字符串映射为String,JSON数字映射为Integer或Double等。 3. **注解支持**:Jackson支持丰富的注解,如`@JsonProperty`、...

    SpringMVC使用JSON的Jackson的jar包

    虽然SpringMVC通常会自动检测并使用Jackson,但有时可能需要手动配置。这可以通过在`WebApplicationContext`中添加`MappingJackson2HttpMessageConverter` bean来完成,或者在XML配置中指定`...

    spring4 jackson2.5.3.jar 包

    配置完成后,Spring会自动检测到Jackson的存在,并在需要的时候使用它。你可以在控制器的方法中定义模型类作为参数,Spring会自动将JSON请求体转换为该模型类的对象。同样,当返回JSON响应时,只需要返回一个Java...

    jackson 2.7.0 jar

    Jackson是Java领域中广泛使用的JSON处理库,它提供了一套高效、灵活的API来解析、生成、序列化和反序列化JSON数据。标题中的"jackson 2.7.0 jar"指的是Jackson库的2.7.0版本的Java Archive(JAR)文件,这个版本的...

    Java之Jackson的基本使用案例讲解.pdf

    另外,`@JsonInclude`和`@JsonAutoDetect`等注解可以控制序列化时哪些字段应被包含,以及访问修饰符的检测。 总之,Jackson作为Java中的JSON处理库,提供了强大的功能,包括高效的序列化和反序列化、丰富的注解支持...

    jackson1.9.10下载

    `ObjectMapper`是此模块的核心类,它实现了自动类型检测、转换和映射机制。使用`ObjectMapper`,开发者可以轻松地实现诸如序列化对象到JSON字符串、反序列化JSON字符串到Java对象等操作。此外,`jackson-mapper-asl`...

    jackson json2.7

    比如,`@JsonAutoDetect`可以控制哪些字段和方法被自动检测,`@JsonCreator`可以指定构造函数或工厂方法作为JSON创建对象的入口,`@JsonFormat`可以定义日期、时间等的序列化格式。2.7.0版本的`jackson-annotations`...

    jackjson 2.2.3 jar 全部

    `ObjectMapper`可以自动检测并应用`jackson-annotations`中的注解,从而简化了数据绑定的过程。此外,`jackson-databind`还支持类型转换、泛型处理、日期和时间格式化等多种高级特性。 在实际开发中,这三个模块...

    基于混合分析的Java反序列化利用链挖掘方法

    总的来说,基于混合分析的Java反序列化利用链挖掘方法是针对Java应用安全的一种重要技术进步,它通过自动化工具减轻了人工分析的负担,提升了漏洞检测的能力,对于保障Java应用的安全具有重要意义。这一方法的应用有...

Global site tag (gtag.js) - Google Analytics