Jackson 工具类使用及配置指南

目录

  1. 前言
  2. Jackson使用工具类
  3. Jackson配置属性
  4. Jackson解析JSON数据
  5. Jackson序列化Java对象

前言

Json数据格式这两年发展的很快,其声称相对XML格式有很对好处:html

  • 容易阅读;
  • 解析速度快;
  • 占用空间更少。

不过,JSON 和 XML二者纠结谁优谁劣,这里不作讨论,能够参见知乎上为何XML这么笨重的数据结构仍在普遍应用?java

最近在项目中,会有各类解析JSON文本的需求,使用第三方Jackson工具来解析时,又担忧当增长会减小字段,会不会出现非预期的状况,所以,研究下jackson解析json数据格式的代码,颇有须要。git

Jackson使用工具类

一般,咱们对json格式的数据,只会进行解析和封装两种,也就是json字符串--->java对象以及java对象---> json字符串github

public class JsonUtils {
    /**
 * Logger for this class
 */
    private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);

    private final static ObjectMapper objectMapper = new ObjectMapper();

    static {
        objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
        objectMapper.configure(JsonParser.Feature.INTERN_FIELD_NAMES, true);
        objectMapper.configure(JsonParser.Feature.CANONICALIZE_FIELD_NAMES, true);
        objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    private JsonUtils() {
    }

    public static String encode(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (JsonGenerationException e) {
            logger.error("encode(Object)", e); //$NON-NLS-1$
        } catch (JsonMappingException e) {
            logger.error("encode(Object)", e); //$NON-NLS-1$
        } catch (IOException e) {
            logger.error("encode(Object)", e); //$NON-NLS-1$
        }
        return null;
    }

    /**
 * 将json string反序列化成对象
 *
 * @param json
 * @param valueType
 * @return
 */
    public static <T> T decode(String json, Class<T> valueType) {
        try {
            return objectMapper.readValue(json, valueType);
        } catch (JsonParseException e) {
            logger.error("decode(String, Class<T>)", e);
        } catch (JsonMappingException e) {
            logger.error("decode(String, Class<T>)", e);
        } catch (IOException e) {
            logger.error("decode(String, Class<T>)", e);
        }
        return null;
    }

    /**
 * 将json array反序列化为对象
 *
 * @param json
 * @param jsonTypeReference
 * @return
 */
    @SuppressWarnings("unchecked")
    public static <T> T decode(String json, TypeReference<T> typeReference) {
        try {
            return (T) objectMapper.readValue(json, typeReference);
        } catch (JsonParseException e) {
            logger.error("decode(String, JsonTypeReference<T>)", e);
        } catch (JsonMappingException e) {
            logger.error("decode(String, JsonTypeReference<T>)", e);
        } catch (IOException e) {
            logger.error("decode(String, JsonTypeReference<T>)", e);
        }
        return null;
    }

}

Jackson配置属性

若是上面的工具类实例,在Jackson中存在一些属性配置,这些配置决定了最后在解析或者编码后数据视图。所以,在分析Jackson以前,先了解下,Jackson具备的一些配置含义。json

JsonParser解析相关配置属性

JsonParser将JSON 数据格式的String字符串,解析成为Java对象。Jackson在解析的时候,对于一些非JSON官方文档支持的属性,则须要经过一些配置才能够被Jackson工具解析成对象。数组

