X-Git-Url: http://git.ieval.ro/?a=blobdiff_plain;f=src%2Fcom%2Fgoogle%2Fgson%2FGson.java;fp=src%2Fcom%2Fgoogle%2Fgson%2FGson.java;h=a54188a678952e60ff5577b78e420cc362cf7f0f;hb=cfd903b6b5113fa28991db19bf98d0340ae709a4;hp=0000000000000000000000000000000000000000;hpb=819197b2305a94ab4924c5ae37a3fad762078448;p=unical.git diff --git a/src/com/google/gson/Gson.java b/src/com/google/gson/Gson.java new file mode 100644 index 0000000..a54188a --- /dev/null +++ b/src/com/google/gson/Gson.java @@ -0,0 +1,905 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * 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 com.google.gson.internal.ConstructorConstructor; +import com.google.gson.internal.Excluder; +import com.google.gson.internal.Primitives; +import com.google.gson.internal.Streams; +import com.google.gson.internal.bind.ArrayTypeAdapter; +import com.google.gson.internal.bind.CollectionTypeAdapterFactory; +import com.google.gson.internal.bind.DateTypeAdapter; +import com.google.gson.internal.bind.JsonTreeReader; +import com.google.gson.internal.bind.JsonTreeWriter; +import com.google.gson.internal.bind.MapTypeAdapterFactory; +import com.google.gson.internal.bind.ObjectTypeAdapter; +import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory; +import com.google.gson.internal.bind.SqlDateTypeAdapter; +import com.google.gson.internal.bind.TimeTypeAdapter; +import com.google.gson.internal.bind.TypeAdapters; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.google.gson.stream.MalformedJsonException; +import java.io.EOFException; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This is the main class for using Gson. Gson is typically used by first constructing a + * Gson instance and then invoking {@link #toJson(Object)} or {@link #fromJson(String, Class)} + * methods on it. + * + *

You can create a Gson instance by invoking {@code new Gson()} if the default configuration + * is all you need. You can also use {@link GsonBuilder} to build a Gson instance with various + * configuration options such as versioning support, pretty printing, custom + * {@link JsonSerializer}s, {@link JsonDeserializer}s, and {@link InstanceCreator}s.

+ * + *

Here is an example of how Gson is used for a simple Class: + * + *

+ * Gson gson = new Gson(); // Or use new GsonBuilder().create();
+ * MyType target = new MyType();
+ * String json = gson.toJson(target); // serializes target to Json
+ * MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2
+ * 

+ * + *

