• Gson学习记录


      Gson是Google开发来用来序列化和反序列化json格式数据的java库,他最大的特点就是对复杂类型的支持度高,可以完美解决java泛型问题,这得益于他对泛型类型数据的特殊处理,他的缺点就是速度慢。

      我们首先看下例子:

    List<MerchantMessage> merchants = new ArrayList<MerchantMessage>();
            MerchantMessage m1 = new MerchantMessage();
            List<Photo> p1 = new ArrayList<Photo>();
            p1.add(new Photo("zcm", "http://zcm.jpg"));
            m1.setPhotos(p1);
            m1.setAdcode("123");
            m1.setAddress(123123);
            m1.setBusinessArea("hello");
            merchants.add(m1);
            ResponseMerchantResult result = new ResponseMerchantResult(0, merchants);
            String json = Gson.toJson(result);
         Gson.formJson(json,
    ResponseMerchantResult.class);

      我们构建了一个ResponseMerchantResult类型的数据,然后执行Gson.toJson方法,然后就可以得到相应的json字符串,我们来看下解析过程

      最终Gson会执行到这个方法:

     @SuppressWarnings("unchecked")
      public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
        TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
        boolean oldLenient = writer.isLenient();
        writer.setLenient(true);
        boolean oldHtmlSafe = writer.isHtmlSafe();
        writer.setHtmlSafe(htmlSafe);
        boolean oldSerializeNulls = writer.getSerializeNulls();
        writer.setSerializeNulls(serializeNulls);
        try {
          ((TypeAdapter<Object>) adapter).write(writer, src);
        } catch (IOException e) {
          throw new JsonIOException(e);
        } finally {
          writer.setLenient(oldLenient);
          writer.setHtmlSafe(oldHtmlSafe);
          writer.setSerializeNulls(oldSerializeNulls);
        }
      }

    我们来分析下:  

    getAdapter(TypeToken.get(typeOfSrc));

    这个代码是获取到解析解析的适配器,根据typeOfSrc匹配现有的适配器,以后所有的操作就是这个适配器来完成。
      @SuppressWarnings("unchecked")
      public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
        TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
        if (cached != null) {
          return (TypeAdapter<T>) cached;
        }
    
        Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
        boolean requiresThreadLocalCleanup = false;
        if (threadCalls == null) {
          threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
          calls.set(threadCalls);
          requiresThreadLocalCleanup = true;
        }
    
        // the key and value type parameters always agree
        FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
        if (ongoingCall != null) {
          return ongoingCall;
        }
    
        try {
          FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
          threadCalls.put(type, call);
    
          for (TypeAdapterFactory factory : factories) {
            TypeAdapter<T> candidate = factory.create(this, type);
            if (candidate != null) {
              call.setDelegate(candidate);
              typeTokenCache.put(type, candidate);
              return candidate;
            }
          }
          throw new IllegalArgumentException("GSON cannot handle " + type);
        } finally {
          threadCalls.remove(type);
    
          if (requiresThreadLocalCleanup) {
            calls.remove();
          }
        }
      }

    我们看到这个方法会遍历所有的适配器工厂,然后得到一个合适的,我们对Gson扩展时最大的扩展点就是这个地方,你可以替换他本身的适配器工厂,也可以添加你需要的工厂。

    我们来看下最常用的一个适配器工厂ReflectiveTypeAdapterFactory  --- 反射类型适配器工厂类  

      

    @Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
        Class<? super T> raw = type.getRawType();
    
        if (!Object.class.isAssignableFrom(raw)) {
          return null; // it's a primitive!
        }
    
        ObjectConstructor<T> constructor = constructorConstructor.get(type);
        return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
      }
    最关键的就是这个create方法,这个方法最终会创建一个需要的适配器,这个适配器工厂会放在所有工厂的最后,当我们要解析的类不是基本类或常用的java类时,就会使用这个工厂来解析
    我们看到getBoundFields()方法
    private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
        Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
        if (raw.isInterface()) {
          return result;
        }
    
        Type declaredType = type.getType();
        while (raw != Object.class) {
          Field[] fields = raw.getDeclaredFields();
          for (Field field : fields) {
            boolean serialize = excludeField(field, true);
            boolean deserialize = excludeField(field, false);
            if (!serialize && !deserialize) {
              continue;
            }
            field.setAccessible(true);
            Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
            List<String> fieldNames = getFieldNames(field);
            BoundField previous = null;
            for (int i = 0, size = fieldNames.size(); i < size; ++i) {
              String name = fieldNames.get(i);
              if (i != 0) serialize = false; // only serialize the default name
              BoundField boundField = createBoundField(context, field, name,
                  TypeToken.get(fieldType), serialize, deserialize);
              BoundField replaced = result.put(name, boundField);
              if (previous == null) previous = replaced;
            }
            if (previous != null) {
              throw new IllegalArgumentException(declaredType
                  + " declares multiple JSON fields named " + previous.name);
            }
          }
          type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
          raw = type.getRawType();
        }
        return result;
      }

    看到这里我们就会发现会获取所有的字段,然后为每个字段绑定他对应的适配器,这样被解析类的每个属性都有了相应的解析适配器,接下来就可以进行解析了

    public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
        TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
        boolean oldLenient = writer.isLenient();
        writer.setLenient(true);
        boolean oldHtmlSafe = writer.isHtmlSafe();
        writer.setHtmlSafe(htmlSafe);
        boolean oldSerializeNulls = writer.getSerializeNulls();
        writer.setSerializeNulls(serializeNulls);
        try {
          ((TypeAdapter<Object>) adapter).write(writer, src);
        } catch (IOException e) {
          throw new JsonIOException(e);
        } finally {
          writer.setLenient(oldLenient);
          writer.setHtmlSafe(oldHtmlSafe);
          writer.setSerializeNulls(oldSerializeNulls);
        }
      }

    我们看到这里会调用适配器的write方法:

      public void write(JsonWriter out, T value) throws IOException {
          if (value == null) {
            out.nullValue();
            return;
          }
    
          out.beginObject();
          try {
            for (BoundField boundField : boundFields.values()) {
              if (boundField.writeField(value)) {
                out.name(boundField.name);
                boundField.write(out, value);
              }
            }
          } catch (IllegalAccessException e) {
            throw new AssertionError(e);
          }
          out.endObject();
        }
      }

    这里会循环所有的属性,然后调用对应的适配器的write方法,最终序列化就完成了。

    我觉得Gson最好的地方就是他可以自动适配泛型类型,我们来看个类

    public class TypeToken<T> {
      final Class<? super T> rawType;
      final Type type;
      final int hashCode;
    
    @SuppressWarnings("unchecked")
    TypeToken(Type type) {
    this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type));
    this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
    this.hashCode = this.type.hashCode();
    }
    }

    这个类是Gson用来记录解析过程需要的type类型,当寻找适配器时就是根据这个类来寻找符合的适配器的,所以泛型的配置也是在这个类里配置。

    我们来看下怎么配置这个类

      首先看几个基本类

    public class GenericResult<T>{
        
        private long l1;
        
        private double d1;
        
        private int i1;
        
        private String s1;
        
        private List<T> datas;
    
        
    }
    public class Message<T> {
        
        private List<T> data;
    
        public List<T> getData() {
            return data;
        }
    
        public void setData(List<T> data) {
            this.data = data;
        }
        
        
        
    }
    GenericResult<Message> result2 = new GenericResult<Message>();
            Message<MerchantMessage> message = new Message();
            message.setData(merchants);
            result2.setD1(11d);
            result2.setI1(1);
            result2.setL1(11l);
            result2.setS1("11");
            result2.setDatas(Arrays.asList(message));
            String json2 = JsonTool.toJson(result2);

    我们看到最后这段代码,这段代码主要是构建一个java类,其中主要类是GenericResult,然后他包含泛型类型Message,Message包含泛型类型MerchantMessage

    那我们解析的时候怎么解析呢

    我们需要一个辅助类

      下面或者类就可以记录我们所有的类型,我们看下怎么使用

    public class TypeMessage implements ParameterizedType{
        
        private final Class main;
        
        private final Type[] args;
        
        public TypeMessage(Class main,Type[] args){
            this.main = main;
            this.args = args == null ? new Type[0] : args;
        }
        
        @Override
        public Type[] getActualTypeArguments() {
            return this.args;
        }
    
        @Override
        public Type getRawType() {
            return this.main;
        }
    
        @Override
        public Type getOwnerType() {
            return null;
        }
        
    }
    JsonTool.<GenericResult>toJson(json2,
                    new TypeMessage(
                            GenericResult.class,
                            new Type[]{new TypeMessage(Message.class,new Type[]{MerchantMessage.class})}))

    当Gson去解析的时候就会按照我们配置的类型去解析。这样我们的泛型就被我们放置进去了。

    以上就是toJson的解析,formJson的解析和toJson类似这里就不再看了。










  • 相关阅读:
    数据卷Volume
    使用 Elastic Stack 分析地理空间数据 (二)
    使用 Elastic Stack 分析地理空间数据 (一)
    如何在 Docker 之上使用 Elastic Stack 和 Kafka 可视化公共交通
    Kubernetes网络模型
    举例:Network Policies
    配置Pod的 /etc/hosts
    k8s上安装安装 Ingress Controller &卸载
    logstash安装插件修改使用的gem源
    示例:Ingress通过互联网访问应用
  • 原文地址:https://www.cnblogs.com/zcmzex/p/9068532.html
Copyright © 2020-2023  润新知