/**
 * Enumeration that defines all togglable features for parsers.
 */
    public enum Feature {

        // // // Low-level I/O handling features:

        /**
 * 这个特性,决定了解析器是否将自动关闭那些不属于parser本身的输入源。 若是禁止,则调用应用不得不分别去关闭那些被用来建立parser的基础输入流InputStream和reader;
 * 若是容许,parser只要本身须要获取closed方法(当遇到输入流结束,或者parser本身调用 JsonParder#close方法),就会处理流关闭。
 *
 * 注意:这个属性默认是true,即容许自动关闭流
 *
 */
        AUTO_CLOSE_SOURCE(true),

        // // // Support for non-standard data format constructs

        /**
 * 该特性决定parser将是否容许解析使用Java/C++ 样式的注释(包括'/'+'*' 和'//' 变量)。 因为JSON标准说明书上面没有提到注释是不是合法的组成,因此这是一个非标准的特性;
 * 尽管如此,这个特性仍是被普遍地使用。
 *
 * 注意:该属性默认是false,所以必须显式容许,即经过JsonParser.Feature.ALLOW_COMMENTS 配置为true。
 *
 */
        ALLOW_COMMENTS(false),

        /**
 * 这个特性决定parser是否将容许使用非双引号属性名字, (这种形式在Javascript中被容许,可是JSON标准说明书中没有)。
 *
 * 注意:因为JSON标准上须要为属性名称使用双引号,因此这也是一个非标准特性,默认是false的。
 * 一样,须要设置JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES为true,打开该特性。
 *
 */
        ALLOW_UNQUOTED_FIELD_NAMES(false),

        /**
 * 该特性决定parser是否容许单引号来包住属性名称和字符串值。
 *
 * 注意:默认下,该属性也是关闭的。须要设置JsonParser.Feature.ALLOW_SINGLE_QUOTES为true
 *
 */
        ALLOW_SINGLE_QUOTES(false),

        /**
 * 该特性决定parser是否容许JSON字符串包含非引号控制字符(值小于32的ASCII字符,包含制表符和换行符)。 若是该属性关闭,则若是遇到这些字符,则会抛出异常。
 * JSON标准说明书要求全部控制符必须使用引号,所以这是一个非标准的特性。
 *
 * 注意:默认时候,该属性关闭的。须要设置:JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS为true。
 *
 */
        ALLOW_UNQUOTED_CONTROL_CHARS(false),

        /**
 * 该特性能够容许接受全部引号引发来的字符,使用‘反斜杠\’机制:若是不容许,只有JSON标准说明书中 列出来的字符能够被避开约束。
 *
 * 因为JSON标准说明中要求为全部控制字符使用引号,这是一个非标准的特性,因此默认是关闭的。
 *
 * 注意:通常在设置ALLOW_SINGLE_QUOTES属性时,也设置了ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER属性,
 * 因此,有时候,你会看到不设置ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER为true,可是依然能够正常运行。
 *
 * @since 1.6
 */
        ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false),

        /**
 * 该特性决定parser是否容许JSON整数以多个0开始(好比,若是000001赋值给json某变量,
 * 若是不设置该属性,则解析成int会抛异常报错:org.codehaus.jackson.JsonParseException: Invalid numeric value: Leading zeroes not
 * allowed)
 *
 * 注意:该属性默认是关闭的,若是须要打开,则设置JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS为true。
 *
 * @since 1.8
 */
        ALLOW_NUMERIC_LEADING_ZEROS(false),

        /**
 * 该特性容许parser能够识别"Not-a-Number" (NaN)标识集合做为一个合法的浮点数。 例如: allows (tokens are quoted contents, not including
 * quotes):
 * <ul>
 * <li>"INF" (for positive infinity), as well as alias of "Infinity"
 * <li>"-INF" (for negative infinity), alias "-Infinity"
 * <li>"NaN" (for other not-a-numbers, like result of division by zero)
 * </ul>
 */

        ALLOW_NON_NUMERIC_NUMBERS(false),

        // // // Controlling canonicalization (interning etc)

        /**
 * 该特性决定JSON对象属性名称是否能够被String#intern 规范化表示。
 *
 * 若是容许,则JSON全部的属性名将会 intern() ;若是不设置,则不会规范化,
 *
 * 默认下,该属性是开放的。此外,必须设置CANONICALIZE_FIELD_NAMES为true
 *
 * 关于intern方法做用:当调用 intern 方法时,若是池已经包含一个等于此 String 对象的字符串 (该对象由 equals(Object) 方法肯定),则返回池中的字符串。不然,将此 String
 * 对象添加到池中, 而且返回此 String 对象的引用。
 *
 * @since 1.3
 */
        INTERN_FIELD_NAMES(true),

        /**
 * 该特性决定JSON对象的属性名称是否被规范化。
 *
 * @since 1.5
 */
        CANONICALIZE_FIELD_NAMES(true),

        ;

        final boolean _defaultState;

        /**
 * Method that calculates bit set (flags) of all features that are enabled by default.
 */
        public static int collectDefaults() {
            int flags = 0;
            for (Feature f : values()) {
                if (f.enabledByDefault()) {
                    flags |= f.getMask();
                }
            }
            return flags;
        }

        private Feature(boolean defaultState) {
            _defaultState = defaultState;
        }

        public boolean enabledByDefault() {
            return _defaultState;
        }

        public boolean enabledIn(int flags) {
            return (flags & getMask()) != 0;
        }

        public int getMask() {
            return (1 << ordinal());
        }
    };

Note: 在枚举最后有一个公共静态方法collectDefaults(),这个方法返回一个整形,整形包含的是全部枚举项对应位bit为初始默认值(true:1;false:0),若是默认属性为true,则经过对1 << ordinal()的值和flags进行亦或来置位。缓存

DeserializationConfig反序列化相关配置属性

将Java 对象序列化为Json字符串。Jackson在序列化Java对象的时候,对于有些不存在的属性处理,以及一些类型转换等,均可以经过配置来设置。数据结构

/**
 * Enumeration that defines togglable features that guide the serialization feature.
 */
    public enum Feature implements MapperConfig.ConfigFeature {
        /*
 * /****************************************************** Introspection features
 * /******************************************************
 */

        /**
 * 该特性决定是否启动内部注解功能支持配置;若是容许,则使用AnnotationIntrospector扫描配置,不然,不考了注解配置。
 *
 * 默认启动该功能配置属性。
 * 
 * @since 1.2
 */
        USE_ANNOTATIONS(true),

        /**
 * 该特性决定是否使用“getter”方法来根据标准bean命名转换方式来自动检测。若是true,则全部公共的带有一个参数
 * 而且前缀为set的方法都将被当作setter方法。若是false,只会把显式注解的做为setter方法。
 *
 * 注意: 这个特性的优先级低于显式注解,而且只会在获取不到更细粒度配置的状况下。
 *
 */
        AUTO_DETECT_GETTERS(true),                        DETECT_IS_GETTERS(true),

        /**
 * 该特性决定是否使用creator方法来根据公共构造函数以及名字为“valueOf”的静态单参数方法自动检测。
 *
 * 注意:这个特性比每一个类上注解的优先级要低。
 *
 */
        AUTO_DETECT_CREATORS(true),

        /**
 * 这个特性决定是否非静态field被当作属性。若是true,则全部公共成员field都被当作属性, 不然只有注解,才会被当作属性field。
 *
 */
        AUTO_DETECT_FIELDS(true),

        /**
 * 使用getter方法来做为setter方法(通常只处理集合和Maps,和其余没有setter的类型)。 该属性决定是否不须要setter方法,而只须要getter方法来修改属性。
 * 
 * 注意:该配置优先级低于setter。
 */
        USE_GETTERS_AS_SETTERS(true),

        /**
 * 该特性决定当访问属性时候,方法和field访问是否修改设置。 若是设置为true,则经过反射调用方法AccessibleObject#setAccessible 来容许访问不能访问的对象。
 * 
 */
        CAN_OVERRIDE_ACCESS_MODIFIERS(true),                GETTERS(false),

        /*
 * /****************************************************** /* Type conversion features
 * /******************************************************
 */

        /**
 * 该特性决定对于json浮点数,是否使用BigDecimal来序列化。若是不容许,则使用Double序列化。
 * 
 * 注意:该特性默认是关闭的,由于性能上来讲,BigDecimal低于Double。
 *
 */
        USE_BIG_DECIMAL_FOR_FLOATS(false),

        /**
 * 该特性决定对于json整形(非浮点),是否使用BigInteger来序列化。若是不容许,则根据数值大小来肯定 是使用Integer}, {@link Long} 或者
 * {@link java.math.BigInteger}
 *
 */
        USE_BIG_INTEGER_FOR_INTS(false),

        // [JACKSON-652]
        /**
 * 该特性决定JSON ARRAY是映射为Object[]仍是List<Object>。若是开启,都为Object[],false时,则使用List。
 *
 * @since 1.9
 */
        USE_JAVA_ARRAY_FOR_JSON_ARRAY(false),

        /**
 * 该特性决定了使用枚举值的标准序列化机制:若是容许,则枚举假定使用Enum.toString()返回的值做为序列化结构;若是禁止, 则返回Enum.name()的值。
 *
 * 注意:默认使用的时Enum.name()的值做为枚举序列化结果。这个的设置和WRITE_ENUMS_USING_TO_STRING须要一致。
 *
 * For further details, check out [JACKSON-212]
 * 
 * @since 1.6
 */
        READ_ENUMS_USING_TO_STRING(false),

        /*
 * /****************************************************** Error handling features
 * /****************************************************** 错误处理特性
 */

        /**
 * 该特性决定了当遇到未知属性(没有映射到属性,没有任何setter或者任何能够处理它的handler),是否应该抛出一个
 * JsonMappingException异常。这个特性通常式全部其余处理方法对未知属性处理都无效后才被尝试,属性保留未处理状态。
 *
 * 默认状况下,该设置是被打开的。
 *
 * @since 1.2
 */
        FAIL_ON_UNKNOWN_PROPERTIES(true),

        /**
 * 该特性决定当遇到JSON null的对象是java 原始类型,则是否抛出异常。当false时,则使用0 for 'int', 0.0 for double 来设定原始对象初始值。
 *
 * 默认状况下,容许原始类型可使用null。
 *
 * @since 1.7
 */
        FAIL_ON_NULL_FOR_PRIMITIVES(false),

        /**
 * 该特性决定JSON 整数是不是一个有效的值,当被用来反序列化Java枚举值。若是false,数字能够接受,而且映射为枚举的值ordinal();
 * 若是true,则数字不容许而且抛出JsonMappingException异常。后面一种行为缘由是由于大部分状况下,枚举被反序列化为 JSON 字符串, 从而形成从整形到枚举的意外映射关系。
 *
 * Feature is disabled by default (to be consistent with behavior of Jackson 1.6), i.e. to allow use of JSON
 * integers for Java enums.
 * 
 * @since 1.7
 */
        FAIL_ON_NUMBERS_FOR_ENUMS(false),

        /**
 * 异常封装,不封装Error,catch异常以后,抛出IOException。默认封装异常。
 *
 * @since 1.7
 */
        WRAP_EXCEPTIONS(true),

        /*
 * /****************************************************** Structural conversion features
 * /****************************************************** 数据结构转换特性
 */

        /**
 * 该特性决定是否接受强制非数组(JSON)值到Java集合类型。若是容许,集合反序列化将尝试处理非数组值。
 *
 * Feature that determines whether it is acceptable to coerce non-array (in JSON) values to work with Java
 * collection (arrays, java.util.Collection) types. If enabled, collection deserializers will try to handle
 * non-array values as if they had "implicit" surrounding JSON array. This feature is meant to be used for
 * compatibility/interoperability reasons, to work with packages (such as XML-to-JSON converters) that leave out
 * JSON array in cases where there is just a single element in array.
 * 
 * @since 1.8
 */
        ACCEPT_SINGLE_VALUE_AS_ARRAY(false),

        /**
 * 该特征容许 unwrap根级别JSON 值,来匹配WRAP_ROOT_VALUE 序列化设置。
 *
 * @since 1.9
 */
        UNWRAP_ROOT_VALUE(false),

        /*
 * /****************************************************** Value conversion features
 * /****************************************************** 值转换特性
 */

        /**
 * 该特性能够容许JSON空字符串转换为POJO对象为null。若是禁用,则标准POJO只会从JSON null或者JSON对象转换过来;
 * 若是容许,则空JSON字符串能够等价于JSON null。
 * @since 1.8
 */
        ACCEPT_EMPTY_STRING_AS_NULL_OBJECT(false)

        ;

        final boolean _defaultState;

        private Feature(boolean defaultState) {
            _defaultState = defaultState;
        }

        @Override
        public boolean enabledByDefault() {
            return _defaultState;
        }

        @Override
        public int getMask() {
            return (1 << ordinal());
        }
    }

SerializationConfig 序列化相关配置属性

/**
 * 定义序列化对象所需配置的一些枚举.
 */
    public enum Feature implements MapperConfig.ConfigFeature
    {
        /*
 /******************************************************
 /* Introspection features
 /******************************************************
 */
        
        /**
 * 注解扫描配置
 */
        USE_ANNOTATIONS(true),

        /**
 * 获取getter方法,前缀为get
 */
        AUTO_DETECT_GETTERS(true),

        /**
 * 获取getter方法,前缀为is
 */
        AUTO_DETECT_IS_GETTERS(true),

        /**
 * 将对象全部的field做为json属性
 */
         AUTO_DETECT_FIELDS(true),

        /**
 * 该特性决定当访问属性时候,方法和field访问是否修改设置。 若是设置为true,
 * 则经过反射调用方法AccessibleObject#setAccessible 来容许访问不能访问的对象。
 */
        CAN_OVERRIDE_ACCESS_MODIFIERS(true),

        /**
 * 获取的getter方法须要setter方法,不然,全部发现的getter均可以做为getter方法。
 */
        REQUIRE_SETTERS_FOR_GETTERS(false),
        
        /*
 /******************************************************
 /* Generic output features
 /******************************************************
 */

        /**
 * 属性对应的值为null,是否须要写出来,write out。
 */
        @Deprecated
        WRITE_NULL_PROPERTIES(true),

        /**
 * 特征决定是使用运行时动态类型,仍是声明的静态类型。
 * 也可使用{@link JsonSerialize#typing} 注解属性
 */
        USE_STATIC_TYPING(false),

        /**
 * 该特性决定拥有view注解{@link org.codehaus.jackson.map.annotate.JsonView}的属性是否在JSON序列化视图中。若是true,则非注解视图,也包含;
 * 不然,它们将会被排除在外。
 *
 */
        DEFAULT_VIEW_INCLUSION(true),
        
        /**
 * 在JAVA中配置XML root{@XmlRootElement.name}注解,最后xml数据中会出现对应root根name.
 */
        WRAP_ROOT_VALUE(false),

        /**
 * 该特性对于最基础的生成器,使用默认pretty printer {@link org.codehaus.jackson.JsonGenerator#useDefaultPrettyPrinter}
 * 这只会对{@link org.codehaus.jackson.JsonGenerator}有影响.该属性值容许使用默认的实现。
 */
        INDENT_OUTPUT(false),

        /**
 * 是否对属性使用排序,默认排序按照字母顺序。
 */
        SORT_PROPERTIES_ALPHABETICALLY(false),
        
        /*
 /******************************************************
 /* Error handling features
 /******************************************************
 */
        
        /**
 * 是否容许一个类型没有注解代表打算被序列化。默认true,抛出一个异常;不然序列化一个空对象,好比没有任何属性。
 *
 * Note that empty types that this feature has only effect on
 * those "empty" beans that do not have any recognized annotations
 * (like <code>@JsonSerialize</code>): ones that do have annotations
 * do not result in an exception being thrown.
 *
 * @since 1.4
 */
        FAIL_ON_EMPTY_BEANS(true),

        /**
 * 封装全部异常
 */
        WRAP_EXCEPTIONS(true),

        /*
 /******************************************************
 /* Output life cycle features
 /******************************************************
 */
        
         /**
 * 该特性决定序列化root级对象的实现closeable接口的close方法是否在序列化后被调用。
 * 
 * 注意:若是true,则完成序列化后就关闭;若是,你能够在处理最后,调用排序操做等,则为false。
 * 
 */
        CLOSE_CLOSEABLE(false),

        /**
 * 该特性决定是否在writeValue()方法以后就调用JsonGenerator.flush()方法。
 * 当咱们须要先压缩,而后再flush,则可能须要false。
 * 
 */
        FLUSH_AFTER_WRITE_VALUE(true),
         
        /*
 /******************************************************
 /* Data type - specific serialization configuration
 /******************************************************
 */

        /**
 * 该特性决定是否将基于Date的值序列化为timestamp数字式的值,或者做为文本表示。
 * 若是文本表示,则实际格式化的时候会调用{@link #getDateFormat}方法。
 * 
 * 该特性可能会影响其余date相关类型的处理,虽然咱们理想状况是只对date起做用。
 * 
 */
        WRITE_DATES_AS_TIMESTAMPS(true),

        /**
 * 是否将Map中得key为Date的值,也序列化为timestamps形式(不然,会被序列化为文本形式的值)。
 */
        WRITE_DATE_KEYS_AS_TIMESTAMPS(false),

        /**
 * 该特性决定怎样处理类型char[]序列化,是否序列化为一个显式的JSON数组,仍是默认做为一个字符串。
 *
 */
        WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS(false),

        /**
 * 该特性决定对Enum 枚举值使用标准的序列化机制。若是true,则返回Enum.toString()值,不然为Enum.name()
 *
 */
        WRITE_ENUMS_USING_TO_STRING(false),

        /**
 * 这个特性决定Java枚举值是否序列化为数字(true)或者文本值(false).若是是值的话,则使用Enum.ordinal().
 * 该特性优先级高于上面的那个。
 * 
 * @since 1.9
 */
        WRITE_ENUMS_USING_INDEX(false),
        
        /**
 * 决定是否Map的带有null值的entry被序列化(true)
 *
 */
        WRITE_NULL_MAP_VALUES(true),

        /**
 * 决定容器空的属性(声明为Collection或者array的值)是否被序列化为空的JSON数组(true),不然强制输出。
 *
 * Note that this does not change behavior of {@link java.util.Map}s, or
 * "Collection-like" types.
 * 
 * @since 1.9
 */
        WRITE_EMPTY_JSON_ARRAYS(true)
        
            ;

        final boolean _defaultState;
        
        private Feature(boolean defaultState) {
            _defaultState = defaultState;
        }

        @Override
        public boolean enabledByDefault() { return _defaultState; }
    
        @Override
        public int getMask() { return (1 << ordinal()); }
    }

Jackson解析JSON数据

Jackson对外提供了多种解析json数据格式的方法,例如,String contextReader srcUrl src等,此外,对于Java POJO类型也提供了三种方式:Class<T> valueTypeTypeReference valueTypeRef以及JavaType valueType。为了简单,这里分析一般应用最多的一种,即app

public <T> T readValue(String content, Class<T> valueType)

readValue()和其余解析方法同样,内部都是经过构造_readMapAndClose(JsonParser jp, JavaType valueType)方法所须要的参数,来调用解析JSON数据的。ide

首先,来看下如何构造方法的两个参数:

public JsonParser createJsonParser(String content)
        throws IOException, JsonParseException
    {
    // true -> we own the Reader (and must close); not a big deal(还记得上面的配置吗:))
    Reader r = new StringReader(content);
    return _createJsonParser(r, _createContext(r, true));
    }
    
    // 构造实际使用的parser的工厂方法。
    // _parserFeatures就是上面默认的属性int,collectDefaults()方法返回值。
    // _objectCodec:实如今JAVA对象和JSON内容之间的转换功能,没有默认,须要显式去设置。通常咱们直接使用MappingJsonFactory构造。
    protected JsonParser _createJsonParser(Reader r, IOContext ctxt)
    throws IOException, JsonParseException
    {
        return new ReaderBasedParser(ctxt, _parserFeatures, r, _objectCodec,
                _rootCharSymbols.makeChild(isEnabled(JsonParser.Feature.CANONICALIZE_FIELD_NAMES),
                    isEnabled(JsonParser.Feature.INTERN_FIELD_NAMES)));
    }
    
    // 直接调用构造函数构造一个JsonParser实现类实例
    public ReaderBasedParser(IOContext ioCtxt, int features, Reader r,
                             ObjectCodec codec, CharsToNameCanonicalizer st)
    {
        super(ioCtxt, features, r);
        _objectCodec = codec;
        _symbols = st;
    }