If the object that your are serializing/deserializing is a {@code ParameterizedType} + * (i.e. contains at least one type parameter and may be an array) then you must use the + * {@link #toJson(Object, Type)} or {@link #fromJson(String, Type)} method. Here is an + * example for serializing and deserialing a {@code ParameterizedType}: + * + *

+ * Type listType = new TypeToken<List<String>>() {}.getType();
+ * List<String> target = new LinkedList<String>();
+ * target.add("blah");
+ *
+ * Gson gson = new Gson();
+ * String json = gson.toJson(target, listType);
+ * List<String> target2 = gson.fromJson(json, listType);
+ * 

+ * + *

See the Gson User Guide + * for a more complete set of examples.

+ * + * @see com.google.gson.reflect.TypeToken + * + * @author Inderjeet Singh + * @author Joel Leitch + * @author Jesse Wilson + */ +public final class Gson { + static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; + + private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; + + /** + * This thread local guards against reentrant calls to getAdapter(). In + * certain object graphs, creating an adapter for a type may recursively + * require an adapter for the same type! Without intervention, the recursive + * lookup would stack overflow. We cheat by returning a proxy type adapter. + * The proxy is wired up once the initial adapter has been created. + */ + private final ThreadLocal, FutureTypeAdapter>> calls + = new ThreadLocal, FutureTypeAdapter>>(); + + private final Map, TypeAdapter> typeTokenCache + = Collections.synchronizedMap(new HashMap, TypeAdapter>()); + + private final List factories; + private final ConstructorConstructor constructorConstructor; + + private final boolean serializeNulls; + private final boolean htmlSafe; + private final boolean generateNonExecutableJson; + private final boolean prettyPrinting; + + final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() { + @SuppressWarnings("unchecked") + public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException { + return (T) fromJson(json, typeOfT); + } + }; + + final JsonSerializationContext serializationContext = new JsonSerializationContext() { + public JsonElement serialize(Object src) { + return toJsonTree(src); + } + public JsonElement serialize(Object src, Type typeOfSrc) { + return toJsonTree(src, typeOfSrc); + } + }; + + /** + * Constructs a Gson object with default configuration. The default configuration has the + * following settings: + *
    + *
  • The JSON generated by toJson methods is in compact representation. This + * means that all the unneeded white-space is removed. You can change this behavior with + * {@link GsonBuilder#setPrettyPrinting()}.
  • + *
  • The generated JSON omits all the fields that are null. Note that nulls in arrays are + * kept as is since an array is an ordered list. Moreover, if a field is not null, but its + * generated JSON is empty, the field is kept. You can configure Gson to serialize null values + * by setting {@link GsonBuilder#serializeNulls()}.
  • + *
  • Gson provides default serialization and deserialization for Enums, {@link Map}, + * {@link java.net.URL}, {@link java.net.URI}, {@link java.util.Locale}, {@link java.util.Date}, + * {@link java.math.BigDecimal}, and {@link java.math.BigInteger} classes. If you would prefer + * to change the default representation, you can do so by registering a type adapter through + * {@link GsonBuilder#registerTypeAdapter(Type, Object)}.
  • + *
  • The default Date format is same as {@link java.text.DateFormat#DEFAULT}. This format + * ignores the millisecond portion of the date during serialization. You can change + * this by invoking {@link GsonBuilder#setDateFormat(int)} or + * {@link GsonBuilder#setDateFormat(String)}.
  • + *
  • By default, Gson ignores the {@link com.google.gson.annotations.Expose} annotation. + * You can enable Gson to serialize/deserialize only those fields marked with this annotation + * through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}.
  • + *
  • By default, Gson ignores the {@link com.google.gson.annotations.Since} annotation. You + * can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}.
  • + *
  • The default field naming policy for the output Json is same as in Java. So, a Java class + * field versionNumber will be output as "versionNumber@quot; in + * Json. The same rules are applied for mapping incoming Json to the Java classes. You can + * change this policy through {@link GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)}.
  • + *
  • By default, Gson excludes transient or static fields from + * consideration for serialization and deserialization. You can change this behavior through + * {@link GsonBuilder#excludeFieldsWithModifiers(int...)}.
  • + *
+ */ + public Gson() { + this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY, + Collections.>emptyMap(), false, false, DEFAULT_JSON_NON_EXECUTABLE, + true, false, false, LongSerializationPolicy.DEFAULT, + Collections.emptyList()); + } + + Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingPolicy, + final Map> instanceCreators, boolean serializeNulls, + boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, + boolean prettyPrinting, boolean serializeSpecialFloatingPointValues, + LongSerializationPolicy longSerializationPolicy, + List typeAdapterFactories) { + this.constructorConstructor = new ConstructorConstructor(instanceCreators); + this.serializeNulls = serializeNulls; + this.generateNonExecutableJson = generateNonExecutableGson; + this.htmlSafe = htmlSafe; + this.prettyPrinting = prettyPrinting; + + List factories = new ArrayList(); + + // built-in type adapters that cannot be overridden + factories.add(TypeAdapters.JSON_ELEMENT_FACTORY); + factories.add(ObjectTypeAdapter.FACTORY); + + // the excluder must precede all adapters that handle user-defined types + factories.add(excluder); + + // user's type adapters + factories.addAll(typeAdapterFactories); + + // type adapters for basic platform types + factories.add(TypeAdapters.STRING_FACTORY); + factories.add(TypeAdapters.INTEGER_FACTORY); + factories.add(TypeAdapters.BOOLEAN_FACTORY); + factories.add(TypeAdapters.BYTE_FACTORY); + factories.add(TypeAdapters.SHORT_FACTORY); + factories.add(TypeAdapters.newFactory(long.class, Long.class, + longAdapter(longSerializationPolicy))); + factories.add(TypeAdapters.newFactory(double.class, Double.class, + doubleAdapter(serializeSpecialFloatingPointValues))); + factories.add(TypeAdapters.newFactory(float.class, Float.class, + floatAdapter(serializeSpecialFloatingPointValues))); + factories.add(TypeAdapters.NUMBER_FACTORY); + factories.add(TypeAdapters.CHARACTER_FACTORY); + factories.add(TypeAdapters.STRING_BUILDER_FACTORY); + factories.add(TypeAdapters.STRING_BUFFER_FACTORY); + factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL)); + factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER)); + factories.add(TypeAdapters.URL_FACTORY); + factories.add(TypeAdapters.URI_FACTORY); + factories.add(TypeAdapters.UUID_FACTORY); + factories.add(TypeAdapters.LOCALE_FACTORY); + factories.add(TypeAdapters.INET_ADDRESS_FACTORY); + factories.add(TypeAdapters.BIT_SET_FACTORY); + factories.add(DateTypeAdapter.FACTORY); + factories.add(TypeAdapters.CALENDAR_FACTORY); + factories.add(TimeTypeAdapter.FACTORY); + factories.add(SqlDateTypeAdapter.FACTORY); + factories.add(TypeAdapters.TIMESTAMP_FACTORY); + factories.add(ArrayTypeAdapter.FACTORY); + factories.add(TypeAdapters.ENUM_FACTORY); + factories.add(TypeAdapters.CLASS_FACTORY); + + // type adapters for composite and user-defined types + factories.add(new CollectionTypeAdapterFactory(constructorConstructor)); + factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization)); + factories.add(new ReflectiveTypeAdapterFactory( + constructorConstructor, fieldNamingPolicy, excluder)); + + this.factories = Collections.unmodifiableList(factories); + } + + private TypeAdapter doubleAdapter(boolean serializeSpecialFloatingPointValues) { + if (serializeSpecialFloatingPointValues) { + return TypeAdapters.DOUBLE; + } + return new TypeAdapter() { + @Override public Double read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return in.nextDouble(); + } + @Override public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + double doubleValue = value.doubleValue(); + checkValidFloatingPoint(doubleValue); + out.value(value); + } + }; + } + + private TypeAdapter floatAdapter(boolean serializeSpecialFloatingPointValues) { + if (serializeSpecialFloatingPointValues) { + return TypeAdapters.FLOAT; + } + return new TypeAdapter() { + @Override public Float read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return (float) in.nextDouble(); + } + @Override public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + float floatValue = value.floatValue(); + checkValidFloatingPoint(floatValue); + out.value(value); + } + }; + } + + private void checkValidFloatingPoint(double value) { + if (Double.isNaN(value) || Double.isInfinite(value)) { + throw new IllegalArgumentException(value + + " is not a valid double value as per JSON specification. To override this" + + " behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method."); + } + } + + private TypeAdapter longAdapter(LongSerializationPolicy longSerializationPolicy) { + if (longSerializationPolicy == LongSerializationPolicy.DEFAULT) { + return TypeAdapters.LONG; + } + return new TypeAdapter() { + @Override public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return in.nextLong(); + } + @Override public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + out.value(value.toString()); + } + }; + } + + /** + * Returns the type adapter for {@code} type. + * + * @throws IllegalArgumentException if this GSON cannot serialize and + * deserialize {@code type}. + */ + @SuppressWarnings("unchecked") + public TypeAdapter getAdapter(TypeToken type) { + TypeAdapter cached = typeTokenCache.get(type); + if (cached != null) { + return (TypeAdapter) cached; + } + + Map, FutureTypeAdapter> threadCalls = calls.get(); + boolean requiresThreadLocalCleanup = false; + if (threadCalls == null) { + threadCalls = new HashMap, FutureTypeAdapter>(); + calls.set(threadCalls); + requiresThreadLocalCleanup = true; + } + + // the key and value type parameters always agree + FutureTypeAdapter ongoingCall = (FutureTypeAdapter) threadCalls.get(type); + if (ongoingCall != null) { + return ongoingCall; + } + + try { + FutureTypeAdapter call = new FutureTypeAdapter(); + threadCalls.put(type, call); + + for (TypeAdapterFactory factory : factories) { + TypeAdapter candidate = factory.create(this, type); + if (candidate != null) { + call.setDelegate(candidate); + typeTokenCache.put(type, candidate); + return candidate; + } + } + throw new IllegalArgumentException("GSON cannot handle " + type); + } finally { + threadCalls.remove(type); + + if (requiresThreadLocalCleanup) { + calls.remove(); + } + } + } + + /** + * This method is used to get an alternate type adapter for the specified type. This is used + * to access a type adapter that is overridden by a {@link TypeAdapterFactory} that you + * may have registered. This features is typically used when you want to register a type + * adapter that does a little bit of work but then delegates further processing to the Gson + * default type adapter. Here is an example: + *

Let's say we want to write a type adapter that counts the number of objects being read + * from or written to JSON. We can achieve this by writing a type adapter factory that uses + * the getDelegateAdapter method: + *

 {@code
+   *  class StatsTypeAdapterFactory implements TypeAdapterFactory {
+   *    public int numReads = 0;
+   *    public int numWrites = 0;
+   *    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
+   *      final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
+   *      return new TypeAdapter<T>() {
+   *        public void write(JsonWriter out, T value) throws IOException {
+   *          ++numWrites;
+   *          delegate.write(out, value);
+   *        }
+   *        public T read(JsonReader in) throws IOException {
+   *          ++numReads;
+   *          return delegate.read(in);
+   *        }
+   *      };
+   *    }
+   *  }
+   *  } 
+ * This factory can now be used like this: + *
 {@code
+   *  StatsTypeAdapterFactory stats = new StatsTypeAdapterFactory();
+   *  Gson gson = new GsonBuilder().registerTypeAdapterFactory(stats).create();
+   *  // Call gson.toJson() and fromJson methods on objects
+   *  System.out.println("Num JSON reads" + stats.numReads);
+   *  System.out.println("Num JSON writes" + stats.numWrites);
+   *  }
+ * Note that since you can not override type adapter factories for String and Java primitive + * types, our stats factory will not count the number of String or primitives that will be + * read or written. + * @param skipPast The type adapter factory that needs to be skipped while searching for + * a matching type adapter. In most cases, you should just pass this (the type adapter + * factory from where {@link #getDelegateAdapter} method is being invoked). + * @param type Type for which the delegate adapter is being searched for. + * + * @since 2.2 + */ + public TypeAdapter getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken type) { + boolean skipPastFound = false; + + for (TypeAdapterFactory factory : factories) { + if (!skipPastFound) { + if (factory == skipPast) { + skipPastFound = true; + } + continue; + } + + TypeAdapter candidate = factory.create(this, type); + if (candidate != null) { + return candidate; + } + } + throw new IllegalArgumentException("GSON cannot serialize " + type); + } + + /** + * Returns the type adapter for {@code} type. + * + * @throws IllegalArgumentException if this GSON cannot serialize and + * deserialize {@code type}. + */ + public TypeAdapter getAdapter(Class type) { + return getAdapter(TypeToken.get(type)); + } + + /** + * This method serializes the specified object into its equivalent representation as a tree of + * {@link JsonElement}s. This method should be used when the specified object is not a generic + * type. This method uses {@link Class#getClass()} to get the type for the specified object, but + * the {@code getClass()} loses the generic type information because of the Type Erasure feature + * of Java. Note that this method works fine if the any of the object fields are of generic type, + * just the object itself should not be of a generic type. If the object is of generic type, use + * {@link #toJsonTree(Object, Type)} instead. + * + * @param src the object for which Json representation is to be created setting for Gson + * @return Json representation of {@code src}. + * @since 1.4 + */ + public JsonElement toJsonTree(Object src) { + if (src == null) { + return JsonNull.INSTANCE; + } + return toJsonTree(src, src.getClass()); + } + + /** + * This method serializes the specified object, including those of generic types, into its + * equivalent representation as a tree of {@link JsonElement}s. This method must be used if the + * specified object is a generic type. For non-generic objects, use {@link #toJsonTree(Object)} + * instead. + * + * @param src the object for which JSON representation is to be created + * @param typeOfSrc The specific genericized type of src. You can obtain + * this type by using the {@link com.google.gson.reflect.TypeToken} class. For example, + * to get the type for {@code Collection}, you should use: + *
+   * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
+   * 
+ * @return Json representation of {@code src} + * @since 1.4 + */ + public JsonElement toJsonTree(Object src, Type typeOfSrc) { + JsonTreeWriter writer = new JsonTreeWriter(); + toJson(src, typeOfSrc, writer); + return writer.get(); + } + + /** + * This method serializes the specified object into its equivalent Json representation. + * This method should be used when the specified object is not a generic type. This method uses + * {@link Class#getClass()} to get the type for the specified object, but the + * {@code getClass()} loses the generic type information because of the Type Erasure feature + * of Java. Note that this method works fine if the any of the object fields are of generic type, + * just the object itself should not be of a generic type. If the object is of generic type, use + * {@link #toJson(Object, Type)} instead. If you want to write out the object to a + * {@link Writer}, use {@link #toJson(Object, Appendable)} instead. + * + * @param src the object for which Json representation is to be created setting for Gson + * @return Json representation of {@code src}. + */ + public String toJson(Object src) { + if (src == null) { + return toJson(JsonNull.INSTANCE); + } + return toJson(src, src.getClass()); + } + + /** + * This method serializes the specified object, including those of generic types, into its + * equivalent Json representation. This method must be used if the specified object is a generic + * type. For non-generic objects, use {@link #toJson(Object)} instead. If you want to write out + * the object to a {@link Appendable}, use {@link #toJson(Object, Type, Appendable)} instead. + * + * @param src the object for which JSON representation is to be created + * @param typeOfSrc The specific genericized type of src. You can obtain + * this type by using the {@link com.google.gson.reflect.TypeToken} class. For example, + * to get the type for {@code Collection}, you should use: + *
+   * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
+   * 
+ * @return Json representation of {@code src} + */ + public String toJson(Object src, Type typeOfSrc) { + StringWriter writer = new StringWriter(); + toJson(src, typeOfSrc, writer); + return writer.toString(); + } + + /** + * This method serializes the specified object into its equivalent Json representation. + * This method should be used when the specified object is not a generic type. This method uses + * {@link Class#getClass()} to get the type for the specified object, but the + * {@code getClass()} loses the generic type information because of the Type Erasure feature + * of Java. Note that this method works fine if the any of the object fields are of generic type, + * just the object itself should not be of a generic type. If the object is of generic type, use + * {@link #toJson(Object, Type, Appendable)} instead. + * + * @param src the object for which Json representation is to be created setting for Gson + * @param writer Writer to which the Json representation needs to be written + * @throws JsonIOException if there was a problem writing to the writer + * @since 1.2 + */ + public void toJson(Object src, Appendable writer) throws JsonIOException { + if (src != null) { + toJson(src, src.getClass(), writer); + } else { + toJson(JsonNull.INSTANCE, writer); + } + } + + /** + * This method serializes the specified object, including those of generic types, into its + * equivalent Json representation. This method must be used if the specified object is a generic + * type. For non-generic objects, use {@link #toJson(Object, Appendable)} instead. + * + * @param src the object for which JSON representation is to be created + * @param typeOfSrc The specific genericized type of src. You can obtain + * this type by using the {@link com.google.gson.reflect.TypeToken} class. For example, + * to get the type for {@code Collection}, you should use: + *
+   * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
+   * 
+ * @param writer Writer to which the Json representation of src needs to be written. + * @throws JsonIOException if there was a problem writing to the writer + * @since 1.2 + */ + public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException { + try { + JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer)); + toJson(src, typeOfSrc, jsonWriter); + } catch (IOException e) { + throw new JsonIOException(e); + } + } + + /** + * Writes the JSON representation of {@code src} of type {@code typeOfSrc} to + * {@code writer}. + * @throws JsonIOException if there was a problem writing to the writer + */ + @SuppressWarnings("unchecked") + public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException { + TypeAdapter adapter = getAdapter(TypeToken.get(typeOfSrc)); + boolean oldLenient = writer.isLenient(); + writer.setLenient(true); + boolean oldHtmlSafe = writer.isHtmlSafe(); + writer.setHtmlSafe(htmlSafe); + boolean oldSerializeNulls = writer.getSerializeNulls(); + writer.setSerializeNulls(serializeNulls); + try { + ((TypeAdapter) adapter).write(writer, src); + } catch (IOException e) { + throw new JsonIOException(e); + } finally { + writer.setLenient(oldLenient); + writer.setHtmlSafe(oldHtmlSafe); + writer.setSerializeNulls(oldSerializeNulls); + } + } + + /** + * Converts a tree of {@link JsonElement}s into its equivalent JSON representation. + * + * @param jsonElement root of a tree of {@link JsonElement}s + * @return JSON String representation of the tree + * @since 1.4 + */ + public String toJson(JsonElement jsonElement) { + StringWriter writer = new StringWriter(); + toJson(jsonElement, writer); + return writer.toString(); + } + + /** + * Writes out the equivalent JSON for a tree of {@link JsonElement}s. + * + * @param jsonElement root of a tree of {@link JsonElement}s + * @param writer Writer to which the Json representation needs to be written + * @throws JsonIOException if there was a problem writing to the writer + * @since 1.4 + */ + public void toJson(JsonElement jsonElement, Appendable writer) throws JsonIOException { + try { + JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer)); + toJson(jsonElement, jsonWriter); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Returns a new JSON writer configured for this GSON and with the non-execute + * prefix if that is configured. + */ + private JsonWriter newJsonWriter(Writer writer) throws IOException { + if (generateNonExecutableJson) { + writer.write(JSON_NON_EXECUTABLE_PREFIX); + } + JsonWriter jsonWriter = new JsonWriter(writer); + if (prettyPrinting) { + jsonWriter.setIndent(" "); + } + jsonWriter.setSerializeNulls(serializeNulls); + return jsonWriter; + } + + /** + * Writes the JSON for {@code jsonElement} to {@code writer}. + * @throws JsonIOException if there was a problem writing to the writer + */ + public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOException { + boolean oldLenient = writer.isLenient(); + writer.setLenient(true); + boolean oldHtmlSafe = writer.isHtmlSafe(); + writer.setHtmlSafe(htmlSafe); + boolean oldSerializeNulls = writer.getSerializeNulls(); + writer.setSerializeNulls(serializeNulls); + try { + Streams.write(jsonElement, writer); + } catch (IOException e) { + throw new JsonIOException(e); + } finally { + writer.setLenient(oldLenient); + writer.setHtmlSafe(oldHtmlSafe); + writer.setSerializeNulls(oldSerializeNulls); + } + } + + /** + * This method deserializes the specified Json into an object of the specified class. It is not + * suitable to use if the specified class is a generic type since it will not have the generic + * type information because of the Type Erasure feature of Java. Therefore, this method should not + * be used if the desired type is a generic type. Note that this method works fine if the any of + * the fields of the specified object are generics, just the object itself should not be a + * generic type. For the cases when the object is of generic type, invoke + * {@link #fromJson(String, Type)}. If you have the Json in a {@link Reader} instead of + * a String, use {@link #fromJson(Reader, Class)} instead. + * + * @param the type of the desired object + * @param json the string from which the object is to be deserialized + * @param classOfT the class of T + * @return an object of type T from the string + * @throws JsonSyntaxException if json is not a valid representation for an object of type + * classOfT + */ + public T fromJson(String json, Class classOfT) throws JsonSyntaxException { + Object object = fromJson(json, (Type) classOfT); + return Primitives.wrap(classOfT).cast(object); + } + + /** + * This method deserializes the specified Json into an object of the specified type. This method + * is useful if the specified object is a generic type. For non-generic objects, use + * {@link #fromJson(String, Class)} instead. If you have the Json in a {@link Reader} instead of + * a String, use {@link #fromJson(Reader, Type)} instead. + * + * @param the type of the desired object + * @param json the string from which the object is to be deserialized + * @param typeOfT The specific genericized type of src. You can obtain this type by using the + * {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for + * {@code Collection}, you should use: + *
+   * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
+   * 
+ * @return an object of type T from the string + * @throws JsonParseException if json is not a valid representation for an object of type typeOfT + * @throws JsonSyntaxException if json is not a valid representation for an object of type + */ + @SuppressWarnings("unchecked") + public T fromJson(String json, Type typeOfT) throws JsonSyntaxException { + if (json == null) { + return null; + } + StringReader reader = new StringReader(json); + T target = (T) fromJson(reader, typeOfT); + return target; + } + + /** + * This method deserializes the Json read from the specified reader into an object of the + * specified class. It is not suitable to use if the specified class is a generic type since it + * will not have the generic type information because of the Type Erasure feature of Java. + * Therefore, this method should not be used if the desired type is a generic type. Note that + * this method works fine if the any of the fields of the specified object are generics, just the + * object itself should not be a generic type. For the cases when the object is of generic type, + * invoke {@link #fromJson(Reader, Type)}. If you have the Json in a String form instead of a + * {@link Reader}, use {@link #fromJson(String, Class)} instead. + * + * @param the type of the desired object + * @param json the reader producing the Json from which the object is to be deserialized. + * @param classOfT the class of T + * @return an object of type T from the string + * @throws JsonIOException if there was a problem reading from the Reader + * @throws JsonSyntaxException if json is not a valid representation for an object of type + * @since 1.2 + */ + public T fromJson(Reader json, Class classOfT) throws JsonSyntaxException, JsonIOException { + JsonReader jsonReader = new JsonReader(json); + Object object = fromJson(jsonReader, classOfT); + assertFullConsumption(object, jsonReader); + return Primitives.wrap(classOfT).cast(object); + } + + /** + * This method deserializes the Json read from the specified reader into an object of the + * specified type. This method is useful if the specified object is a generic type. For + * non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the Json in a + * String form instead of a {@link Reader}, use {@link #fromJson(String, Type)} instead. + * + * @param the type of the desired object + * @param json the reader producing Json from which the object is to be deserialized + * @param typeOfT The specific genericized type of src. You can obtain this type by using the + * {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for + * {@code Collection}, you should use: + *
+   * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
+   * 
+ * @return an object of type T from the json + * @throws JsonIOException if there was a problem reading from the Reader + * @throws JsonSyntaxException if json is not a valid representation for an object of type + * @since 1.2 + */ + @SuppressWarnings("unchecked") + public T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException { + JsonReader jsonReader = new JsonReader(json); + T object = (T) fromJson(jsonReader, typeOfT); + assertFullConsumption(object, jsonReader); + return object; + } + + private static void assertFullConsumption(Object obj, JsonReader reader) { + try { + if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) { + throw new JsonIOException("JSON document was not fully consumed."); + } + } catch (MalformedJsonException e) { + throw new JsonSyntaxException(e); + } catch (IOException e) { + throw new JsonIOException(e); + } + } + + /** + * Reads the next JSON value from {@code reader} and convert it to an object + * of type {@code typeOfT}. + * Since Type is not parameterized by T, this method is type unsafe and should be used carefully + * + * @throws JsonIOException if there was a problem writing to the Reader + * @throws JsonSyntaxException if json is not a valid representation for an object of type + */ + @SuppressWarnings("unchecked") + public T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { + boolean isEmpty = true; + boolean oldLenient = reader.isLenient(); + reader.setLenient(true); + try { + reader.peek(); + isEmpty = false; + TypeToken typeToken = (TypeToken) TypeToken.get(typeOfT); + TypeAdapter typeAdapter = getAdapter(typeToken); + T object = typeAdapter.read(reader); + return object; + } 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); + } finally { + reader.setLenient(oldLenient); + } + } + + /** + * This method deserializes the Json read from the specified parse tree into an object of the + * specified type. It is not suitable to use if the specified class is a generic type since it + * will not have the generic type information because of the Type Erasure feature of Java. + * Therefore, this method should not be used if the desired type is a generic type. Note that + * this method works fine if the any of the fields of the specified object are generics, just the + * object itself should not be a generic type. For the cases when the object is of generic type, + * invoke {@link #fromJson(JsonElement, Type)}. + * @param the type of the desired object + * @param json the root of the parse tree of {@link JsonElement}s from which the object is to + * be deserialized + * @param classOfT The class of T + * @return an object of type T from the json + * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT + * @since 1.3 + */ + public T fromJson(JsonElement json, Class classOfT) throws JsonSyntaxException { + Object object = fromJson(json, (Type) classOfT); + return Primitives.wrap(classOfT).cast(object); + } + + /** + * This method deserializes the Json read from the specified parse tree into an object of the + * specified type. This method is useful if the specified object is a generic type. For + * non-generic objects, use {@link #fromJson(JsonElement, Class)} instead. + * + * @param the type of the desired object + * @param json the root of the parse tree of {@link JsonElement}s from which the object is to + * be deserialized + * @param typeOfT The specific genericized type of src. You can obtain this type by using the + * {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for + * {@code Collection}, you should use: + *
+   * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
+   * 
+ * @return an object of type T from the json + * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT + * @since 1.3 + */ + @SuppressWarnings("unchecked") + public T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException { + if (json == null) { + return null; + } + return (T) fromJson(new JsonTreeReader(json), typeOfT); + } + + static class FutureTypeAdapter extends TypeAdapter { + private TypeAdapter delegate; + + public void setDelegate(TypeAdapter typeAdapter) { + if (delegate != null) { + throw new AssertionError(); + } + delegate = typeAdapter; + } + + @Override public T read(JsonReader in) throws IOException { + if (delegate == null) { + throw new IllegalStateException(); + } + return delegate.read(in); + } + + @Override public void write(JsonWriter out, T value) throws IOException { + if (delegate == null) { + throw new IllegalStateException(); + } + delegate.write(out, value); + } + } + + @Override + public String toString() { + return new StringBuilder("{serializeNulls:") + .append(serializeNulls) + .append("factories:").append(factories) + .append(",instanceCreators:").append(constructorConstructor) + .append("}") + .toString(); + } +}