一個對象(某個字段爲枚舉類型,爲了避免採用默認的序列化過程,用@JSONField
指定了序列化器和反序列器,過程見舊博文),將其放到JSONArray
中再序列化JSONArray
對象,用獲得的JSON
字符串再反序列化時,發現可以正常反序列化出JSONArray
,而對JSONArray
中的某個元素再反序列化成類對象時,出錯。html
一樣用舊博文的示例作個簡單測試。
基本對象類Article
。java
public class Article { private String title; private String content; @JSONField(serializeUsing = AuditStatusCodec.class, deserializeUsing = AuditStatusCodec.class) private AuditStatus status; public Article(){ } public Article(String title, String content, AuditStatus status){ this.title = title; this.content = content; this.status = status; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public AuditStatus getStatus() { return status; } public void setStatus(AuditStatus status) { this.status = status; } @Override public String toString() { return "Article{" + "title='" + title + '\'' + ", content='" + content + '\'' + ", status=" + status + '}'; } @Override public boolean equals(Object o) { if (this == o){ return true; } if (o == null || getClass() != o.getClass()){ return false; } Article article = (Article) o; return Objects.equals(title, article.title) && Objects.equals(content, article.content) && status == article.status; } @Override public int hashCode() { return Objects.hash(title, content, status); } }
枚舉類型AuditStatus
。json
public enum AuditStatus { /** * 審覈中 */ AUDITING(1), /** * 經過 */ PASSED(2), /** * 失敗 */ FAILED(3); private int code; AuditStatus(int code){ this.code = code; } public int getCode() { return code; } public static AuditStatus convert(int code){ AuditStatus[] enums = AuditStatus.values(); for(AuditStatus e : enums){ if(e.code == code){ return e; } } return null; } }
以及序列化/反序列化器AuditStatusCodec
緩存
public class AuditStatusCodec implements ObjectSerializer, ObjectDeserializer { @Override public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { Object value = parser.parse(); return value == null ? (T) value : (T) AuditStatus.convert(TypeUtils.castToInt(value)); } @Override public int getFastMatchToken() { return 0; } @Override public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException { serializer.write(((AuditStatus)object).getCode()); } }
按照出問題的狀況,寫模擬用例:ide
public class FastJsonTest { @Test public void deserializeTest(){ testJSONParse(); testJSONArrayParse(); } protected static void testJSONParse(){ System.out.println("**************Start Test JSON Parse"); Article originalArticle = new Article("Article 1", "This is content", AuditStatus.AUDITING); String jsonStr = JSON.toJSONString(originalArticle); System.out.println("Serialize to json string: " + jsonStr); Article deserializeArticle = JSON.parseObject(jsonStr, Article.class); System.out.println("Deserialize to Java Object: " + deserializeArticle + "; and the status is " + deserializeArticle.getStatus()); //Equals Assert.assertTrue(deserializeArticle.getStatus().equals(AuditStatus.AUDITING)); Assert.assertEquals(originalArticle, deserializeArticle); } protected static void testJSONArrayParse(){ System.out.println("**************Start Test JSONArray Parse"); JSONArray arr = new JSONArray(); Article originArticle = new Article("Article 1", "This is content", AuditStatus.AUDITING); arr.add(originArticle); String jsonArrStr = JSON.toJSONString(arr); System.out.println("Serialize to json array string: " + jsonArrStr); arr = JSON.parseArray(jsonArrStr); Article deserializeArticle = arr.getObject(0, Article.class); System.out.println("Deserialize to json arr, then to java object: " + deserializeArticle + "; ant the status is " + deserializeArticle.getStatus()); //Not Equals Assert.assertFalse(deserializeArticle.getStatus().equals(AuditStatus.AUDITING)); Assert.assertNotEquals(originArticle, deserializeArticle); } }
看控制檯輸出的狀況:測試
**************Start Test JSON Parse Serialize to json string: {"content":"This is content","status":1,"title":"Article 1"} Deserialize to Java Object: Article{title='Article 1', content='This is content', status=AUDITING}; and the status is AUDITING **************Start Test JSONArray Parse Serialize to json array string: [{"content":"This is content","status":1,"title":"Article 1"}] Deserialize to json arr, then to java object: Article{title='Article 1', content='This is content', status=PASSED}; and the status is PASSED
上述代碼中testJsonParse
沒有把類對象放到JSONArray
中,能夠從結果中看出序列化和反序列化過程均正常。而testJSONArrayParse
先把類對象放到JSONArray
中,在從JSONArray
中取出對象反序列化,反序列化的結果就不正常了。this
爲何JSONObject
和JSONArray
的反序列化過程獲得的結果不一致?二者的反序列過程差別在哪?spa
遇事不決,開始DEBUG
。code
首先,JSON
是一個門面類,提供出一些靜態的方法供外部使用。好比說parseObject()
方法。其內部會建立解析器DefaultJSONParser
,並將解析委託給解析器執行。htm
DefualtJSONParser
在建立時接受輸入,全局配置及特性,至關於獲取到了本次解析全部的數據。同時DefualtJSONParser
的內部建立了一些用於解析的組件,例如JSONLexer
(用於字符串解析)。解析過程在parseObject
中執行,parseObject
會經過ParseConfig
(保存解析配置的一個全局對象)獲取到解析器ObjectDeserializer
,並由解析器處理真正的解析過程。
在經過Class
獲取ObjectDeserializer
時,首先會肯定ParserConfig
中是否緩存了對應的反序列化器,若是不存在,則會新建一個JavaBeanDeserializer
(對於通常Java對象而言)。在新建過程當中,會解析Class
的屬性,並保存在JavaBeanInfo
中。 解析器的解析過程就是對比JSON
字符串中的KEY
和JavaBeanInfo
的過程,把對應的值反序列化出來(判斷是否有JSONField
註解,並根據註解的屬性處理也在這一步),最終還原對象。
以流程圖表示上述過程:
JSONArray.getObject()
會先從JSONArray
中獲取出Object
,而後調用TypeUtils
對Object
經過TypeUtils.castToJavaBean()
轉型。
TypeUtils
經過根據須要轉型的類型從ParserConfig
中獲取ObjectDeserializer
反序列化器,對於普通 Java Bean 而言,是JavaBeanDeserializer
。
因爲JSONArray
中取出的Object
其實是JSONObject
對象,所以會由JavaBeanDeserializer
反序列化器的createInstance()
方法執行反序列化,獲得對象。
以流程圖表示上述過程:
deserialize
在反序列化時,會從class
上獲取更多的屬性,其中就包括JSONField
註解上的信息,而createInstance
獲取的信息較少,所以忽略JSONField
所帶的信息,致使自定義的反序列化器在反序列化時失效。
爲何都是反序列化過程,兩者在行爲和表現上會有所不一樣?官方是如何定義deserialize
和createInstance
的?
上述這些問題還須要查詢更多資料來明確。也但願瞭解原因的讀者進行告知。
解決的辦法不先轉換成JSONArray
,而後再反序列化對象。而是經過JSON.parseArray
直接轉成對象的List
。