在上面实现中,还能够看看_createContext(r, true)的实现,它会构造出一个IOConetxt对象。

public IOContext(BufferRecycler br, Object sourceRef, boolean managedResource)
    {
        _bufferRecycler = br;
        _sourceRef = sourceRef;
        _managedResource = managedResource;
    }
    
    // _recyclerRef 是一个定义全局的ThreadLocal<SoftReference<BufferRecycler>>
     public BufferRecycler _getBufferRecycler()
    {
        SoftReference<BufferRecycler> ref = _recyclerRef.get();
        BufferRecycler br = (ref == null) ? null : ref.get();

        if (br == null) {
            br = new BufferRecycler();
            _recyclerRef.set(new SoftReference<BufferRecycler>(br));
        }
        return br;
    }

构造函数有三个参数,managedResource,咱们在配置上讲过,Jackson对于外部的资源会默认自动关闭流,可是对本身拥有的流Reader,会自动关闭,不管设置与否。Object sourceRef参数其实就是咱们经过content构造出来的Reader引用。BufferRecycler br这是一个很重要的参数,涉及到内存分配优化。

Note: BufferRecycler其实就是一个小的工具类,主要负责初始字节/字符缓存的重复使用。在Jackson中,主要用来经过引用该类的SoftReference形式做为ThreadLocal成员,这样能够达到低负载GC循环的效果,显然是使用流reader所期待的结果。其主要定义四种类型缓存:

public enum CharBufferType {
        TOKEN_BUFFER(2000) // Tokenizable input
            ,CONCAT_BUFFER(2000) // concatenated output
            ,TEXT_BUFFER(200) // Text content from input
            ,NAME_COPY_BUFFER(200) // Temporary buffer for getting name characters
            ;
        
 private final int size;

 CharBufferType(int size) { this.size = size; }
    }

上面的JsonParser就完成的参数构造,接下来就是JavaType了,两种类型都最后调用_constructType来构造JavaType类型。

public JavaType constructType(Type type) {
        return _constructType(type, null);
    }
    
    
    /**
 * Factory method that can be used if type information is passed
 * as Java typing returned from <code>getGenericXxx</code> methods
 * (usually for a return or argument type).
 */
    public JavaType _constructType(Type type, TypeBindings context)
    {
        JavaType resultType;

        // simple class?
        if (type instanceof Class<?>) {
            Class<?> cls = (Class<?>) type;
            /* 24-Mar-2010, tatu: Better create context if one was not passed;
 * mostly matters for root serialization types
 */
            if (context == null) {
                context = new TypeBindings(this, cls);
            }
            resultType = _fromClass(cls, context);
        }
        // But if not, need to start resolving.
        else if (type instanceof ParameterizedType) {//带有参数化的类型,好比Collection<String>
            resultType = _fromParamType((ParameterizedType) type, context);
        }
        else if (type instanceof GenericArrayType) {//表示一个数组类型,成员有参数化类型或者type变量
            resultType = _fromArrayType((GenericArrayType) type, context);
        }
        else if (type instanceof TypeVariable<?>) {//其余类型的父接口
            resultType = _fromVariable((TypeVariable<?>) type, context);
        }
        else if (type instanceof WildcardType) {//通配符类型,好比? 或者 ? extends Number
            resultType = _fromWildcard((WildcardType) type, context);
        } else {
        
        // 最后类型都不符合,就会抛出非法参数异常Unrecognized Type
          throw new IllegalArgumentException("Unrecognized Type: "+type.toString());
        }
        /* 
 * 目前只会被 simple types调用 (i.e. not for arrays, map or collections).
 */
        if (_modifiers != null && !resultType.isContainerType()) {
            for (TypeModifier mod : _modifiers) {
                resultType = mod.modifyType(resultType, type, context, this);
            }
        }
        return resultType;
    }

