對于服務(wù)器端開發(fā)人員而言,調(diào)用第三方接口獲取數(shù)據(jù),將其“代理”轉(zhuǎn)化并返給客戶端幾乎是家常便飯的事兒。 一般情況下,第三方接口返回的數(shù)據(jù)類型是json格式,而服務(wù)器開發(fā)人員則需將json格式的數(shù)據(jù)轉(zhuǎn)換成對象,繼而對其進行處理并封裝,以返回給客戶端。
在不是特別考慮效率的情況下(對于搜索、緩存等情形可以考慮使用thrift和protobuffer),通常我們會選取jackson包中的ObjectMapper類對json串反序列化以得到相應(yīng)對象。通常會選取readValue(String content, Class<T>valueType)方法進行反序列化。
ObjectMapper的readValue方法將json串反序列化為對象的過程大致為: 依據(jù)傳入的json串和目標(biāo)對象類型分別創(chuàng)建JsonParse和JavaType,隨后生成DeserializationConfig、DeserializationContext、JsonDeserializer,其中JsonDeserializer的實現(xiàn)類決定將要執(zhí)行哪一種類型解析(Bean、Map、String等),JsonParse中存儲了待解析字符串及其它信息,在解析的過程中通過token來判斷當(dāng)前匹配的類型(例如:如果遇到{,將其判斷為對象類型的起始位置;遇到[,將其判斷為集合類型的起始位置),一旦確定了類型,則跳入與之對應(yīng)的反序列化類中進行處理,得到結(jié)果,然后token往后移動,接著解析下一個串。可以看做類似遞歸的方式進行解析,當(dāng)通過token判斷為一個對象時,則會跳入BeanDeserializer中進行解析,隨后遍歷該對象的所有字段,如果字段是字符串,則跳到StringDeserializer中進行解析,如果字段是數(shù)組,則跳到CollectionDeserializer中進行解析,直到解析完整個字符串為止。也可以看做類似而樹的深度遍歷,理解起來還是挺容易的。
下面將簡單介紹ObjectMapper的readValue方法進行反序列化的過程:
a:通過json串和對象類型得到JsonParser和JavaType。
1
2
3
4
5
|
public <T> T readValue(String content, Class<T> valueType) throws IOException, JsonParseException, JsonMappingException { return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueType)); } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//獲取json解析器,其中包含帶解析的串 public JsonParser createParser(String content) throws IOException, JsonParseException { final int strLen = content.length(); // Actually, let's use this for medium-sized content, up to 64kB chunk (32kb char) if (_inputDecorator != null || strLen > 0x8000 || !canUseCharArrays()) { // easier to just wrap in a Reader than extend InputDecorator; or, if content // is too long for us to copy it over return createParser( new StringReader(content)); } IOContext ctxt = _createContext(content, true ); char [] buf = ctxt.allocTokenBuffer(strLen); content.getChars( 0 , strLen, buf, 0 ); return _createParser(buf, 0 , strLen, ctxt, 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
|
//將待解析的類型轉(zhuǎn)化為JavaType類型 public JavaType constructType(Type type) { return _constructType(type, null ); } protected JavaType _constructType(Type type, TypeBindings context) { JavaType resultType; // simple class? if (type instanceof Class<?>) { resultType = _fromClass((Class<?>) type, context); } // But if not, need to start resolving. else if (type instanceof ParameterizedType) { resultType = _fromParamType((ParameterizedType) type, context); } else if (type instanceof JavaType) { // [Issue#116] return (JavaType) type; } else if (type instanceof GenericArrayType) { resultType = _fromArrayType((GenericArrayType) type, context); } else if (type instanceof TypeVariable<?>) { resultType = _fromVariable((TypeVariable<?>) type, context); } else if (type instanceof WildcardType) { resultType = _fromWildcard((WildcardType) type, context); } else { // sanity check throw new IllegalArgumentException( "Unrecognized Type: " +((type == null ) ? "[null]" : type.toString())); } if (_modifiers != null && !resultType.isContainerType()) { for (TypeModifier mod : _modifiers) { resultType = mod.modifyType(resultType, type, context, this ); } } return resultType; } |
b、獲取反序列化配置對象和上下文對象,進行第一步的序列化操作。
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
|
protected Object _readMapAndClose(JsonParser jp, JavaType valueType) throws IOException, JsonParseException, JsonMappingException { try { Object result; DeserializationConfig cfg = getDeserializationConfig(); DeserializationContext ctxt = createDeserializationContext(jp, cfg); //依據(jù)valueType得到反序列化的解析器 // 對象對應(yīng)的是beanDeserializer map對應(yīng)的是MapDeserializer 。。。。 JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType); if (cfg.useRootWrapping()) { result = _unwrapAndDeserialize(jp, ctxt, cfg, valueType, deser); } else { //如果是對象,則調(diào)到BeanDeserializer類中進行解析 result = deser.deserialize(jp, ctxt); } ctxt.checkUnresolvedObjectId(); } // Need to consume the token too jp.clearCurrentToken(); return result; } finally { try { jp.close(); } catch (IOException ioe) { } } } |
c、跳入到BeanDeserializer類中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
下面以BeanDeserializer為例進行講解: @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonToken t = p.getCurrentToken(); // common case first if (t == JsonToken.START_OBJECT) { // TODO: in 2.6, use 'p.hasTokenId()' if (_vanillaProcessing) { return vanillaDeserialize(p, ctxt, p.nextToken()); } p.nextToken(); if (_objectIdReader != null ) { return deserializeWithObjectId(p, ctxt); } return deserializeFromObject(p, ctxt); } return _deserializeOther(p, ctxt, t); } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/** * Streamlined version that is only used when no "special" * features are enabled. */ private final Object vanillaDeserialize(JsonParser p, DeserializationContext ctxt, JsonToken t) throws IOException { final Object bean = _valueInstantiator.createUsingDefault(ctxt); // [databind#631]: Assign current value, to be accessible by custom serializers p.setCurrentValue(bean); for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { String propName = p.getCurrentName(); p.nextToken(); if (!_beanProperties.findDeserializeAndSet(p, ctxt, bean, propName)) { handleUnknownVanilla(p, ctxt, bean, propName); } } return bean; } |
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
|
/** * Convenience method that tries to find property with given name, and * if it is found, call {@link SettableBeanProperty#deserializeAndSet} * on it, and return true; or, if not found, return false. * Note, too, that if deserialization is attempted, possible exceptions * are wrapped if and as necessary, so caller need not handle those. * * @since 2.5 */ public boolean findDeserializeAndSet(JsonParser p, DeserializationContext ctxt, Object bean, String key) throws IOException { if (_caseInsensitive) { key = key.toLowerCase(); } int index = key.hashCode() & _hashMask; Bucket bucket = _buckets[index]; // Let's unroll first lookup since that is null or match in 90+% cases if (bucket == null ) { return false ; } // Primarily we do just identity comparison as keys should be interned if (bucket.key == key) { try { bucket.value.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { wrapAndThrow(e, bean, key, ctxt); } return true ; } return _findDeserializeAndSet2(p, ctxt, bean, key, index); } |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
MethodProperty @Override public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, Object instance) throws IOException { Object value = deserialize(jp, ctxt); try { //將得到的結(jié)果放入反序列化對應(yīng)的對象中 _setter.invoke(instance, value); } catch (Exception e) { _throwAsIOE(e, value); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public final Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonToken t = p.getCurrentToken(); if (t == JsonToken.VALUE_NULL) { return (_nullProvider == null ) ? null : _nullProvider.nullValue(ctxt); } if (_valueTypeDeserializer != null ) { return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); } return _valueDeserializer.deserialize(p, ctxt); } //如果繼承了JsonDeserializer類重寫了deseriakize方法,則會跳轉(zhuǎn)到對應(yīng)注入的類中進行處理 //不出意外的話最后都會調(diào)用 DeserializationContext的readValue(JsonParser p, Class<T> type)方法,然后會根據(jù)type的類型跳轉(zhuǎn)到對應(yīng)的反序列化類中進行處理。 |
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
|
public <T> T readValue(JsonParser p, Class<T> type) throws IOException { return readValue(p, getTypeFactory().constructType(type)); } @SuppressWarnings ( "unchecked" ) public <T> T readValue(JsonParser p, JavaType type) throws IOException { //得到最終解析的類型,Map list string。。。。 JsonDeserializer<Object> deser = findRootValueDeserializer(type); if (deser == null ) { } return (T) deser.deserialize(p, this ); } //例如這里如果是一個map,則會調(diào)用MapDeserializer的deserizlize方法得到最后的返回結(jié)果。 //對于集合類,會通過token按照順序解析生成一個個的集合對象并放入集合中。 JsonToken t; while ((t = p.nextToken()) != JsonToken.END_ARRAY) { try { Object value; if (t == JsonToken.VALUE_NULL) { value = valueDes.getNullValue(); } else if (typeDeser == null ) { value = valueDes.deserialize(p, ctxt); } else { value = valueDes.deserializeWithType(p, ctxt, typeDeser); } if (referringAccumulator != null ) { referringAccumulator.add(value); } else { result.add(value); } } catch (UnresolvedForwardReference reference) { if (referringAccumulator == null ) { throw JsonMappingException .from(p, "Unresolved forward reference but no identity info" , reference); } Referring ref = referringAccumulator.handleUnresolvedReference(reference); reference.getRoid().appendReferring(ref); } catch (Exception e) { throw JsonMappingException.wrapWithPath(e, result, result.size()); } } return result; |
在不同的業(yè)務(wù)場景下,第三方接口返回的數(shù)據(jù)類型可能會發(fā)生變化,比如最初第三方業(yè)務(wù)代碼是使用php實現(xiàn)的,而與之對接的服務(wù)器端也是用php實現(xiàn)的。后來,又成立了以Java為開發(fā)語言的服務(wù)器端開發(fā)小組,此時,對接第三方可能會出現(xiàn)問題。第三方返回數(shù)據(jù)類型的不唯一性,可能會使Java開發(fā)人員無法“正常”反序列化第三方接口返回的json串。例如:第三方接口返回的字段中,當(dāng)字段為空時,返回的是數(shù)組;而字段不為空時,返回的卻是對象。這樣,那么通過ObjectMapper進行解析時,就會拋出異常,導(dǎo)致服務(wù)器端無法正常將數(shù)據(jù)返回給客戶端。面對這樣的問題,可能有 以下兩種解決方法:
第一種解決方法是對bean中每個字段set方法內(nèi)進行判斷,當(dāng)解析字符串是一個數(shù)組時,則返回空對象;
當(dāng)解析的字符串不為空時,就會特別的麻煩,默認情況下,會將Json串解析成一個map,其中key為bean中字段的名稱,value為bean的值。這樣,就需要創(chuàng)建一個新的bean,隨后依次從map中取出對應(yīng)字段的值,然后再set到bean中。顯然,這種方式很麻煩,一旦第三方字段發(fā)生變化時,需要不停地維護這段代碼。
第二種解決方法是繼承JsonDeserialize,并重寫反序列化方法。通過源碼可知,JsonDeserializer抽象類是處理反序列化的類,只需在Bean類中的字段上加入注解@JsonDeserialize(using=xxx.class),并且xxx類要繼承JsonDeserializer類,且重新對應(yīng)的deserialize方法,在該方法中進行相應(yīng)處理即可。在該方法中處理待反序列化字段可能出現(xiàn)的多種不同情況,詳情見源碼。
這里需要注意的是:當(dāng)反序列化字段是一個對象,而第三方返回的數(shù)據(jù)為一個數(shù)組時,在重寫deserialize方法時,如果判斷出當(dāng)前token指向的是一個數(shù)組,而此時需得到空對象。此時,不能直接返回空對象,必須調(diào)用readValue方法,目的是將token移動到正確的位置,否則,將創(chuàng)建一些奇怪的對象。
對于第二種解決方法,下面舉例說明:
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
|
package com.string; import java.util.Map; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; public class Comment { public String id; @JsonDeserialize (using = ImgPackSerializer. class ) public Map<String, String> imgPack; @JsonDeserialize (using = CoopSerializer. class ) public Coop coop; public Coop getCoop() { return coop; } public void setCoop(Coop coop) { this .coop = coop; } public Map<String, String> getImgPack() { return imgPack; } public void setImgPack(Map<String, String> imgPack) { this .imgPack = imgPack; } public String getId() { return id; } public void setId(String id) { this .id = id; } } class Coop { public Integer age; public Integer getAge() { return age; } public void setAge(Integer age) { this .age = age; } } |
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
71
72
73
74
75
76
|
package com.string; import java.io.IOException; import java.util.List; import java.util.Map; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.ObjectMapper; public class TestJson { static ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static void main(String[] args) { String s = "{\"code\":\"1\",\"comm\":[{\"imgPack\":{\"abc\":\"abc\"},\"coop\":[]}],\"name\":\"car\"}" ; try { Response readValue = OBJECT_MAPPER.readValue(s, Response. class ); System.err.println(readValue.toString()); } catch (IOException e) { e.printStackTrace(); } } } class Response { public String code; public List<Comment> comm; public String name; public String getName() { return name; } public void setName(String name) { this .name = name; } public String getCode() { return code; } public void setCode(String code) { this .code = code; } public List<Comment> getComm() { return comm; } public void setComm(List<Comment> comm) { this .comm = comm; } } class CoopSerializer extends JsonDeserializer<Coop> { @Override public Coop deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { JsonToken currentToken = jp.getCurrentToken(); if (currentToken == JsonToken.START_ARRAY) { // return null; //error may create more object // jp.nextToken(); //error return ctxt.readValue(jp, Object. class ) == null ? null : null ; } else if (currentToken == JsonToken.START_OBJECT) { return (Coop) ctxt.readValue(jp, Coop. class ); } return null ; } } class ImgPackSerializer extends JsonDeserializer<Map<String, String>> { @Override public Map<String, String> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { JsonToken currentToken = jp.getCurrentToken(); if (currentToken == JsonToken.START_ARRAY) { return ctxt.readValue(jp, Object. class ) == null ? null : null ; } else if (currentToken == JsonToken.START_OBJECT) { return (Map<String, String>) ctxt.readValue(jp, Map. class ); } return null ; } } |
總結(jié)
以上就是本文關(guān)于實例解析Json反序列化之ObjectMapper(自定義實現(xiàn)反序列化方法)的全部內(nèi)容,希望對大家有所幫助。歡迎大家參閱本站其他專題,有什么問題可以留言,小編會及時回復(fù)大家的。
原文鏈接:http://blog.csdn.net/pistolove/article/details/50868105