摘要:对于如果以修改源码的方式,也可以通过判断当前的数据类型是否与预期的数据类型一致的方式进行。但由于每种数据类型都是一个匿名内部类,很难通过判断预期的数据类型是啥,所以可以通过添加捕获异常,在发生异常后,跳过解析。
一、项目地址
项目地址:github-gson-plugin
二、Gson解析核心类1.ArrayTypeAdapter.JAVA 用于解析数组类型的数据
public Object read(JsonReader in) throws IOException { if(in.peek() == JsonToken.NULL) { in.nextNull(); return null; } else { Listlist = new ArrayList(); in.beginArray(); Object array; while(in.hasNext()) { array = this.componentTypeAdapter.read(in); list.add(array); } in.endArray(); array = Array.newInstance(this.componentType, list.size()); for(int i = 0; i < list.size(); ++i) { Array.set(array, i, list.get(i)); } return array; } }
2.CollectionTypeAdapterFactory.JAVA 用于解析集合类型的数据
@Override public Collectionread(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } Collection collection = constructor.construct(); in.beginArray(); while (in.hasNext()) { E instance = elementTypeAdapter.read(in); collection.add(instance); } in.endArray(); return collection; }
3.MapTypeAdapterFactory.JAVA 用于解析map类型的数据
@Override public Mapread(JsonReader in) throws IOException { JsonToken peek = in.peek(); if (peek == JsonToken.NULL) { in.nextNull(); return null; } Map map = constructor.construct(); if (peek == JsonToken.BEGIN_ARRAY) { in.beginArray(); while (in.hasNext()) { in.beginArray(); // entry array K key = keyTypeAdapter.read(in); V value = valueTypeAdapter.read(in); V replaced = map.put(key, value); if (replaced != null) { throw new JsonSyntaxException("duplicate key: " + key); } in.endArray(); } in.endArray(); } else { in.beginObject(); while (in.hasNext()) { JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in); K key = keyTypeAdapter.read(in); V value = valueTypeAdapter.read(in); V replaced = map.put(key, value); if (replaced != null) { throw new JsonSyntaxException("duplicate key: " + key); } } in.endObject(); } return map; }
4.ReflectiveTypeAdapterFactory.JAVA 用于解析Object类型
@Override public T read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } T instance = constructor.construct(); try { in.beginObject(); while (in.hasNext()) { String name = in.nextName(); BoundField field = boundFields.get(name); if (field == null || !field.deserialized) { in.skipValue(); } else { field.read(in, instance); } } } catch (IllegalStateException e) { throw new JsonSyntaxException(e); } catch (IllegalAccessException e) { throw new AssertionError(e); } in.endObject(); return instance; }
5.TypeAdapters.JAVA 用于解析基本数据类型
里边每种基本数据类型,都对应一个匿名内部类,只列出boolean类型的解析,其它省略
public static final TypeAdapter三、跳过异常字段BOOLEAN = new TypeAdapter () { @Override public Boolean read(JsonReader in) throws IOException { JsonToken peek = in.peek(); if (peek == JsonToken.NULL) { in.nextNull(); return null; } else if (peek == JsonToken.STRING) { // support strings for compatibility with GSON 1.7 return Boolean.parseBoolean(in.nextString()); } return in.nextBoolean(); } @Override public void write(JsonWriter out, Boolean value) throws IOException { out.value(value); } };
1.对于ArrayTypeAdapter.JAVA,CollectionTypeAdapterFactory.JAVA,MapTypeAdapterFactory.JAVA,ReflectiveTypeAdapterFactory.JAVA 我们很清楚的知道预期的数据类型是啥,所以可以判断当前的数据类型是否与预期的数据类型一致,如果不一致则跳过解析。
2.对于TypeAdapters.JAVA 如果以修改源码的方式,也可以通过判断当前的数据类型是否与预期的数据类型一致的方式进行。但由于每种数据类型都是一个匿名内部类,很难通过javassist判断预期的数据类型是啥,所以可以通过添加try-catch捕获异常,在发生异常后,跳过解析。
3.判断数据类型是否与预期的数据类型一致,如果不一致则跳过解析。
/** * used for array、collection、map、object * skipValue when expected token error * * @param in input json reader * @param expectedToken expected token */ public static boolean checkJsonToken(JsonReader in, JsonToken expectedToken) { if (in == null || expectedToken == null) { return false; } JsonToken inToken = null; try { inToken = in.peek(); } catch (IOException e) { e.printStackTrace(); } if (inToken == expectedToken) { return true; } if (inToken != JsonToken.NULL) { String exception = "expected " + expectedToken + " but was " + inToken + " path " + in.getPath(); notifyJsonSyntaxError(exception); } skipValue(in); return false; }
1.方法入参:输入的json为JsonReader,期望的数据类型为JsonToken
2.期望的数据类型:在调用方法的时候传入
3.当前的数据类型:通过in.peek()获取
4.当前字段:通过in.getPath()获取
5.异常信息拼接:
String exception = "expected " + expectedToken + " but was " + inToken + " path " + in.getPath();
ReaderTools.JAVA源码
四、GsonPlugin插件编写/** * Created by tangfuling on 2018/10/25. */ class GsonPlugin implements Plugin五、Transform侵入编译流程{ @Override void apply(Project project) { //add dependencies project.dependencies.add("compile", "com.ke.gson.sdk:gson_sdk:1.3.0") //add transform project.android.registerTransform(new GsonJarTransform(project)) } }
/** * Created by tangfuling on 2018/10/25. */ class GsonJarTransform extends Transform { private Project mProject GsonJarTransform(Project project) { mProject = project } @Override String getName() { return "GsonJarTransform" } @Override Set六、javassist修改Gson字节码getInputTypes() { return TransformManager.CONTENT_CLASS } @Override Set super QualifiedContent.Scope> getScopes() { return TransformManager.SCOPE_FULL_PROJECT } @Override boolean isIncremental() { return false } @Override void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { //初始化ClassPool MyClassPool.resetClassPool(mProject, transformInvocation) //处理jar和file TransformOutputProvider outputProvider = transformInvocation.getOutputProvider() for (TransformInput input : transformInvocation.getInputs()) { for (JarInput jarInput : input.getJarInputs()) { // name must be unique,or throw exception "multiple dex files define" def jarName = jarInput.name if (jarName.endsWith(".jar")) { jarName = jarName.substring(0, jarName.length() - 4) } def md5Name = DigestUtils.md5Hex(jarInput.file.getAbsolutePath()) //source file File file = InjectGsonJar.inject(jarInput.file, transformInvocation.context, mProject) if (file == null) { file = jarInput.file } //dest file File dest = outputProvider.getContentLocation(jarName + md5Name, jarInput.contentTypes, jarInput.scopes, Format.JAR) FileUtils.copyFile(file, dest) } for (DirectoryInput directoryInput : input.getDirectoryInputs()) { File dest = outputProvider.getContentLocation(directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY) FileUtils.copyDirectory(directoryInput.file, dest) } } } }
修改ArrayTypeAdapter.JAVA的read()方法
/** * Created by tangfuling on 2018/10/30. */ public class InjectArrayTypeAdapter { public static void inject(String dirPath) { ClassPool classPool = MyClassPool.getClassPool() File dir = new File(dirPath) if (dir.isDirectory()) { dir.eachFileRecurse { File file -> if ("ArrayTypeAdapter.class".equals(file.name)) { CtClass ctClass = classPool.getCtClass("com.google.gson.internal.bind.ArrayTypeAdapter") CtMethod ctMethod = ctClass.getDeclaredMethod("read") ctMethod.insertBefore(" if (!com.ke.gson.sdk.ReaderTools.checkJsonToken($1, com.google.gson.stream.JsonToken.BEGIN_ARRAY)) { " + " return null; " + " }") ctClass.writeFile(dirPath) ctClass.detach() println("GsonPlugin: inject ArrayTypeAdapter success") } } } } }七、目录
1.gson-plugin告别Json数据类型不一致(一)
2.gson-plugin基础源码分析(二)
3.gson-plugin深入源码分析(三)
4.gson-plugin如何在JitPack发布(四)
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/72238.html
摘要:一项目地址项目地址二与关系普通的库可以通过源码的方式直接引入并使用,是一个插件,无法通过源码的方式使用,只能编译并发布以后,才能被正常使用。是一个代码仓库,我们可以将源代码托管在这个平台上。 一、项目地址 项目地址:github-gson-plugin 二、github与JitPack关系 1.普通的java库可以通过源码的方式直接引入并使用,gson-plugin是一个插件,无法通过...
摘要:六原理说明侵入编译流程,在编译过程中,修改库的字节码,修改解析相关的方法,在数据类型不一致的时候,跳过当前字段的解析。 一、目录 1.gson-plugin告别Json数据类型不一致(一)2.gson-plugin基础源码分析(二)3.gson-plugin深入源码分析(三)4.gson-plugin如何在JitPack发布(四) 看完这4篇文章,对Gson解析会有更加深刻的认识,对A...
阅读 789·2021-11-11 16:54
阅读 1521·2021-08-24 10:01
阅读 1913·2019-08-30 15:54
阅读 3298·2019-08-29 14:02
阅读 3132·2019-08-28 18:22
阅读 2245·2019-08-28 18:09
阅读 3701·2019-08-26 10:26
阅读 2668·2019-08-23 18:23