|
16 | 16 |
|
17 | 17 | package com.google.gson.internal.bind;
|
18 | 18 |
|
| 19 | +import com.google.gson.Gson; |
| 20 | +import com.google.gson.JsonArray; |
| 21 | +import com.google.gson.JsonElement; |
| 22 | +import com.google.gson.JsonIOException; |
| 23 | +import com.google.gson.JsonNull; |
| 24 | +import com.google.gson.JsonObject; |
| 25 | +import com.google.gson.JsonPrimitive; |
| 26 | +import com.google.gson.JsonSyntaxException; |
| 27 | +import com.google.gson.TypeAdapter; |
| 28 | +import com.google.gson.TypeAdapterFactory; |
| 29 | +import com.google.gson.annotations.SerializedName; |
| 30 | +import com.google.gson.internal.LazilyParsedNumber; |
| 31 | +import com.google.gson.reflect.TypeToken; |
| 32 | +import com.google.gson.stream.JsonReader; |
| 33 | +import com.google.gson.stream.JsonToken; |
| 34 | +import com.google.gson.stream.JsonWriter; |
19 | 35 | import java.io.IOException;
|
20 | 36 | import java.lang.reflect.AccessibleObject;
|
21 | 37 | import java.lang.reflect.Field;
|
|
27 | 43 | import java.net.URL;
|
28 | 44 | import java.security.AccessController;
|
29 | 45 | import java.security.PrivilegedAction;
|
| 46 | +import java.util.ArrayDeque; |
30 | 47 | import java.util.ArrayList;
|
31 | 48 | import java.util.BitSet;
|
32 | 49 | import java.util.Calendar;
|
33 | 50 | import java.util.Currency;
|
| 51 | +import java.util.Deque; |
34 | 52 | import java.util.GregorianCalendar;
|
35 | 53 | import java.util.HashMap;
|
36 | 54 | import java.util.List;
|
|
42 | 60 | import java.util.concurrent.atomic.AtomicInteger;
|
43 | 61 | import java.util.concurrent.atomic.AtomicIntegerArray;
|
44 | 62 |
|
45 |
| -import com.google.gson.Gson; |
46 |
| -import com.google.gson.JsonArray; |
47 |
| -import com.google.gson.JsonElement; |
48 |
| -import com.google.gson.JsonIOException; |
49 |
| -import com.google.gson.JsonNull; |
50 |
| -import com.google.gson.JsonObject; |
51 |
| -import com.google.gson.JsonPrimitive; |
52 |
| -import com.google.gson.JsonSyntaxException; |
53 |
| -import com.google.gson.TypeAdapter; |
54 |
| -import com.google.gson.TypeAdapterFactory; |
55 |
| -import com.google.gson.annotations.SerializedName; |
56 |
| -import com.google.gson.internal.LazilyParsedNumber; |
57 |
| -import com.google.gson.reflect.TypeToken; |
58 |
| -import com.google.gson.stream.JsonReader; |
59 |
| -import com.google.gson.stream.JsonToken; |
60 |
| -import com.google.gson.stream.JsonWriter; |
61 |
| - |
62 | 63 | /**
|
63 | 64 | * Type adapters for basic types.
|
64 | 65 | */
|
@@ -695,44 +696,99 @@ public void write(JsonWriter out, Locale value) throws IOException {
|
695 | 696 | public static final TypeAdapterFactory LOCALE_FACTORY = newFactory(Locale.class, LOCALE);
|
696 | 697 |
|
697 | 698 | public static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() {
|
| 699 | + /** |
| 700 | + * Tries to begin reading a JSON array or JSON object, returning {@code null} if |
| 701 | + * the next element is neither of those. |
| 702 | + */ |
| 703 | + private JsonElement tryBeginNesting(JsonReader in, JsonToken peeked) throws IOException { |
| 704 | + switch (peeked) { |
| 705 | + case BEGIN_ARRAY: |
| 706 | + in.beginArray(); |
| 707 | + return new JsonArray(); |
| 708 | + case BEGIN_OBJECT: |
| 709 | + in.beginObject(); |
| 710 | + return new JsonObject(); |
| 711 | + default: |
| 712 | + return null; |
| 713 | + } |
| 714 | + } |
| 715 | + |
| 716 | + /** Reads a {@link JsonElement} which cannot have any nested elements */ |
| 717 | + private JsonElement readTerminal(JsonReader in, JsonToken peeked) throws IOException { |
| 718 | + switch (peeked) { |
| 719 | + case STRING: |
| 720 | + return new JsonPrimitive(in.nextString()); |
| 721 | + case NUMBER: |
| 722 | + String number = in.nextString(); |
| 723 | + return new JsonPrimitive(new LazilyParsedNumber(number)); |
| 724 | + case BOOLEAN: |
| 725 | + return new JsonPrimitive(in.nextBoolean()); |
| 726 | + case NULL: |
| 727 | + in.nextNull(); |
| 728 | + return JsonNull.INSTANCE; |
| 729 | + default: |
| 730 | + // When read(JsonReader) is called with JsonReader in invalid state |
| 731 | + throw new IllegalStateException("Unexpected token: " + peeked); |
| 732 | + } |
| 733 | + } |
| 734 | + |
698 | 735 | @Override public JsonElement read(JsonReader in) throws IOException {
|
699 | 736 | if (in instanceof JsonTreeReader) {
|
700 | 737 | return ((JsonTreeReader) in).nextJsonElement();
|
701 | 738 | }
|
702 | 739 |
|
703 |
| - switch (in.peek()) { |
704 |
| - case STRING: |
705 |
| - return new JsonPrimitive(in.nextString()); |
706 |
| - case NUMBER: |
707 |
| - String number = in.nextString(); |
708 |
| - return new JsonPrimitive(new LazilyParsedNumber(number)); |
709 |
| - case BOOLEAN: |
710 |
| - return new JsonPrimitive(in.nextBoolean()); |
711 |
| - case NULL: |
712 |
| - in.nextNull(); |
713 |
| - return JsonNull.INSTANCE; |
714 |
| - case BEGIN_ARRAY: |
715 |
| - JsonArray array = new JsonArray(); |
716 |
| - in.beginArray(); |
| 740 | + // Either JsonArray or JsonObject |
| 741 | + JsonElement current; |
| 742 | + JsonToken peeked = in.peek(); |
| 743 | + |
| 744 | + current = tryBeginNesting(in, peeked); |
| 745 | + if (current == null) { |
| 746 | + return readTerminal(in, peeked); |
| 747 | + } |
| 748 | + |
| 749 | + Deque<JsonElement> stack = new ArrayDeque<>(); |
| 750 | + |
| 751 | + while (true) { |
717 | 752 | while (in.hasNext()) {
|
718 |
| - array.add(read(in)); |
| 753 | + String name = null; |
| 754 | + // Name is only used for JSON object members |
| 755 | + if (current instanceof JsonObject) { |
| 756 | + name = in.nextName(); |
| 757 | + } |
| 758 | + |
| 759 | + peeked = in.peek(); |
| 760 | + JsonElement value = tryBeginNesting(in, peeked); |
| 761 | + boolean isNesting = value != null; |
| 762 | + |
| 763 | + if (value == null) { |
| 764 | + value = readTerminal(in, peeked); |
| 765 | + } |
| 766 | + |
| 767 | + if (current instanceof JsonArray) { |
| 768 | + ((JsonArray) current).add(value); |
| 769 | + } else { |
| 770 | + ((JsonObject) current).add(name, value); |
| 771 | + } |
| 772 | + |
| 773 | + if (isNesting) { |
| 774 | + stack.addLast(current); |
| 775 | + current = value; |
| 776 | + } |
719 | 777 | }
|
720 |
| - in.endArray(); |
721 |
| - return array; |
722 |
| - case BEGIN_OBJECT: |
723 |
| - JsonObject object = new JsonObject(); |
724 |
| - in.beginObject(); |
725 |
| - while (in.hasNext()) { |
726 |
| - object.add(in.nextName(), read(in)); |
| 778 | + |
| 779 | + // End current element |
| 780 | + if (current instanceof JsonArray) { |
| 781 | + in.endArray(); |
| 782 | + } else { |
| 783 | + in.endObject(); |
| 784 | + } |
| 785 | + |
| 786 | + if (stack.isEmpty()) { |
| 787 | + return current; |
| 788 | + } else { |
| 789 | + // Continue with enclosing element |
| 790 | + current = stack.removeLast(); |
727 | 791 | }
|
728 |
| - in.endObject(); |
729 |
| - return object; |
730 |
| - case END_DOCUMENT: |
731 |
| - case NAME: |
732 |
| - case END_OBJECT: |
733 |
| - case END_ARRAY: |
734 |
| - default: |
735 |
| - throw new IllegalArgumentException(); |
736 | 792 | }
|
737 | 793 | }
|
738 | 794 |
|
@@ -803,7 +859,7 @@ public EnumTypeAdapter(final Class<T> classOfT) {
|
803 | 859 | T constant = (T)(constantField.get(null));
|
804 | 860 | String name = constant.name();
|
805 | 861 | String toStringVal = constant.toString();
|
806 |
| - |
| 862 | + |
807 | 863 | SerializedName annotation = constantField.getAnnotation(SerializedName.class);
|
808 | 864 | if (annotation != null) {
|
809 | 865 | name = annotation.value();
|
|
0 commit comments