代码逻辑很简单,就是经过对参数类型进行不一样的处理构造,最后返回JavaType某一具体的实现类实例。和其余处理同样,通常都是从精确类型开始匹配,慢慢抽象。

接下来,为了简单起见,咱们不对很是用设置进行分析,看下面代码:

protected Object _readMapAndClose(JsonParser jp, JavaType valueType)
        throws IOException, JsonParseException, JsonMappingException
    {
        try {
            Object result;
            JsonToken t = _initForReading(jp);
            
            // 省略一部分非重点代码。。。。。。
            
                DeserializationConfig cfg = copyDeserializationConfig();//建立一个反序列化配置的副本,内部采用copy-on-write模式,因此使用副本。
                DeserializationContext ctxt = _createDeserializationContext(jp, cfg);//反序列化上下文
                JsonDeserializer<Object> deser = _findRootDeserializer(cfg, valueType);
                if (cfg.isEnabled(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE)) {
                    result = _unwrapAndDeserialize(jp, valueType, ctxt, deser);
                } else {
                    result = deser.deserialize(jp, ctxt);
                }
            
            // Need to consume the token too
            jp.clearCurrentToken();
            return result;
        } finally {
            try {
                jp.close();//关闭资源
            } catch (IOException ioe) { }
        }
    }

上述代码中有几点重要的方法:

  • JsonToken _initForReading(JsonParser jp)方法:

该方法主要是在反序列化开始的时候,获取JsonToken标识。

protected JsonToken _initForReading(JsonParser jp)
        throws IOException, JsonParseException, JsonMappingException
    {
        /* 首先:必须只想一个token;没有token的状况只能够出如今第一次,和当前token被清除。
 * 此外,这里的JsonParser具体是指ReaderBaseParser实例,(还记得把String Reader处理吗?!)
 */
        JsonToken t = jp.getCurrentToken(); //内部使用了简单地缓存ConcurrentHashMap实现
        if (t == null) {
            // and then we must get something...
            t = jp.nextToken(); // 判断当前是什么类型的JSON标识,好比对象JSON刚开始为"START_OBJECT"
            if (t == null) {
                /* [JACKSON-99] Should throw EOFException, closest thing
 * semantically
 */
                throw new EOFException("No content to map to Object due to end of input");
            }
        }
        return t;
    }
  • _findRootDeserializer(DeserializationConfig cfg, JavaType valueType)方法:

该方法在反序列化root-level值时调用。该方法使用了ConcurrentHashMap缓存来优化,只有root-level的反序列化才会被缓存,这主要是由于反序列化的输入和解决方案都是静态的,可是序列化倒是动态的,因此作缓存只对反序列化。

/**
 * Method called to locate deserializer for the passed root-level value.
 */
    protected JsonDeserializer<Object> _findRootDeserializer(DeserializationConfig cfg, JavaType valueType)
        throws JsonMappingException
    {
        // First: have we already seen it?
        JsonDeserializer<Object> deser = _rootDeserializers.get(valueType);//从缓存中获取对应类型的反序列化实例
        if (deser != null) {
            return deser;
        }
        // Nope: need to ask provider to resolve it
        deser = _deserializerProvider.findTypedValueDeserializer(cfg, valueType, null);//使用StdDeserializationContext默认的DeserializationContext实现。反序列化类实际上就是对某一类型进行详细描述,以下图。
        if (deser == null) { // can this happen?
            throw new JsonMappingException("Can not find a deserializer for type "+valueType);
        }
        _rootDeserializers.put(valueType, deser);//放入缓存中
        return deser;
    }

反序列化类结构如图:

  • deserialize(JsonParser jp, DeserializationContext ctxt)方法:

因为解析对象为简单地Bean,因此调用BeanDeserializer转换为POJO对象。

