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用來構築完備集合,一個集合沒有e(空元素)是不能產生閉包運算的。
從字面上理解,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這種狀態壓入棧中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章