jackson序列化和反序列化

10/6/2024 jackson序列化

# 序列化

# springboot

Spring Boot默认使用Jackson作为其JSON处理库。当你使用@RestController@ResponseBody注解时,Spring Boot会自动使用Jackson来序列化返回的对象。同样,当接收JSON请求体时,也会使用Jackson进行反序列化。

# get、set方法

# lombok生成

属性首字母大写,拼接get/set 属性类型为boolean时特别,get方法为is拼接属性首字母大写

# idea生成

# jackson规则

懒加载+缓存策略

  1. 通过反射获取属性
  2. 获取get、set方法,与属性名匹配
  3. 移除不可见属性(非public的,ignore注解等)

Jackson的核心逻辑在POJOPropertiesCollector类中: com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector

/**
     * Internal method that will collect actual property information.
     *
     * @since 2.6
     */
    protected void collectAll()
    {
        LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>();

        // First: gather basic data
        final boolean isRecord = isRecordType();
        // 15-Jan-2023, tatu: [databind#3736] Let's avoid detecting fields of Records
        //   altogether (unless we find a good reason to detect them)
        // 17-Apr-2023: Need Records' fields for serialization for cases like [databind#3895] & [databind#3628]
        if (!isRecord || _forSerialization) {
            _addFields(props); // note: populates _fieldRenameMappings
        }
        _addMethods(props);
        // 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static
        //    inner classes, see [databind#1502]
        // 13-May-2023, PJ: Need to avoid adding creators for Records when serializing [databind#3925]
        if (!_classDef.isNonStaticInnerClass() && !(_forSerialization && isRecord)) {
            _addCreators(props);
        }

        // Remove ignored properties, first; this MUST precede annotation merging
        // since logic relies on knowing exactly which accessor has which annotation
        _removeUnwantedProperties(props);
        // and then remove unneeded accessors (wrt read-only, read-write)
        _removeUnwantedAccessor(props);

        // Rename remaining properties
        _renameProperties(props);

        // and now add injectables, but taking care to avoid overlapping ones
        // via creator and regular properties
        _addInjectables(props);

        // then merge annotations, to simplify further processing
        // 26-Sep-2017, tatu: Before 2.9.2 was done earlier but that prevented some of
        //   annotations from getting properly merged
        for (POJOPropertyBuilder property : props.values()) {
            property.mergeAnnotations(_forSerialization);
        }

        // And use custom naming strategy, if applicable...
        // 18-Jan-2021, tatu: To be done before trimming, to resolve
        //   [databind#3368]
        PropertyNamingStrategy naming = _findNamingStrategy();
        if (naming != null) {
            _renameUsing(props, naming);
        }

        // Sort by visibility (explicit over implicit); drop all but first of member
        // type (getter, setter etc) if there is visibility difference
        for (POJOPropertyBuilder property : props.values()) {
            property.trimByVisibility();
        }

        // and, if required, apply wrapper name: note, MUST be done after
        // annotations are merged.
        if (_config.isEnabled(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)) {
            _renameWithWrappers(props);
        }

        // well, almost last: there's still ordering...
        _sortProperties(props);
        _properties = props;
        _collected = true;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

属性名推断规则 Jackson会根据getter方法名推断属性名: 移除get,首字母改为小写,之后持续到首个小写字母 getPid => pid getIPhone => iphone

 /**
     * Method called to figure out name of the property, given
     * corresponding suggested name based on a method or field name.
     *
     * @param basename Name of accessor/mutator method, not including prefix
     *  ("get"/"is"/"set")
     */
    protected String legacyManglePropertyName(final String basename, final int offset)
    {
        final int end = basename.length();
        if (end == offset) { // empty name, nope
            return null;
        }
        char c = basename.charAt(offset);
        // 12-Oct-2020, tatu: Additional configurability; allow checking that
        //    base name is acceptable (currently just by checking first character)
        if (_baseNameValidator != null) {
            if (!_baseNameValidator.accept(c, basename, offset)) {
                return null;
            }
        }

        // next check: is the first character upper case? If not, return as is
        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();
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# 总结

# 常见问题

# 属性为null,不序列化

# 生成的json有多余字段

# 最佳实践

为避免潜在问题,建议使用@JsonProperty注解明确指定属性名: