Json字符串和java对象之间的相互转换(原理篇)

本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/50868105
常见的使用点击:ObjectMapper实用篇php

前言:

对于服务器端开发人员而言,调用第三方接口获取数据,将其“代理”转化并返给客户端几乎是屡见不鲜的事儿。 通常状况下,第三方接口返回的数据类型是json格式,而服务器开发人员则需将json格式的数据转换成对象,继而对其进行处理并封装,以返回给客户端。java

在不是特别考虑效率的状况下(对于搜索、缓存等情形能够考虑使用thrift和protobuffer),一般咱们会选取jackson包中的ObjectMapper类对json串反序列化以获得相应对象。web

  • 一般会选取readValue(String content, ClassvalueType)方法进行反序列化。

原理:

ObjectMapper的readValue方法将json串反序列化为对象的过程大体为: 依据传入的json串和目标对象类型分别建立JsonParse和JavaType,随后生成DeserializationConfig、DeserializationContext、JsonDeserializer,其中JsonDeserializer的实现类决定将要执行哪种类型解析(Bean、Map、String等),JsonParse中存储了待解析字符串及其它信息,在解析的过程当中经过token来判断当前匹配的类型(例如:若是遇到{,将其判断为对象类型的起始位置;遇到[,将其判断为集合类型的起始位置),一旦肯定了类型,则跳入与之对应的反序列化类中进行处理,获得结果,而后token日后移动,接着解析下一个串。能够看作相似递归的方式进行解析,当经过token判断为一个对象时,则会跳入BeanDeserializer中进行解析,随后遍历该对象的全部字段,若是字段是字符串,则跳到StringDeserializer中进行解析,若是字段是数组,则跳到CollectionDeserializer中进行解析,直到解析完整个字符串为止。也能够看作相似而树的深度遍历,理解起来仍是挺容易的。json

下面将简单介绍ObjectMapper的readValue方法进行反序列化的过程:
a:经过json串和对象类型获得JsonParser和JavaType。数组

public <T> T readValue(String content, Class<T> valueType)
        throws IOException, JsonParseException, JsonMappingException
    {
        return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueType));
    }
//获取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);
    }
//将待解析的类型转化为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、获取反序列化配置对象和上下文对象,进行第一步的序列化操做。缓存

protected Object _readMapAndClose(JsonParser jp, JavaType valueType)
        throws IOException, JsonParseException, JsonMappingException
    {
        try {
            Object result;
  			......
                DeserializationConfig cfg = getDeserializationConfig();
                DeserializationContext ctxt = createDeserializationContext(jp, cfg);
                //依据valueType获得反序列化的解析器
                // 对象对应的是beanDeserializer map对应的是MapDeserializer 。。。。
                JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
                if (cfg.useRootWrapping()) {
                    result = _unwrapAndDeserialize(jp, ctxt, cfg, valueType, deser);
                } else {
                	//若是是对象,则调到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类中。服务器

下面以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);
    }
/** * 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;
    }
/** * 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);
    }
MethodProperty
    @Override
    public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
            Object instance) throws IOException
    {
        Object value = deserialize(jp, ctxt);
        try {
 
        	//将获得的结果放入反序列化对应的对象中
            _setter.invoke(instance, value);
        } catch (Exception e) {
            _throwAsIOE(e, value);
        }
    }
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方法,则会跳转到对应注入的类中进行处理
 
 
    //不出意外的话最后都会调用 DeserializationContext的readValue(JsonParser p, Class<T> type)方法,而后会根据type的类型跳转到对应的反序列化类中进行处理
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,则会调用MapDeserializer的deserizlize方法获得最后的返回结果。
   
    //对于集合类,会经过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;

在不一样的业务场景下,第三方接口返回的数据类型可能会发生变化,好比最初第三方业务代码是使用php实现的,而与之对接的服务器端也是用php实现的。后来,又成立了以Java为开发语言的服务器端开发小组,此时,对接第三方可能会出现问题。第三方返回数据类型的不惟一性,可能会使Java开发人员没法“正常”反序列化第三方接口返回的json串。例如:第三方接口返回的字段中,当字段为空时,返回的是数组;而字段不为空时,返回的倒是对象。这样,那么经过ObjectMapper进行解析时,就会抛出异常,致使服务器端没法正常将数据返回给客户端。面对这样的问题,可能有 如下两种解决方法:app

第一种解决方法:

是对bean中每一个字段set方法内进行判断,当解析字符串是一个数组时,则返回空对象;
当解析的字符串不为空时,就会特别的麻烦,默认状况下,会将Json串解析成一个map,其中key为bean中字段的名称,value为bean的值。这样,就须要建立一个新的bean,随后依次从map中取出对应字段的值,而后再set到bean中。显然,这种方式很麻烦,一旦第三方字段发生变化时,须要不停地维护这段代码。ide

第二种解决方法:

是继承JsonDeserialize,并重写反序列化方法。经过源码可知,JsonDeserializer抽象类是处理反序列化的类,只需在Bean类中的字段上加入注解@JsonDeserialize(using=xxx.class),
而且xxx类要继承JsonDeserializer类,且从新对应的deserialize方法,在该方法中进行相应处理便可。在该方法中处理待反序列化字段可能出现的多种不一样状况,详情见源码。
这里须要注意的是:当反序列化字段是一个对象,而第三方返回的数据为一个数组时,在重写deserialize方法时,若是判断出当前token指向的是一个数组,而此时需获得空对象。此时,不能直接返回空对象,必须调用readValue方法,目的是将token移动到正确的位置,不然,将建立一些奇怪的对象。svg

对于第二种解决方法,下面举例说明:
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;
 }

}
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;
    }
}