JsonElement的生成

JsonElement

先来看下JsonElement继承关系:

JsonElement (com.google.gson)
    JsonObject (com.google.gson)
    JsonArray (com.google.gson)
    JsonNull (com.google.gson)
    JsonPrimitive (com.google.gson)

json中的类型也就是上面四种。JsonPrimitive是基础类型,例如,String,Number,Boolean。
JsonNull用来构筑完备集合。从字面上理解,JsonElement便是把json解析之后的元素。比较有意思的JsonArray与JsonObject的数据结构。我们重点来分析下,这两相类。

Gson与JsonElement相关的数据结构

先来看下json的文法:

object = {} | { members }
members = pair | pair , members
pair = string : value
array = [] | [ elements ]
elements = value  | value , elements
value = string | number | object | array | true | false | null
string = "" | " chars "
chars = char | char chars
char = any-Unicode-character-except-"-or-\-or- control-character | \" | \\ | \/ | \b | \f | \n | \r | \t | \u four-hex-digits
number = int | int frac | int exp | int frac exp
int = digit | digit1-9 digits  | - digit | - digit1-9 digits
frac = . digits
exp = e digits
digits = digit | digit digits
e = e | e+ | e-  | E | E+ | E-

首先,无论是object还是array,都应当是多叉树的结构。
object可以是{}或者{members},members是键值对或者members,也就是0个,1个或多个pair组成,而pair中value又可定义为object,所以是个树状结构。而array是由[]或者[elements]推导出,也就是任意个value组成。而value中可以含有object,object可以有嵌套。
所以,在JsonObject和JsonArray中均有树状结构,其本身可以视为一个note:

public final class JsonObject extends JsonElement {
	  ...
  private final LinkedTreeMap<String, JsonElement> members =
      new LinkedTreeMap<String, JsonElement>();
      ...
      }
public final class JsonArray extends JsonElement implements Iterable<JsonElement> {
...
  private final List<JsonElement> elements;
  ...
}

JsonObject会有键值对,为了保证顺序,以及速度,所以使用了自定义的一个数据结构。

JsonElement的生成

生成过程:
package com.google.gson.internal;
Streams.java

  public static JsonElement parse(JsonReader reader) throws JsonParseException {
    boolean isEmpty = true;
    try {
      reader.peek();
      isEmpty = false;
      return TypeAdapters.JSON_ELEMENT.read(reader);
    } catch (EOFException e) {
      /*
       * For compatibility with JSON 1.5 and earlier, we return a JsonNull for
       * empty documents instead of throwing.
       */
      if (isEmpty) {
        return JsonNull.INSTANCE;
      }
      // The stream ended prematurely so it is likely a syntax error.
      throw new JsonSyntaxException(e);
    } catch (MalformedJsonException e) {
      throw new JsonSyntaxException(e);
    } catch (IOException e) {
      throw new JsonIOException(e);
    } catch (NumberFormatException e) {
      throw new JsonSyntaxException(e);
    }
  }


前面的文章已经简单分析了JsonReader,其peek方法便是生成一个Token。而生成JsonElement的方法在TypeAdapter中。

  public static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() {
    @Override public JsonElement read(JsonReader in) throws IOException {
      switch (in.peek()) {
      case STRING:
        return new JsonPrimitive(in.nextString());
      case NUMBER:
        String number = in.nextString();
        return new JsonPrimitive(new LazilyParsedNumber(number));
      case BOOLEAN:
        return new JsonPrimitive(in.nextBoolean());
      case NULL:
        in.nextNull();
        return JsonNull.INSTANCE;
      case BEGIN_ARRAY:
        JsonArray array = new JsonArray();
        in.beginArray();
        while (in.hasNext()) {
          array.add(read(in));
        }
        in.endArray();
        return array;
      case BEGIN_OBJECT:
        JsonObject object = new JsonObject();
        in.beginObject();
        while (in.hasNext()) {
          object.add(in.nextName(), read(in));
        }
        in.endObject();
        return object;
      case END_DOCUMENT:
      case NAME:
      case END_OBJECT:
      case END_ARRAY:
      default:
        throw new IllegalArgumentException();
      }
    }

    @Override public void write(JsonWriter out, JsonElement value) throws IOException {
      if (value == null || value.isJsonNull()) {
        out.nullValue();
      } else if (value.isJsonPrimitive()) {
        JsonPrimitive primitive = value.getAsJsonPrimitive();
        if (primitive.isNumber()) {
          out.value(primitive.getAsNumber());
        } else if (primitive.isBoolean()) {
          out.value(primitive.getAsBoolean());
        } else {
          out.value(primitive.getAsString());
        }

      } else if (value.isJsonArray()) {
        out.beginArray();
        for (JsonElement e : value.getAsJsonArray()) {
          write(out, e);
        }
        out.endArray();

      } else if (value.isJsonObject()) {
        out.beginObject();
        for (Map.Entry<String, JsonElement> e : value.getAsJsonObject().entrySet()) {
          out.name(e.getKey());
          write(out, e.getValue());
        }
        out.endObject();

      } else {
        throw new IllegalArgumentException("Couldn't write " + value.getClass());
      }
    }
  };

