項目地址:github-gson-pluginjava
1.ArrayTypeAdapter.JAVA 用於解析數組類型的數據android
public Object read(JsonReader in) throws IOException { if(in.peek() == JsonToken.NULL) { in.nextNull(); return null; } else { List<E> list = 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 用於解析集合類型的數據git
@Override public Collection<E> read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } Collection<E> collection = constructor.construct(); in.beginArray(); while (in.hasNext()) { E instance = elementTypeAdapter.read(in); collection.add(instance); } in.endArray(); return collection; }
3.MapTypeAdapterFactory.JAVA 用於解析map類型的數據github
@Override public Map<K, V> read(JsonReader in) throws IOException { JsonToken peek = in.peek(); if (peek == JsonToken.NULL) { in.nextNull(); return null; } Map<K, V> 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類型json
@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類型的解析,其它省略segmentfault
public static final TypeAdapter<Boolean> BOOLEAN = new TypeAdapter<Boolean>() { @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.異常信息拼接:app
String exception = "expected " + expectedToken + " but was " + inToken + " path " + in.getPath();
/** * Created by tangfuling on 2018/10/25. */ class GsonPlugin implements Plugin<Project> { @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<QualifiedContent.ContentType> 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)) {\n" + " return null;\n" + " }") ctClass.writeFile(dirPath) ctClass.detach() println("GsonPlugin: inject ArrayTypeAdapter success") } } } } }
1.gson-plugin告別Json數據類型不一致(一)
2.gson-plugin基礎源碼分析(二)
3.gson-plugin深刻源碼分析(三)
4.gson-plugin如何在JitPack發佈(四)