public final Object deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException
    {
        JsonToken t = jp.getCurrentToken();
        // common case first:
        if (t == JsonToken.START_OBJECT) {
            jp.nextToken();
            return deserializeFromObject(jp, ctxt);
        }
        // and then others, generally requiring use of @JsonCreator
        switch (t) {
        case VALUE_STRING:
            return deserializeFromString(jp, ctxt);
        case VALUE_NUMBER_INT:
            return deserializeFromNumber(jp, ctxt);
        case VALUE_NUMBER_FLOAT:
        return deserializeFromDouble(jp, ctxt);
        case VALUE_EMBEDDED_OBJECT:
            return jp.getEmbeddedObject();
        case VALUE_TRUE:
        case VALUE_FALSE:
            return deserializeFromBoolean(jp, ctxt);
        case START_ARRAY:
            // these only work if there's a (delegating) creator...
            return deserializeFromArray(jp, ctxt);
        case FIELD_NAME:
        case END_OBJECT: // added to resolve [JACKSON-319], possible related issues
            return deserializeFromObject(jp, ctxt);
    }
        throw ctxt.mappingException(getBeanClass());
    }

下面是具体的反序列化方法,以下:

public Object deserializeFromObject(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException
    {
        if (_nonStandardCreation) {
            if (_unwrappedPropertyHandler != null) {
                return deserializeWithUnwrapped(jp, ctxt);
            }
            if (_externalTypeIdHandler != null) {
                return deserializeWithExternalTypeId(jp, ctxt);
            }
            return deserializeFromObjectUsingNonDefault(jp, ctxt);
        }

        final Object bean = _valueInstantiator.createUsingDefault();// 构造一个默认的初始化类型对象,对象属性值使用初始化默认值。
        if (_injectables != null) {
            injectValues(ctxt, bean);
        }
        for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) { //迭代输入流来设置各个属性的值
            String propName = jp.getCurrentName();
            // Skip field name:
            jp.nextToken();// 迭代到下一个输入流的token名字,即便在POJO中没有对应属性,则会调用_handleUnknown来处理。
            SettableBeanProperty prop = _beanProperties.find(propName); //具体实现以下
            if (prop != null) { // 若是在POJO中有对应属性,则返回kv
                try {
                    prop.deserializeAndSet(jp, ctxt, bean);//根据类型调用具体的方法设置属性值,好比StringDeserializer类对字符串。
                } catch (Exception e) {
                    wrapAndThrow(e, bean, propName, ctxt);
                }
                continue;
            }
            _handleUnknown(jp, ctxt, bean, propName);//这里会根据上面的配置来决定处理方案
        }
        return bean;
    }
    
    // BeanPropertyMap类是一个存储属性名到SettableBeanProperty实例之间的映射的工具类。该类主要代替HashMap,作了一些业务优化。
    public SettableBeanProperty find(String key)
    {
        int index = key.hashCode() & _hashMask;//hash找到bucket位置
        Bucket bucket = _buckets[index];//
        // Let's unroll first lookup since that is null or match in 90+% cases
        if (bucket == null) {//若是不存在,则返回null
            return null;
        }
        // Primarily we do just identity comparison as keys should be interned
        if (bucket.key == key) { // 当没有hash碰撞时,则会相等
            return bucket.value;
        }
        while ((bucket = bucket.next) != null) {//不能直接相等,则遍历找到bucket里面全部的key
            if (bucket.key == key) {
                return bucket.value;
            }
        }
        // Do we need fallback for non-interned Strings?
        return _findWithEquals(key, index);
    }

Jackson序列化Java对象

Jackson工具包对外提供看好几种序列化接口,好比转换为String对象,Byte[]数组,或者直接写到stream中等等,可是无论如何,其内部实现的核心方法都是

protected final void _configAndWriteValue(JsonGenerator jgen, Object value)

所以,这里就选择经常使用的writeValueAsString(Object value)来解析器内部实现。

Note:虽然在Jackson中提供了public void writeValue(Writer w, Object value)的通用方法来序列化java对象,对于须要转换为String类型的需求,只须要StringWriter就能够调用writeValue,可是因为这种序列化对性能要求高,而且使用频繁,因此单独提供更高效的实现方式。而正因为在项目代码中AsString 场景十分多,因此这里选择writeValueAsString方法分析。

序列化方法处理流程

writeValueAsString(Object value)其处理和反序列化很类似,基本流程为:

  1. 首先,构建通用序列化基础方法所须要的参数类型对象;
  2. 其次,对序列化类型进行分析,根据注解或者"get方法名(好比getXxx,isXxx)“等来构建须要序列化的属性
  3. 而后,经过反射机制分别对全部的序列化属性进行处理:经过发现拿到对应的值,getxxx方法等
  4. 拼接字符串:其内部是根据类型写入一些开始结束符号,例如{,[等,在其中嵌入步骤3的解析设值
  5. 返回最后获得的字符串内容