Jackson 工具類使用及配置指南


目錄

  1. 前言
  2. Jackson使用工具類
  3. Jackson配置屬性
  4. Jackson解析JSON數據
  5. Jackson序列化Java對象

前言

Json數據格式這兩年發展的很快,其聲稱相對XML格式有很對好處:

  • 容易閱讀;
  • 解析速度快;
  • 占用空間更少。

不過,JSON 和 XML兩者糾結誰優誰劣,這里不做討論,可以參見知乎上為什么XML這么笨重的數據結構仍在廣泛應用?

最近在項目中,會有各種解析JSON文本的需求,使用第三方Jackson工具來解析時,又擔心當增加會減少字段,會不會出現非預期的情況,因此,研究下jackson解析json數據格式的代碼,很有需要。

Jackson使用工具類

通常,我們對json格式的數據,只會進行解析和封裝兩種,也就是json字符串--->java對象以及java對象---> json字符串

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具有的一些配置含義。

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。為了簡單,這里分析通常應用最多的一種,即

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

readValue()和其他解析方法一樣,內部都是通過構造_readMapAndClose(JsonParser jp, JavaType valueType)方法所需要的參數,來調用解析JSON數據的。

首先,來看下如何構造方法的兩個參數:

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. 返回最后得到的字符串內容


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM