Skip to content

Commit

Permalink
better support for read/write to/from JsonElement tree
Browse files Browse the repository at this point in the history
  • Loading branch information
amogilev committed Jan 13, 2019
1 parent 017e5ca commit 3970e83
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 122 deletions.
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2016 Andrey Mogilev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gilecode.yagson.adapters;

import com.gilecode.yagson.ReadContext;
import com.gilecode.yagson.WriteContext;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;

/**
* Special low-level type adapters which does not perform any processing of types or references YaGson metadata, so
* do not use contexts at all. It is supposed that low-level representation of that metadata is processed somewhere
* else.
* <p/>
* For example, a type adapter for {@link com.google.gson.JsonElement} would treats references as simple JSON strings,
* so it is a task of {@link com.google.gson.internal.bind.JsonTreeReader} to convert the references to actual objects.
* Similarly, for writes, it is a task of some external class to convert type/references information into the
* corresponding JSON elements.
*
* @author Andrey Mogilev
*/
public abstract class RawTypeAdapter<T> extends TypeAdapter<T> {

abstract public void write(JsonWriter out, T value) throws IOException;
abstract public T read(JsonReader in) throws IOException;

@Override
public T read(JsonReader in, ReadContext ctx) throws IOException {
// ignore context
return read(in);
}

@Override
public void write(JsonWriter out, T value, WriteContext ctx) throws IOException {
// ignore context
write(out, value);
}
}
Expand Up @@ -253,7 +253,7 @@ public void applyActualObject(Object actualObject) throws IOException {

private Object registerReferenceUse(String reference) {
Object value = getObjectByReference(reference);
// the object may now be reference both with the used and the current reference
// the object may now be referenced by either of references (old or new), so need to register it again
registerObject(value, false);
return value;
}
Expand Down
189 changes: 94 additions & 95 deletions gson/src/main/java/com/google/gson/JsonParser.java
@@ -1,95 +1,94 @@
/*
* Copyright (C) 2009 Google Inc.
* Modifications copyright (C) 2016 Andrey Mogilev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

import com.gilecode.yagson.ReadContext;
import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.MalformedJsonException;

/**
* A parser to parse Json into a parse tree of {@link JsonElement}s
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.3
*/
public final class JsonParser {

/**
* Parses the specified JSON string into a parse tree
*
* @param json JSON text
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
* @throws JsonParseException if the specified text is not valid JSON
* @since 1.3
*/
public JsonElement parse(String json) throws JsonSyntaxException {
return parse(new StringReader(json));
}

/**
* Parses the specified JSON string into a parse tree
*
* @param json JSON text
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
* @throws JsonParseException if the specified text is not valid JSON
* @since 1.3
*/
public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException {
try {
JsonReader jsonReader = new JsonReader(json);
JsonElement element = parse(jsonReader);
if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonSyntaxException("Did not consume the entire document.");
}
return element;
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}

/**
* Returns the next value from the JSON stream as a parse tree.
*
* @throws JsonParseException if there is an IOException or if the specified
* text is not valid JSON
* @since 1.6
*/
public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException {
boolean lenient = json.isLenient();
json.setLenient(true);
try {
return Streams.parse(json, ReadContext.nullContext());
} catch (StackOverflowError e) {
throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);
} catch (OutOfMemoryError e) {
throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);
} finally {
json.setLenient(lenient);
}
}
}
/*
* Copyright (C) 2009 Google Inc.
* Modifications copyright (C) 2016 Andrey Mogilev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.MalformedJsonException;

/**
* A parser to parse Json into a parse tree of {@link JsonElement}s
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.3
*/
public final class JsonParser {

/**
* Parses the specified JSON string into a parse tree
*
* @param json JSON text
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
* @throws JsonParseException if the specified text is not valid JSON
* @since 1.3
*/
public JsonElement parse(String json) throws JsonSyntaxException {
return parse(new StringReader(json));
}

/**
* Parses the specified JSON string into a parse tree
*
* @param json JSON text
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
* @throws JsonParseException if the specified text is not valid JSON
* @since 1.3
*/
public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException {
try {
JsonReader jsonReader = new JsonReader(json);
JsonElement element = parse(jsonReader);
if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonSyntaxException("Did not consume the entire document.");
}
return element;
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}

/**
* Returns the next value from the JSON stream as a parse tree.
*
* @throws JsonParseException if there is an IOException or if the specified
* text is not valid JSON
* @since 1.6
*/
public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException {
boolean lenient = json.isLenient();
json.setLenient(true);
try {
return Streams.parse(json);
} catch (StackOverflowError e) {
throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);
} catch (OutOfMemoryError e) {
throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);
} finally {
json.setLenient(lenient);
}
}
}
3 changes: 1 addition & 2 deletions gson/src/main/java/com/google/gson/JsonStreamParser.java
Expand Up @@ -23,7 +23,6 @@
import java.util.Iterator;
import java.util.NoSuchElementException;

import com.gilecode.yagson.ReadContext;
import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
Expand Down Expand Up @@ -86,7 +85,7 @@ public JsonElement next() throws JsonParseException {
}

