摘要:對于如果以修改源碼的方式,也可以通過判斷當前的數據類型是否與預期的數據類型一致的方式進行。但由于每種數據類型都是一個匿名內部類,很難通過判斷預期的數據類型是啥,所以可以通過添加捕獲異常,在發生異常后,跳過解析。
一、項目地址
項目地址: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發布(四)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/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...
閱讀 787·2021-11-11 16:54
閱讀 1517·2021-08-24 10:01
閱讀 1911·2019-08-30 15:54
閱讀 3296·2019-08-29 14:02
閱讀 3130·2019-08-28 18:22
閱讀 2245·2019-08-28 18:09
閱讀 3698·2019-08-26 10:26
閱讀 2665·2019-08-23 18:23