Gson的反射解析机制详解(2)
2015-11-01 13:52
399 查看
在上一篇博客中笼统的介绍了Gson中解析json的整体流程,但是具体细节并没有说明,本篇就简单的梳理一下:
1)怎么利用反射来创建自定义的JavaBean?
2)怎么给JavaBen的变量类来赋值?
3)集合类对象怎么创建?
通过阅读Gson源码可得出以下的结论:
1)先 获取type获取JavaBean的java.lang.reflect.Constructor,构造器为默认构造器
2)通过Constructor的newInstance()来创建一个type类型的JavaBean对象。
3)循环遍历json中的键值对,获取key和value;并且获取key对应的JavaBean中的Field
4)将value通过Filed的set(JavaBean,value)方法,将value赋值给Javaben对应的Field中去
实例代码如下:
上面的例子程序可以说是Gson中对json解析的简单雏形,其实这些结论都是Gson对于Java反射知识的基本应用,只不过在用的时候结合泛型做了有效的封装来进行通用。下面就简单的梳理一下Gson是怎么来进行处理的。
1)json串键值对key/value中key绑定JavaBean中对应的变量。(本节假设JavaBean中便令名没有用到注解), 在Gson中创建了一个BoundField的类,该类就是负责json中key与JavaBean的变量名进行映射绑定:
在ReflectiveTypeAdapterFactory类中通过getBoundFields方法来对boundFields(详见Gson的反射解析机制详解(1)这个map进行初始化,主要是循环遍历JavaBean中的变量,然后把每个变量封装成一个BoundField,放入map中.该map的key对应的是JavaBean的变量名,value对应的就是BoundField。而BoundField还负责对其绑定的JavaBean变量或者json中key进行读取解析,然后赋值给该JavaBean变量,通过Field的set(JavaBean,value)来完成(这部分详见Gson的反射解析机制详解(1) )。.方法基本的骨架跟文章开头类似。
2)步骤1)中提到,最终是通过Field.setValue(JavaBean,value)来完成当前变量的赋值操作,方法参数中value我们知道是通过读取json来获取,而第一个参数JavaBean是怎么创建的?答案是通过反射创建Gson 的TypeToken 中T代表的对象,也就是JavaBean对象。具体的就是用Constructor.newInstance();见文章开头实例方法:但在Gson中是通过ConstructorConstructor这个类来完成这一工作的:该类提供了newDefaultConstructor来获取JavaBean中的默认构造器,并通过该构造器创建一个JavaBean对象:
通过上面的代码发现该方法返回的是一个ObjectConstructor的匿名子类,通过调用该子类的construct()来创建并返回JavaBean对象。(ObjectConstructor是一个泛型接口,该接口就提供了一个construct()方法),返回的JavaBean对象交给Field的set方法第一个参数。
所以通过ReflectiveTypeAdapterFactory的create()方法返回的Adapter的read方法就是如下:
实际上对于Type是集合类的时候,通过反射构建集合类对象在ConstructorConstructor类的newDefaultImplementationConstructor中做了处理,这里就不多做说明了。
另外如果自定义的JavaBean中没有默认构造器,那么最终会调用ConstructorConstructor类中的newUnsafeAllocator方法来为你创建对应的JavaBean对象,如果此时创建失败的话就会抛出异常:
Gson用反射解析json ,核心简而言之就是通过反射创建JavaBean对象,循环遍历该对象的Field,并通过Field的set(JavaBean,value)方法来对JavaBean的Field赋值。其实就是简单的反射的应用,Gson只不过是做了简单而有效的封装处理来完成了这其中的操作。
1)怎么利用反射来创建自定义的JavaBean?
2)怎么给JavaBen的变量类来赋值?
3)集合类对象怎么创建?
通过阅读Gson源码可得出以下的结论:
1)先 获取type获取JavaBean的java.lang.reflect.Constructor,构造器为默认构造器
2)通过Constructor的newInstance()来创建一个type类型的JavaBean对象。
3)循环遍历json中的键值对,获取key和value;并且获取key对应的JavaBean中的Field
4)将value通过Filed的set(JavaBean,value)方法,将value赋值给Javaben对应的Field中去
实例代码如下:
[code] Constructor<?> c = Person.class.getDeclaredConstructor(); Person person=(Personn) c.newInstance(); //获取Person中的变量 Field[] fields = Person.class.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); //获取变量名 String name = field.getName(); if (name.equals("name"){ field.set(person, "凌云"); } else if (name.equals("sex")) { field.set(person, "屌丝男"); } else if(name.equals("job")){ field.set(person,"IT monkey"); } } return person
上面的例子程序可以说是Gson中对json解析的简单雏形,其实这些结论都是Gson对于Java反射知识的基本应用,只不过在用的时候结合泛型做了有效的封装来进行通用。下面就简单的梳理一下Gson是怎么来进行处理的。
1)json串键值对key/value中key绑定JavaBean中对应的变量。(本节假设JavaBean中便令名没有用到注解), 在Gson中创建了一个BoundField的类,该类就是负责json中key与JavaBean的变量名进行映射绑定:
[code] // private final Map<String, BoundField> boundFields; //该类在ReflectiveTypeAdapterFactory中定义 static abstract class BoundField { //json的key或者JavaBean中的变量名 final String name; ...... protected BoundField(String name, boolean serialized, boolean deserialized) { this.name = name; this.serialized = serialized; this.deserialized = deserialized; }
在ReflectiveTypeAdapterFactory类中通过getBoundFields方法来对boundFields(详见Gson的反射解析机制详解(1)这个map进行初始化,主要是循环遍历JavaBean中的变量,然后把每个变量封装成一个BoundField,放入map中.该map的key对应的是JavaBean的变量名,value对应的就是BoundField。而BoundField还负责对其绑定的JavaBean变量或者json中key进行读取解析,然后赋值给该JavaBean变量,通过Field的set(JavaBean,value)来完成(这部分详见Gson的反射解析机制详解(1) )。.方法基本的骨架跟文章开头类似。
2)步骤1)中提到,最终是通过Field.setValue(JavaBean,value)来完成当前变量的赋值操作,方法参数中value我们知道是通过读取json来获取,而第一个参数JavaBean是怎么创建的?答案是通过反射创建Gson 的TypeToken 中T代表的对象,也就是JavaBean对象。具体的就是用Constructor.newInstance();见文章开头实例方法:但在Gson中是通过ConstructorConstructor这个类来完成这一工作的:该类提供了newDefaultConstructor来获取JavaBean中的默认构造器,并通过该构造器创建一个JavaBean对象:
[code] private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) { //此方法返回具有指定参数列表构造函数的构造函数对象,在这里是获取默认的构造器 final Constructor<? super T> constructor = rawType.getDeclaredConstructor(); if (!constructor.isAccessible()) {//设置访问权限 constructor.setAccessible(true); } return new ObjectConstructor<T>() { @SuppressWarnings("unchecked") // T is the same raw type as is requested public T construct() { Object[] args = null; //创建JavaBean的对象,并返回之 return (T) constructor.newInstance(args); } }; }
通过上面的代码发现该方法返回的是一个ObjectConstructor的匿名子类,通过调用该子类的construct()来创建并返回JavaBean对象。(ObjectConstructor是一个泛型接口,该接口就提供了一个construct()方法),返回的JavaBean对象交给Field的set方法第一个参数。
所以通过ReflectiveTypeAdapterFactory的create()方法返回的Adapter的read方法就是如下:
[code]public T read(JsonReader in) throws IOException { //此处省略了代码 //获取通过步骤2创建的JavaBean对象 T instance = constructor.construct(); try { in.beginObject(); while (in.hasNext()) {//遍历当前JsonObject的值 //获取name,也就是json键值对的key String name = in.nextName(); //获取这个name 绑定的JavaBean变量名 BoundField field = boundFields.get(name); if (field == null || !field.deserialized) { in.skipValue(); } else { //读取此时name对应的值,设置到instance对象中 field.read(in, instance); } } } catch (IllegalStateException e) { throw new JsonSyntaxException(e); } catch (IllegalAccessException e) { throw new AssertionError(e); } in.endObject(); //返回解析完成后的JavaBean对象 return instance; }
实际上对于Type是集合类的时候,通过反射构建集合类对象在ConstructorConstructor类的newDefaultImplementationConstructor中做了处理,这里就不多做说明了。
另外如果自定义的JavaBean中没有默认构造器,那么最终会调用ConstructorConstructor类中的newUnsafeAllocator方法来为你创建对应的JavaBean对象,如果此时创建失败的话就会抛出异常:
[code] throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". " + "Register an InstanceCreator with Gson for this type may fix this problem."), e);
Gson用反射解析json ,核心简而言之就是通过反射创建JavaBean对象,循环遍历该对象的Field,并通过Field的set(JavaBean,value)方法来对JavaBean的Field赋值。其实就是简单的反射的应用,Gson只不过是做了简单而有效的封装处理来完成了这其中的操作。
相关文章推荐
- 杭电1019——最小公倍数(简单题)
- 程序员技术练级攻略
- 挤死collideWith: function(other, axis) { if (other.touches(this)) { other.kill(); } }
- javascript:void(0)
- 优秀程序员必须知道的32个算法
- html和css做网页时注意事项
- oracle参数配置
- 如果flip,y方向的offset不能太大
- 学习DataGridViewX
- SHUTDOWN: waiting for active calls to complete
- oracle信息统计
- IOS开发之C基础-循环结构
- 找出发贴水王
- 执行计划
- php操作mongodb教程
- js获取所有复选框中的值
- iOS自定义转场动画(4)——自定义模态跳转之dismiss与手势驱动
- HDU 2076 夹角有多大(水~)
- android之Volley框架的使用(1)
- Eclipse 下C++ 多线程 pthread.h1