以object为例,如果是BEGIN_OBJECT,则创建JsonObject实例,设置读状态,循环读,读完关闭并返回实例。来看个两个比较有意思的方法,JsonReader的beginObject和endObject

  public void beginObject() throws IOException {
    int p = peeked;
    if (p == PEEKED_NONE) {
      p = doPeek();
    }
    if (p == PEEKED_BEGIN_OBJECT) {
      push(JsonScope.EMPTY_OBJECT);
      peeked = PEEKED_NONE;
    } else {
      throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek() + locationString());
    }
  }

开始读object的时候,会将JsonScope.EMPTY_OBJECT压栈。

  public void endObject() throws IOException {
    int p = peeked;
    if (p == PEEKED_NONE) {
      p = doPeek();
    }
    if (p == PEEKED_END_OBJECT) {
      stackSize--;
      pathNames[stackSize] = null; // Free the last path name so that it can be garbage collected!
      pathIndices[stackSize - 1]++;
      peeked = PEEKED_NONE;
    } else {
      throw new IllegalStateException("Expected END_OBJECT but was " + peek() + locationString());
    }
  }

读完之后,将开始读object压入的EMPTY_OBJECT清空。这是什么操作?
在doPeek函数中会栈顶元素纪录着当前所处理的字符类型。

if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT)
{
      stack[stackSize - 1] = JsonScope.DANGLING_NAME;
      // Look for a comma before the next element.
      if (peekStack == JsonScope.NONEMPTY_OBJECT) {
        int c = nextNonWhitespace(true);
        switch (c) {
        case '}':
          return peeked = PEEKED_END_OBJECT;
        case ';':
          checkLenient(); // fall-through
        case ',':
          break;
        default:
          throw syntaxError("Unterminated object");
        }
      }
      int c = nextNonWhitespace(true);
      switch (c) {
      case '"':
        return peeked = PEEKED_DOUBLE_QUOTED_NAME;
      case '\'':
        checkLenient();
        return peeked = PEEKED_SINGLE_QUOTED_NAME;
      case '}':
        if (peekStack != JsonScope.NONEMPTY_OBJECT) {
          return peeked = PEEKED_END_OBJECT;
        } else {
          throw syntaxError("Expected name");
        }
      default:
        checkLenient();
        pos--; // Don't consume the first character in an unquoted string.
        if (isLiteral((char) c)) {
          return peeked = PEEKED_UNQUOTED_NAME;
        } else {
          throw syntaxError("Expected name");
        }
      }
    

如果是EMPTY_OBJECT或者NONEMPTY_OBJECT都会走到这个流程,
如果是EMPTY_OBJECT的话,输入的有效字符是“ “ ”,以及” }“ ,如果是NONEMPTY_OBJECT的话,有效字符是” ,“ 以及”}“。从这里看出,EMPTY_OBJECT是还没有解析内容的状态。如果接下来的输入是”}“,则才是真正的EMPTY,不要被名字引起误会。
所以刚开始解析的时候,会把EMPTY_OBJECT这种状态压入栈中。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值