Gson原理

160 阅读6分钟

归期几何?

前言:

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。JSON 是基于文本的格式,通常用于在客户端和服务器之间传输数据。它的设计目标是简洁和易于理解。

JSON-Java、Gson、Jackson、FastJson等。

这个框架要做的事情有两件:

● 如何把对象序列化为json——原理

● 如何把json序列化为对象——原理

● Gson序列化和反序列化的方式有哪些?都是如何实现的?

● 其实想想应该不难,很容易想到序列化为json,一个String就ok

● 反序列化回来,只需要使用反射就可以了

● 对于这个框架我们需要知道的是,实现方式,设计模式——coding的艺术,建造者,工厂。。。可扩展的编程思想

简单介绍一下json:

what:

1.  对象与 JSON 的相互转换:

序列化: 将 Java 对象转换为 JSON 字符串。

反序列化: 将 JSON 字符串转换为 Java 对象。

2.  支持复杂数据结构: Gson 可以处理复杂的 Java 对象,包括嵌套对象、集合(如 List 和 Map)等。

3.  自定义序列化和反序列化: 你可以通过实现 JsonSerializer 和 JsonDeserializer 接口来自定义对象的序列化和反序列化过程。

4.  灵活的配置: Gson 提供了多种配置选项,例如控制字段的命名策略、忽略某些字段等。

5.  支持泛型: Gson 可以处理带有泛型的类型,能够正确地序列化和反序列化泛型集合。

why:

我们需要在客户端将数据呈现给用户,技术上涉及到将后端发送的Json数据转化为bean类,一般我们需要自己封装实现,现在有了Gson,足矣。

how:

提供两个主要的API:

Gson json = new Gson();
json.toJson();
json.fromJson();

toJson:

我们先从toJson路径理解原理:

首先是toJson的重载方法们:

image.png 根据不同参数选择不同的重载方法,最后都会出现到这里:

public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
    @SuppressWarnings("unchecked")
    //1
    TypeAdapter<Object> adapter = (TypeAdapter<Object>) getAdapter(TypeToken.get(typeOfSrc));
    //2
    Strictness oldStrictness = writer.getStrictness();
    if (this.strictness != null) {
      writer.setStrictness(this.strictness);
    } else if (writer.getStrictness() == Strictness.LEGACY_STRICT) {
      // For backward compatibility change to LENIENT if writer has default strictness LEGACY_STRICT
      writer.setStrictness(Strictness.LENIENT);
    }
    //3
    boolean oldHtmlSafe = writer.isHtmlSafe();
    //4
    boolean oldSerializeNulls = writer.getSerializeNulls();

    writer.setHtmlSafe(htmlSafe);
    writer.setSerializeNulls(serializeNulls);
    try {
      //5
      adapter.write(writer, src);
    } catch (IOException e) {
      throw new JsonIOException(e);
    } catch (AssertionError e) {
      throw new AssertionError(
          "AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e);
    } finally {
      writer.setStrictness(oldStrictness);
      writer.setHtmlSafe(oldHtmlSafe);
      writer.setSerializeNulls(oldSerializeNulls);
    }
  }

分析:

● 1处getAdapter是为了拿到对应类的类型适配器。 类型适配器how:根据序列化不同的类选取不同的实现方式。 当然我们也可以根据自己的需要自定义一个类型适配器,细节无需讲解,看例子自然明白:

     //官方:
     //一个序列化坐标的类型适配器:
     //use:Gson 会将Points转换为 JSON 作为像"5,8"这样的字符串
     //not use:,而不是像{"x":5,"y":8}
     PointAdapter extends TypeAdapter<Point> {
        public Point read(JsonReader reader) throws IOException {
            if (reader.peek() == JsonToken.NULL) {
                reader.nextNull();
                return null;
            }

            String xy = reader.nextString();
            String[] parts = xy.split(",");
            int x = Integer.parseInt(parts[0]);
            int y = Integer.parseInt(parts[1]);
            return new Point(x, y);
        }

        public void write(JsonWriter writer, Point value) throws IOException {
            if (value == null) {
                writer.nullValue();
                return;
            }
            String xy = value.getX() + "," + value.getY();
            writer.value(xy);
        }
    }

● 2处Strictness这个类用于限定遵守Json格式,先将Gson的严格模式设置为writer的严格模式:有自定义的writer因此。。。

一共有三种模式:

/* Allow large deviations from the JSON specification. /

LENIENT,

/* Allow certain small deviations from the JSON specification for legacy reasons. / LEGACY_STRICT,

/* Strict compliance with the JSON specification. / STRICT 模式默认赋值是null,

// Strictness of null is the legacy mode where some Gson APIs are always lenient

⬆️null就是第二个模式:LEGACY_STRICT

Strictness. STRICT和Strictness. LEGACY_STRICT目前它们的行为是相同的。在这些严格模式下,作者仅按照 RFC 8259 编写 JSON。 rfc8259 通过反射测试,确实为null;

● 3处决定序列化之后作者编写的 JSON 是否可以安全地包含在 HTML 和 XML 文档中,可以则返回 true,也就是转义字符那些:"\u0004"

● 4处 if object members are serialized when their value is null:Returns true , This has no impact on array elements. The default is true.否则忽略

● 5处开始转化为Json,不同的类型适配器有不同的实现。

现在我们看看类型适配器是如何获取的:

 public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
    Objects.requireNonNull(type, "type must not be null");
    //1
    TypeAdapter<?> cached = typeTokenCache.get(type);
    if (cached != null) {
      @SuppressWarnings("unchecked")
      TypeAdapter<T> adapter = (TypeAdapter<T>) cached;
      return adapter;
    }
    //2
    Map<TypeToken<?>, TypeAdapter<?>> threadCalls = threadLocalAdapterResults.get();
    boolean isInitialAdapterRequest = false;
    if (threadCalls == null) {
      threadCalls = new HashMap<>();
      threadLocalAdapterResults.set(threadCalls);
      isInitialAdapterRequest = true;
    } else {
      // the key and value type parameters always agree
      @SuppressWarnings("unchecked")
      TypeAdapter<T> ongoingCall = (TypeAdapter<T>) threadCalls.get(type);
      if (ongoingCall != null) {
        return ongoingCall;
      }
    }

    TypeAdapter<T> candidate = null;
    try {
        //3
      FutureTypeAdapter<T> call = new FutureTypeAdapter<>();
      threadCalls.put(type, call);
        //4
      for (TypeAdapterFactory factory : factories) {
        candidate = factory.create(this, type);
        if (candidate != null) {
          call.setDelegate(candidate);
          // Replace future adapter with actual adapter
          threadCalls.put(type, candidate);
          break;
        }
      }
    } finally {
        //5
      if (isInitialAdapterRequest) {
        threadLocalAdapterResults.remove();
      }
    }
    //6
    if (candidate == null) {
      throw new IllegalArgumentException(
          "GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type);
    }

    if (isInitialAdapterRequest) {
      /*
       * Publish resolved adapters to all threads
       * Can only do this for the initial request because cyclic dependency TypeA -> TypeB -> TypeA
       * would otherwise publish adapter for TypeB which uses not yet resolved adapter for TypeA
       * See https://github.com/google/gson/issues/625
       */
      typeTokenCache.putAll(threadCalls);
    }
    return candidate;
  }

分析:

● 1处从缓存ConcurrentHashMap中拿到TypeToken,待会要通过TypeToken拿到对应的TypeAdapter

判断缓存中存在直接返回

● 2处从threadLoacl中拿出threadCalls,映射关系依然是<TypeToken,TypeAdapter>的HashMap,想知道ConcurrentHashMap和HashMap可看:

不存在就初始化,isInitialAdapterRequest = true; 存在就直接返回,return ongoingCall

不存在继续:

● 3处初始化一个FutureTypeAdapter,作为TypeAdapter的代理类,放入缓存集合中

● 4处循环寻找对应type的工厂类,使用工厂创建对应的TypeAdapter

工厂集合初始化在Gson的构造器中,

image.png 分为三部分:

1.  系统内置默认的适配器

2.  用户自己添加的适配器

3.  平台基础的适配器

这里选取系统默认的ObjectTypeAdapter的write和read继续接下来的解读

参数是ObjToNum

image.png 这里走上面的分支:

image.png 进入ObjectTypeAdapter,

    public void write(JsonWriter out, Object value) throws IOException {
        if (value == null) {
            out.nullValue();
        } else {
            TypeAdapter<Object> typeAdapter = this.gson.getAdapter(value.getClass());
            if (typeAdapter instanceof ObjectTypeAdapter) {
                out.beginObject();
                out.endObject();
            } else {
                typeAdapter.write(out, value);
            }
        }
    }

开始write,比较容易理解:

fromJson

同样,从重载方法们中找到

 public <T> T fromJson(JsonReader reader, TypeToken<T> typeOfT)
      throws JsonIOException, JsonSyntaxException {
    boolean isEmpty = true;
    Strictness oldStrictness = reader.getStrictness();

    if (this.strictness != null) {
      reader.setStrictness(this.strictness);
    } else if (reader.getStrictness() == Strictness.LEGACY_STRICT) {
      // For backward compatibility change to LENIENT if reader has default strictness LEGACY_STRICT
      reader.setStrictness(Strictness.LENIENT);
    }

    try {
      //1
      JsonToken unused = reader.peek();
      isEmpty = false;
      //2
      TypeAdapter<T> typeAdapter = getAdapter(typeOfT);
      return typeAdapter.read(reader);
    } catch (EOFException e) {
      /*
       * For compatibility with JSON 1.5 and earlier, we return null for empty
       * documents instead of throwing.
       */
      if (isEmpty) {
        return null;
      }
      throw new JsonSyntaxException(e);
    } catch (IllegalStateException e) {
      throw new JsonSyntaxException(e);
    } catch (IOException e) {
      // TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException
      throw new JsonSyntaxException(e);
    } catch (AssertionError e) {
      throw new AssertionError(
          "AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e);
    } finally {
      reader.setStrictness(oldStrictness);
    }
  }

● 1处使用peek方法拿到上层的类型,但是不消耗。

JsonReader通过stack数组,模拟栈的出栈,就是如此了

● 2处开始读

Gson针对下一个即将读出的行为有一个状态,不同的类型对应的状态不同,用于 JSON 解析器或生成器的内部实现中,帮助跟踪当前的 JSON 结构状态。通过使用这些常量,代码可以更清晰地表达当前的解析状态,从而在处理 JSON 数据时做出相应的决策。

小bug:

对于Object类的处理:

是用这个ObjectTypeAdapter⬇️

适应静态类型仅为“Object”的类型。在序列化时使用 getClass(),在反序列化时使用原语/ Map/ List。

case BEGIN_OBJECT:
      Map<String, Object> map = new LinkedTreeMap<String,Object>();
      in.beginObject();
      while (in.hasNext()) {
        map.put(in.nextName(), read(in));
      }
      in.endObject();
      return map;

如果是Obj类型,就会转换为LinkedTreeMap 存储数据,如果想要强转,就会报错

将Object类型向下转型到某一个具体类的时候就会发生映射失败!

通过字符流Read和Writter,(更加迅速,虽然字符也是按照byte存储)

使用一个Map缓存实体类的Type -> TypeAdapter作为映射关系,这样我们就可以在最后转型的时候知道对应的类信息。