try {
return Streams.parse(parser, ReadContext.nullContext());
return Streams.parse(parser);
} catch (StackOverflowError e) {
throw new JsonParseException("Failed parsing JSON source to Json", e);
} catch (OutOfMemoryError e) {
Expand Down
4 changes: 4 additions & 0 deletions gson/src/main/java/com/google/gson/TypeAdapter.java
Expand Up @@ -97,6 +97,10 @@
* Gson gson = builder.create();
* }</pre>
*
* <strong>YaGson change:</strong> In YaGson, type adapters read/write methods MUST NOT be used directly,
* but only through Gson instance or contexts, i.e. {@link ReadContext#doRead(JsonReader, TypeAdapter, String)} and
* {@link WriteContext#doWrite(Object, TypeAdapter, String, JsonWriter)}
*
* @since 2.1
*/
// non-Javadoc:
Expand Down
9 changes: 3 additions & 6 deletions gson/src/main/java/com/google/gson/internal/Streams.java
Expand Up @@ -42,15 +42,12 @@ private Streams() {
/**
* Takes a reader in any state and returns the next value as a JsonElement.
*/
public static JsonElement parse(JsonReader reader, ReadContext ctx) throws JsonParseException {
if (ctx == null)
ctx = ReadContext.nullContext();

public static JsonElement parse(JsonReader reader) throws JsonParseException {
boolean isEmpty = true;
try {
reader.peek();
isEmpty = false;
return TypeAdapters.JSON_ELEMENT.read(reader, ctx);
return TypeAdapters.JSON_ELEMENT.read(reader);
} catch (EOFException e) {
/*
* For compatibility with JSON 1.5 and earlier, we return a JsonNull for
Expand All @@ -74,7 +71,7 @@ public static JsonElement parse(JsonReader reader, ReadContext ctx) throws JsonP
* Writes the JSON element to the writer, recursively.
*/
public static void write(JsonElement element, JsonWriter writer) throws IOException {
TypeAdapters.JSON_ELEMENT.write(writer, element, null);
TypeAdapters.JSON_ELEMENT.write(writer, element);
}

public static Writer writerForAppendable(Appendable appendable) {
Expand Down
Expand Up @@ -67,7 +67,7 @@ public TreeTypeAdapter(JsonSerializer<T> serializer, JsonDeserializer<T> deseria
if (deserializer == null) {
return delegate().read(in, ctx);
}
JsonElement value = Streams.parse(in, ctx);
JsonElement value = Streams.parse(in);
if (value.isJsonNull()) {
return null;
}
Expand Down
21 changes: 9 additions & 12 deletions gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java
Expand Up @@ -44,6 +44,7 @@
import com.gilecode.yagson.ReadContext;
import com.gilecode.yagson.WriteContext;

import com.gilecode.yagson.adapters.RawTypeAdapter;
import com.gilecode.yagson.adapters.SimpleTypeAdapter;
import com.gilecode.yagson.types.TypeUtils;
import com.google.gson.*;
Expand Down Expand Up @@ -682,16 +683,14 @@ public void write(JsonWriter out, Locale value) throws IOException {

public static final TypeAdapterFactory LOCALE_FACTORY = newFactory(Locale.class, LOCALE);

public static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() {
/*
YaGson note: this type adapter is special, as 'read' returns low-level data, with no types/references
processing, so context is ignored in 'read' and 'write'.
*/
public static final RawTypeAdapter<JsonElement> JSON_ELEMENT = new RawTypeAdapter<JsonElement>() {

@Override
public JsonElement read(JsonReader in, ReadContext ctx) throws IOException {
JsonElement value = read(in);
ctx.registerObject(value, false);
return value;
}

private JsonElement read(JsonReader in) throws IOException {
public JsonElement read(JsonReader in) throws IOException {
switch (in.peek()) {
case STRING:
return new JsonPrimitive(in.nextString());
Expand All @@ -717,9 +716,7 @@ private JsonElement read(JsonReader in) throws IOException {
while (in.hasNext()) {
String name = in.nextName();
JsonElement value = read(in);
if (!name.equals("@type")) {
object.add(name, value);
}
object.add(name, value);
}
in.endObject();
return object;
Expand All @@ -736,7 +733,7 @@ private JsonElement read(JsonReader in) throws IOException {
* {@inheritDoc}
* @param ctx WriteContext is not used for writing JsonElements, so may be null
*/
@Override public void write(JsonWriter out, JsonElement value, WriteContext ctx) throws IOException {
@Override public void write(JsonWriter out, JsonElement value) throws IOException {
if (value == null || value.isJsonNull()) {
out.nullValue();
} else if (value.isJsonPrimitive()) {
Expand Down
4 changes: 2 additions & 2 deletions gson/src/test/java/com/google/gson/JsonParserTest.java
Expand Up @@ -117,8 +117,8 @@ public void testReadWriteTwoObjects() throws Exception {

JsonReader parser = new JsonReader(reader);
parser.setLenient(true);
JsonElement element1 = Streams.parse(parser, null);
JsonElement element2 = Streams.parse(parser, null);
JsonElement element1 = Streams.parse(parser);
JsonElement element2 = Streams.parse(parser);
BagOfPrimitives actualOne = gson.fromJson(element1, BagOfPrimitives.class);
assertEquals("one", actualOne.stringValue);
BagOfPrimitives actualTwo = gson.fromJson(element2, BagOfPrimitives.class);
Expand Down
Expand Up @@ -166,7 +166,7 @@ public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {

return new TypeAdapter<R>() {
@Override public R read(JsonReader in, ReadContext ctx) throws IOException {
JsonElement jsonElement = Streams.parse(in, ctx);
JsonElement jsonElement = Streams.parse(in);
JsonElement labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
if (labelJsonElement == null) {
throw new JsonParseException("cannot deserialize " + baseType
Expand Down

0 comments on commit 3970e83

Please sign in to comment.