From 84b76c060602fb5b5512a9c4f8eb3f4ade958a07 Mon Sep 17 00:00:00 2001
From: Adrian Cole
Date: Mon, 6 Dec 2010 21:32:34 +0000
Subject: [PATCH] Issue 421: Updated to google gson 1.6, and patched their
classes to support natural parsing of Map
---
core/pom.xml | 2 +-
.../google/gson/JcloudsCompactFormatter.java | 182 ----
.../gson/JcloudsDefaultTypeAdapters.java | 928 ------------------
.../com/google/gson/JcloudsGsonBuilder.java | 591 -----------
.../gson/JcloudsGsonPackageAccessor.java | 33 +
.../com/google/gson/JcloudsTreeNavigator.java | 138 ---
.../java/com/google/gson/MapTypeAdapter.java | 71 ++
.../main/java/com/google/gson/Streams.java | 192 ++++
.../com/google/gson/stream/JsonWriter.java | 568 +++++++++++
core/src/main/java/org/jclouds/Constants.java | 4 -
.../org/jclouds/json/config/GsonModule.java | 67 +-
.../EnumTypeAdapterThatReturnsFromValue.java | 82 ++
.../json/internal/JsonObjectAsMap.java | 59 ++
.../json/internal/ParseObjectFromElement.java | 59 ++
.../java/org/jclouds/domain/JsonBallTest.java | 14 +-
.../org/jclouds/domain/JsonObjectTest.java | 98 ++
.../test/java/org/jclouds/json/JsonTest.java | 41 +
.../gogrid/config/GoGridParserModule.java | 3 -
.../ParseCredentialsFromJsonResponseTest.java | 2 -
.../ParseJobsFromJsonResponseTest.java | 2 -
...arseLoadBalancersFromJsonResponseTest.java | 2 -
...eToCredentialsMapFromJsonResponseTest.java | 2 -
.../ParseServersFromJsonResponseTest.java | 2 -
23 files changed, 1239 insertions(+), 1903 deletions(-)
delete mode 100644 core/src/main/java/com/google/gson/JcloudsCompactFormatter.java
delete mode 100644 core/src/main/java/com/google/gson/JcloudsDefaultTypeAdapters.java
delete mode 100644 core/src/main/java/com/google/gson/JcloudsGsonBuilder.java
create mode 100644 core/src/main/java/com/google/gson/JcloudsGsonPackageAccessor.java
delete mode 100644 core/src/main/java/com/google/gson/JcloudsTreeNavigator.java
create mode 100644 core/src/main/java/com/google/gson/MapTypeAdapter.java
create mode 100644 core/src/main/java/com/google/gson/Streams.java
create mode 100644 core/src/main/java/com/google/gson/stream/JsonWriter.java
create mode 100644 core/src/main/java/org/jclouds/json/internal/EnumTypeAdapterThatReturnsFromValue.java
create mode 100644 core/src/main/java/org/jclouds/json/internal/JsonObjectAsMap.java
create mode 100644 core/src/main/java/org/jclouds/json/internal/ParseObjectFromElement.java
create mode 100644 core/src/test/java/org/jclouds/domain/JsonObjectTest.java
diff --git a/core/pom.xml b/core/pom.xml
index 372ab3e87b..aa4156ab2e 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -101,7 +101,7 @@
com.google.code.gsongson
- 1.5
+ 1.6com.google.guava
diff --git a/core/src/main/java/com/google/gson/JcloudsCompactFormatter.java b/core/src/main/java/com/google/gson/JcloudsCompactFormatter.java
deleted file mode 100644
index 52670be44c..0000000000
--- a/core/src/main/java/com/google/gson/JcloudsCompactFormatter.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/**
- *
- * Copyright (C) 2010 Cloud Conscious, LLC.
- *
- * ====================================================================
- * 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.
- * ====================================================================
- */
-
-/*
- * Copyright (C) 2010 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 java.io.IOException;
-
-/**
- * The only reason I am pasting this class and changing 2 lines is because the
- * gson project uses abstract classes and final otherwise for every part one
- * might want to extend. We simply need to control the formatting of json
- * literals, and hopefully one day gson will allow us to subclass something.
- *
- * @author Adrian Cole
- * @author Inderjeet Singh
- */
-public class JcloudsCompactFormatter implements JsonFormatter {
-
- static class FormattingVisitor implements JsonElementVisitor {
- private final Appendable writer;
- private final Escaper escaper;
- private final boolean serializeNulls;
-
- FormattingVisitor(Appendable writer, Escaper escaper, boolean serializeNulls) {
- this.writer = writer;
- this.escaper = escaper;
- this.serializeNulls = serializeNulls;
- }
-
- public void visitLiteral(JsonLiteral primitive) throws IOException {
- primitive.toString(writer, escaper);
- }
-
- public void visitPrimitive(JsonPrimitive primitive) throws IOException {
- primitive.toString(writer, escaper);
- }
-
- public void visitNull() throws IOException {
- writer.append("null");
- }
-
- public void startArray(JsonArray array) throws IOException {
- writer.append('[');
- }
-
- public void visitArrayMember(JsonArray parent, JsonPrimitive member, boolean isFirst) throws IOException {
- if (!isFirst) {
- writer.append(',');
- }
- member.toString(writer, escaper);
- }
-
- public void visitArrayMember(JsonArray parent, JsonArray member, boolean isFirst) throws IOException {
- if (!isFirst) {
- writer.append(',');
- }
- }
-
- public void visitArrayMember(JsonArray parent, JsonObject member, boolean isFirst) throws IOException {
- if (!isFirst) {
- writer.append(',');
- }
- }
-
- public void visitNullArrayMember(JsonArray parent, boolean isFirst) throws IOException {
- if (!isFirst) {
- writer.append(',');
- }
- }
-
- public void endArray(JsonArray array) throws IOException {
- writer.append(']');
- }
-
- public void startObject(JsonObject object) throws IOException {
- writer.append('{');
- }
-
- public void visitObjectMember(JsonObject parent, String memberName, JsonLiteral member, boolean isFirst)
- throws IOException {
- if (!isFirst) {
- writer.append(',');
- }
- writer.append('"');
- writer.append(memberName);
- writer.append("\":");
- member.toString(writer, escaper);
- }
-
- public void visitObjectMember(JsonObject parent, String memberName, JsonPrimitive member, boolean isFirst)
- throws IOException {
- if (!isFirst) {
- writer.append(',');
- }
- writer.append('"');
- writer.append(memberName);
- writer.append("\":");
- member.toString(writer, escaper);
- }
-
- public void visitObjectMember(JsonObject parent, String memberName, JsonArray member, boolean isFirst)
- throws IOException {
- if (!isFirst) {
- writer.append(',');
- }
- writer.append('"');
- writer.append(memberName);
- writer.append("\":");
- }
-
- public void visitObjectMember(JsonObject parent, String memberName, JsonObject member, boolean isFirst)
- throws IOException {
- if (!isFirst) {
- writer.append(',');
- }
- writer.append('"');
- writer.append(memberName);
- writer.append("\":");
- }
-
- public void visitNullObjectMember(JsonObject parent, String memberName, boolean isFirst) throws IOException {
- if (serializeNulls) {
- visitObjectMember(parent, memberName, (JsonObject) null, isFirst);
- }
- }
-
- public void endObject(JsonObject object) throws IOException {
- writer.append('}');
- }
- }
-
- private final boolean escapeHtmlChars;
-
- public JcloudsCompactFormatter() {
- this(true);
- }
-
- JcloudsCompactFormatter(boolean escapeHtmlChars) {
- this.escapeHtmlChars = escapeHtmlChars;
- }
-
- public void format(JsonElement root, Appendable writer, boolean serializeNulls) throws IOException {
- if (root == null) {
- return;
- }
- FormattingVisitor visitor = new FormattingVisitor(writer, new Escaper(escapeHtmlChars), serializeNulls);
- JcloudsTreeNavigator navigator = new JcloudsTreeNavigator(visitor, serializeNulls);
- navigator.navigate(root);
- }
-}
diff --git a/core/src/main/java/com/google/gson/JcloudsDefaultTypeAdapters.java b/core/src/main/java/com/google/gson/JcloudsDefaultTypeAdapters.java
deleted file mode 100644
index 39fdc04c80..0000000000
--- a/core/src/main/java/com/google/gson/JcloudsDefaultTypeAdapters.java
+++ /dev/null
@@ -1,928 +0,0 @@
-/**
- *
- * Copyright (C) 2010 Cloud Conscious, LLC.
- *
- * ====================================================================
- * 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.
- * ====================================================================
- */
-
-/*
- * 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 java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.sql.Time;
-import java.sql.Timestamp;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.StringTokenizer;
-import java.util.TreeSet;
-import java.util.UUID;
-
-import com.google.common.base.Function;
-import com.google.common.collect.MapMaker;
-
-/**
- * List of all the default type adapters ({@link JsonSerializer}s, {@link JsonDeserializer}s, and
- * {@link InstanceCreator}s.
- *
- *
Note!
- * changed to edit the default behaviour of enum parsing by Adrian Cole
- *
- * @author Inderjeet Singh
- * @author Joel Leitch
- */
-final class JcloudsDefaultTypeAdapters {
-
- private static final DefaultDateTypeAdapter DATE_TYPE_ADAPTER = new DefaultDateTypeAdapter();
- private static final DefaultJavaSqlDateTypeAdapter JAVA_SQL_DATE_TYPE_ADAPTER = new DefaultJavaSqlDateTypeAdapter();
- private static final DefaultTimeTypeAdapter TIME_TYPE_ADAPTER = new DefaultTimeTypeAdapter();
- private static final DefaultTimestampDeserializer TIMESTAMP_DESERIALIZER = new DefaultTimestampDeserializer();
-
- @SuppressWarnings({ "rawtypes" })
- private static final EnumTypeAdapter ENUM_TYPE_ADAPTER = new EnumTypeAdapter();
- private static final UrlTypeAdapter URL_TYPE_ADAPTER = new UrlTypeAdapter();
- private static final UriTypeAdapter URI_TYPE_ADAPTER = new UriTypeAdapter();
- private static final UuidTypeAdapter UUUID_TYPE_ADAPTER = new UuidTypeAdapter();
- private static final LocaleTypeAdapter LOCALE_TYPE_ADAPTER = new LocaleTypeAdapter();
- private static final CollectionTypeAdapter COLLECTION_TYPE_ADAPTER = new CollectionTypeAdapter();
- private static final MapTypeAdapter MAP_TYPE_ADAPTER = new MapTypeAdapter();
- private static final BigDecimalTypeAdapter BIG_DECIMAL_TYPE_ADAPTER = new BigDecimalTypeAdapter();
- private static final BigIntegerTypeAdapter BIG_INTEGER_TYPE_ADAPTER = new BigIntegerTypeAdapter();
-
- private static final BooleanTypeAdapter BOOLEAN_TYPE_ADAPTER = new BooleanTypeAdapter();
- private static final ByteTypeAdapter BYTE_TYPE_ADAPTER = new ByteTypeAdapter();
- private static final CharacterTypeAdapter CHARACTER_TYPE_ADAPTER = new CharacterTypeAdapter();
- private static final DoubleDeserializer DOUBLE_TYPE_ADAPTER = new DoubleDeserializer();
- private static final FloatDeserializer FLOAT_TYPE_ADAPTER = new FloatDeserializer();
- private static final IntegerTypeAdapter INTEGER_TYPE_ADAPTER = new IntegerTypeAdapter();
- private static final LongDeserializer LONG_DESERIALIZER = new LongDeserializer();
- private static final NumberTypeAdapter NUMBER_TYPE_ADAPTER = new NumberTypeAdapter();
- private static final ShortTypeAdapter SHORT_TYPE_ADAPTER = new ShortTypeAdapter();
- private static final StringTypeAdapter STRING_TYPE_ADAPTER = new StringTypeAdapter();
-
- private static final PropertiesCreator PROPERTIES_CREATOR = new PropertiesCreator();
- private static final TreeSetCreator TREE_SET_CREATOR = new TreeSetCreator();
- private static final HashSetCreator HASH_SET_CREATOR = new HashSetCreator();
- private static final GregorianCalendarTypeAdapter GREGORIAN_CALENDAR_TYPE_ADAPTER = new GregorianCalendarTypeAdapter();
-
- // The constants DEFAULT_SERIALIZERS, DEFAULT_DESERIALIZERS, and DEFAULT_INSTANCE_CREATORS
- // must be defined after the constants for the type adapters. Otherwise, the type adapter
- // constants will appear as nulls.
- private static final ParameterizedTypeHandlerMap> DEFAULT_SERIALIZERS = createDefaultSerializers();
- private static final ParameterizedTypeHandlerMap> DEFAULT_DESERIALIZERS = createDefaultDeserializers();
- private static final ParameterizedTypeHandlerMap> DEFAULT_INSTANCE_CREATORS = createDefaultInstanceCreators();
-
- private static ParameterizedTypeHandlerMap> createDefaultSerializers() {
- ParameterizedTypeHandlerMap> map = new ParameterizedTypeHandlerMap>();
-
- map.registerForTypeHierarchy(Enum.class, ENUM_TYPE_ADAPTER);
- map.register(URL.class, URL_TYPE_ADAPTER);
- map.register(URI.class, URI_TYPE_ADAPTER);
- map.register(UUID.class, UUUID_TYPE_ADAPTER);
- map.register(Locale.class, LOCALE_TYPE_ADAPTER);
- map.registerForTypeHierarchy(Collection.class, COLLECTION_TYPE_ADAPTER);
- map.registerForTypeHierarchy(Map.class, MAP_TYPE_ADAPTER);
- map.register(Date.class, DATE_TYPE_ADAPTER);
- map.register(java.sql.Date.class, JAVA_SQL_DATE_TYPE_ADAPTER);
- map.register(Timestamp.class, DATE_TYPE_ADAPTER);
- map.register(Time.class, TIME_TYPE_ADAPTER);
- map.register(Calendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER);
- map.register(GregorianCalendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER);
- map.register(BigDecimal.class, BIG_DECIMAL_TYPE_ADAPTER);
- map.register(BigInteger.class, BIG_INTEGER_TYPE_ADAPTER);
-
- // Add primitive serializers
- map.register(Boolean.class, BOOLEAN_TYPE_ADAPTER);
- map.register(boolean.class, BOOLEAN_TYPE_ADAPTER);
- map.register(Byte.class, BYTE_TYPE_ADAPTER);
- map.register(byte.class, BYTE_TYPE_ADAPTER);
- map.register(Character.class, CHARACTER_TYPE_ADAPTER);
- map.register(char.class, CHARACTER_TYPE_ADAPTER);
- map.register(Integer.class, INTEGER_TYPE_ADAPTER);
- map.register(int.class, INTEGER_TYPE_ADAPTER);
- map.register(Number.class, NUMBER_TYPE_ADAPTER);
- map.register(Short.class, SHORT_TYPE_ADAPTER);
- map.register(short.class, SHORT_TYPE_ADAPTER);
- map.register(String.class, STRING_TYPE_ADAPTER);
-
- map.makeUnmodifiable();
- return map;
- }
-
- private static ParameterizedTypeHandlerMap> createDefaultDeserializers() {
- ParameterizedTypeHandlerMap> map = new ParameterizedTypeHandlerMap>();
- map.registerForTypeHierarchy(Enum.class, wrapDeserializer(ENUM_TYPE_ADAPTER));
- map.register(URL.class, wrapDeserializer(URL_TYPE_ADAPTER));
- map.register(URI.class, wrapDeserializer(URI_TYPE_ADAPTER));
- map.register(UUID.class, wrapDeserializer(UUUID_TYPE_ADAPTER));
- map.register(Locale.class, wrapDeserializer(LOCALE_TYPE_ADAPTER));
- map.registerForTypeHierarchy(Collection.class, wrapDeserializer(COLLECTION_TYPE_ADAPTER));
- map.registerForTypeHierarchy(Map.class, wrapDeserializer(MAP_TYPE_ADAPTER));
- map.register(Date.class, wrapDeserializer(DATE_TYPE_ADAPTER));
- map.register(java.sql.Date.class, wrapDeserializer(JAVA_SQL_DATE_TYPE_ADAPTER));
- map.register(Timestamp.class, wrapDeserializer(TIMESTAMP_DESERIALIZER));
- map.register(Time.class, wrapDeserializer(TIME_TYPE_ADAPTER));
- map.register(Calendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER);
- map.register(GregorianCalendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER);
- map.register(BigDecimal.class, wrapDeserializer(BIG_DECIMAL_TYPE_ADAPTER));
- map.register(BigInteger.class, wrapDeserializer(BIG_INTEGER_TYPE_ADAPTER));
-
- // Add primitive deserializers
- map.register(Boolean.class, wrapDeserializer(BOOLEAN_TYPE_ADAPTER));
- map.register(boolean.class, wrapDeserializer(BOOLEAN_TYPE_ADAPTER));
- map.register(Byte.class, wrapDeserializer(BYTE_TYPE_ADAPTER));
- map.register(byte.class, wrapDeserializer(BYTE_TYPE_ADAPTER));
- map.register(Character.class, wrapDeserializer(CHARACTER_TYPE_ADAPTER));
- map.register(char.class, wrapDeserializer(CHARACTER_TYPE_ADAPTER));
- map.register(Double.class, wrapDeserializer(DOUBLE_TYPE_ADAPTER));
- map.register(double.class, wrapDeserializer(DOUBLE_TYPE_ADAPTER));
- map.register(Float.class, wrapDeserializer(FLOAT_TYPE_ADAPTER));
- map.register(float.class, wrapDeserializer(FLOAT_TYPE_ADAPTER));
- map.register(Integer.class, wrapDeserializer(INTEGER_TYPE_ADAPTER));
- map.register(int.class, wrapDeserializer(INTEGER_TYPE_ADAPTER));
- map.register(Long.class, wrapDeserializer(LONG_DESERIALIZER));
- map.register(long.class, wrapDeserializer(LONG_DESERIALIZER));
- map.register(Number.class, wrapDeserializer(NUMBER_TYPE_ADAPTER));
- map.register(Short.class, wrapDeserializer(SHORT_TYPE_ADAPTER));
- map.register(short.class, wrapDeserializer(SHORT_TYPE_ADAPTER));
- map.register(String.class, wrapDeserializer(STRING_TYPE_ADAPTER));
-
- map.makeUnmodifiable();
- return map;
- }
-
- private static ParameterizedTypeHandlerMap> createDefaultInstanceCreators() {
- ParameterizedTypeHandlerMap> map = new ParameterizedTypeHandlerMap>();
- map.registerForTypeHierarchy(Map.class, MAP_TYPE_ADAPTER);
-
- // Add Collection type instance creators
- map.registerForTypeHierarchy(Collection.class, COLLECTION_TYPE_ADAPTER);
-
- map.registerForTypeHierarchy(Set.class, HASH_SET_CREATOR);
- map.registerForTypeHierarchy(SortedSet.class, TREE_SET_CREATOR);
- map.register(Properties.class, PROPERTIES_CREATOR);
- map.makeUnmodifiable();
- return map;
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- private static JsonDeserializer> wrapDeserializer(JsonDeserializer> deserializer) {
- return new JsonDeserializerExceptionWrapper(deserializer);
- }
-
- static ParameterizedTypeHandlerMap> getDefaultSerializers() {
- return getDefaultSerializers(false, LongSerializationPolicy.DEFAULT);
- }
-
- static ParameterizedTypeHandlerMap> getDefaultSerializers(
- boolean serializeSpecialFloatingPointValues, LongSerializationPolicy longSerializationPolicy) {
- ParameterizedTypeHandlerMap> serializers = new ParameterizedTypeHandlerMap>();
-
- // Double primitive
- JcloudsDefaultTypeAdapters.DoubleSerializer doubleSerializer = new JcloudsDefaultTypeAdapters.DoubleSerializer(
- serializeSpecialFloatingPointValues);
- serializers.registerIfAbsent(Double.class, doubleSerializer);
- serializers.registerIfAbsent(double.class, doubleSerializer);
-
- // Float primitive
- JcloudsDefaultTypeAdapters.FloatSerializer floatSerializer = new JcloudsDefaultTypeAdapters.FloatSerializer(
- serializeSpecialFloatingPointValues);
- serializers.registerIfAbsent(Float.class, floatSerializer);
- serializers.registerIfAbsent(float.class, floatSerializer);
-
- // Long primitive
- JcloudsDefaultTypeAdapters.LongSerializer longSerializer = new JcloudsDefaultTypeAdapters.LongSerializer(
- longSerializationPolicy);
- serializers.registerIfAbsent(Long.class, longSerializer);
- serializers.registerIfAbsent(long.class, longSerializer);
-
- serializers.registerIfAbsent(DEFAULT_SERIALIZERS);
- return serializers;
- }
-
- static ParameterizedTypeHandlerMap> getDefaultDeserializers() {
- return DEFAULT_DESERIALIZERS;
- }
-
- static ParameterizedTypeHandlerMap> getDefaultInstanceCreators() {
- return DEFAULT_INSTANCE_CREATORS;
- }
-
- static class DefaultDateTypeAdapter implements JsonSerializer, JsonDeserializer {
- private final DateFormat format;
-
- DefaultDateTypeAdapter() {
- this.format = DateFormat.getDateTimeInstance();
- }
-
- DefaultDateTypeAdapter(final String datePattern) {
- this.format = new SimpleDateFormat(datePattern);
- }
-
- DefaultDateTypeAdapter(final int style) {
- this.format = DateFormat.getDateInstance(style);
- }
-
- public DefaultDateTypeAdapter(final int dateStyle, final int timeStyle) {
- this.format = DateFormat.getDateTimeInstance(dateStyle, timeStyle);
- }
-
- // These methods need to be synchronized since JDK DateFormat classes are not thread-safe
- // See issue 162
- public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
- synchronized (format) {
- String dateFormatAsString = format.format(src);
- return new JsonPrimitive(dateFormatAsString);
- }
- }
-
- public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- if (!(json instanceof JsonPrimitive)) {
- throw new JsonParseException("The date should be a string value");
- }
- try {
- synchronized (format) {
- return format.parse(json.getAsString());
- }
- } catch (ParseException e) {
- throw new JsonParseException(e);
- }
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(DefaultDateTypeAdapter.class.getSimpleName());
- sb.append('(').append(format.getClass().getSimpleName()).append(')');
- return sb.toString();
- }
- }
-
- static class DefaultJavaSqlDateTypeAdapter implements JsonSerializer, JsonDeserializer {
- private final DateFormat format;
-
- DefaultJavaSqlDateTypeAdapter() {
- this.format = new SimpleDateFormat("MMM d, yyyy");
- }
-
- public JsonElement serialize(java.sql.Date src, Type typeOfSrc, JsonSerializationContext context) {
- synchronized (format) {
- String dateFormatAsString = format.format(src);
- return new JsonPrimitive(dateFormatAsString);
- }
- }
-
- public java.sql.Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- if (!(json instanceof JsonPrimitive)) {
- throw new JsonParseException("The date should be a string value");
- }
- try {
- synchronized (format) {
- Date date = format.parse(json.getAsString());
- return new java.sql.Date(date.getTime());
- }
- } catch (ParseException e) {
- throw new JsonParseException(e);
- }
- }
- }
-
- static class DefaultTimestampDeserializer implements JsonDeserializer {
- public Timestamp deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- Date date = context.deserialize(json, Date.class);
- return new Timestamp(date.getTime());
- }
- }
-
- static class DefaultTimeTypeAdapter implements JsonSerializer
- *
- *
NOTE: the order of invocation of configuration methods does not matter.
- *
- * @author Inderjeet Singh
- * @author Joel Leitch
- */
-public final class JcloudsGsonBuilder {
- private static final InnerClassExclusionStrategy innerClassExclusionStrategy =
- new InnerClassExclusionStrategy();
- private static final ExposeAnnotationSerializationExclusionStrategy
- exposeAnnotationSerializationExclusionStrategy =
- new ExposeAnnotationSerializationExclusionStrategy();
- private static final ExposeAnnotationDeserializationExclusionStrategy
- exposeAnnotationDeserializationExclusionStrategy =
- new ExposeAnnotationDeserializationExclusionStrategy();
-
- private final Collection exclusionStrategies =
- new HashSet();
-
- private double ignoreVersionsAfter;
- private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy;
- private boolean serializeInnerClasses;
- private boolean excludeFieldsWithoutExposeAnnotation;
- private LongSerializationPolicy longSerializationPolicy;
- private FieldNamingStrategy2 fieldNamingPolicy;
- private final ParameterizedTypeHandlerMap> instanceCreators;
- private final ParameterizedTypeHandlerMap> serializers;
- private final ParameterizedTypeHandlerMap> deserializers;
- private boolean serializeNulls;
- private String datePattern;
- private int dateStyle;
- private int timeStyle;
- private boolean serializeSpecialFloatingPointValues;
- private boolean escapeHtmlChars;
- private boolean prettyPrinting;
- private boolean generateNonExecutableJson;
-
- /**
- * Creates a JcloudsGsonBuilder instance that can be used to build Gson with various configuration
- * settings. JcloudsGsonBuilder follows the builder pattern, and it is typically used by first
- * invoking various configuration methods to set desired options, and finally calling
- * {@link #create()}.
- */
- public JcloudsGsonBuilder() {
- // add default exclusion strategies
- exclusionStrategies.add(Gson.DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY);
- exclusionStrategies.add(Gson.DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY);
-
- // setup default values
- ignoreVersionsAfter = VersionConstants.IGNORE_VERSIONS;
- serializeInnerClasses = true;
- prettyPrinting = false;
- escapeHtmlChars = true;
- modifierBasedExclusionStrategy = Gson.DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY;
- excludeFieldsWithoutExposeAnnotation = false;
- longSerializationPolicy = LongSerializationPolicy.DEFAULT;
- fieldNamingPolicy = Gson.DEFAULT_NAMING_POLICY;
- instanceCreators = new ParameterizedTypeHandlerMap>();
- serializers = new ParameterizedTypeHandlerMap>();
- deserializers = new ParameterizedTypeHandlerMap>();
- serializeNulls = false;
- dateStyle = DateFormat.DEFAULT;
- timeStyle = DateFormat.DEFAULT;
- serializeSpecialFloatingPointValues = false;
- generateNonExecutableJson = false;
- }
-
- /**
- * Configures Gson to enable versioning support.
- *
- * @param ignoreVersionsAfter any field or type marked with a version higher than this value
- * are ignored during serialization or deserialization.
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- */
- public JcloudsGsonBuilder setVersion(double ignoreVersionsAfter) {
- this.ignoreVersionsAfter = ignoreVersionsAfter;
- return this;
- }
-
- /**
- * Configures Gson to excludes all class fields that have the specified modifiers. By default,
- * Gson will exclude all fields marked transient or static. This method will override that
- * behavior.
- *
- * @param modifiers the field modifiers. You must use the modifiers specified in the
- * {@link java.lang.reflect.Modifier} class. For example,
- * {@link java.lang.reflect.Modifier#TRANSIENT},
- * {@link java.lang.reflect.Modifier#STATIC}.
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- */
- public JcloudsGsonBuilder excludeFieldsWithModifiers(int... modifiers) {
- modifierBasedExclusionStrategy = new ModifierBasedExclusionStrategy(modifiers);
- return this;
- }
-
- /**
- * Makes the output JSON non-executable in Javascript by prefixing the generated JSON with some
- * special text. This prevents attacks from third-party sites through script sourcing. See
- * Gson Issue 42
- * for details.
- *
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.3
- */
- public JcloudsGsonBuilder generateNonExecutableJson() {
- this.generateNonExecutableJson = true;
- return this;
- }
-
- /**
- * Configures Gson to exclude all fields from consideration for serialization or deserialization
- * that do not have the {@link com.google.gson.annotations.Expose} annotation.
- *
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- */
- public JcloudsGsonBuilder excludeFieldsWithoutExposeAnnotation() {
- excludeFieldsWithoutExposeAnnotation = true;
- return this;
- }
-
- /**
- * Configure Gson to serialize null fields. By default, Gson omits all fields that are null
- * during serialization.
- *
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.2
- */
- public JcloudsGsonBuilder serializeNulls() {
- this.serializeNulls = true;
- return this;
- }
-
- /**
- * Configures Gson to exclude inner classes during serialization.
- *
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.3
- */
- public JcloudsGsonBuilder disableInnerClassSerialization() {
- serializeInnerClasses = false;
- return this;
- }
-
- /**
- * Configures Gson to apply a specific serialization policy for {@code Long} and {@code long}
- * objects.
- *
- * @param serializationPolicy the particular policy to use for serializing longs.
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.3
- */
- public JcloudsGsonBuilder setLongSerializationPolicy(LongSerializationPolicy serializationPolicy) {
- this.longSerializationPolicy = serializationPolicy;
- return this;
- }
-
- /**
- * Configures Gson to apply a specific naming policy to an object's field during serialization
- * and deserialization.
- *
- * @param namingConvention the JSON field naming convention to use for serialization and
- * deserialization.
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- */
- public JcloudsGsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) {
- return setFieldNamingStrategy(namingConvention.getFieldNamingPolicy());
- }
-
- /**
- * Configures Gson to apply a specific naming policy strategy to an object's field during
- * serialization and deserialization.
- *
- * @param fieldNamingStrategy the actual naming strategy to apply to the fields
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.3
- */
- public JcloudsGsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) {
- return setFieldNamingStrategy(new FieldNamingStrategy2Adapter(fieldNamingStrategy));
- }
-
- /**
- * Configures Gson to apply a specific naming policy strategy to an object's field during
- * serialization and deserialization.
- *
- * @param fieldNamingStrategy the actual naming strategy to apply to the fields
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- */
- JcloudsGsonBuilder setFieldNamingStrategy(FieldNamingStrategy2 fieldNamingStrategy) {
- this.fieldNamingPolicy =
- new SerializedNameAnnotationInterceptingNamingPolicy(fieldNamingStrategy);
- return this;
- }
-
- /**
- * Configures Gson to apply a set of exclusion strategies during both serialization and
- * deserialization. Each of the {@code strategies} will be applied as a disjunction rule.
- * This means that if one of the {@code strategies} suggests that a field (or class) should be
- * skipped then that field (or object) is skipped during serializaiton/deserialization.
- *
- * @param strategies the set of strategy object to apply during object (de)serialization.
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.4
- */
- public JcloudsGsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) {
- for (ExclusionStrategy strategy : strategies) {
- exclusionStrategies.add(strategy);
- }
- return this;
- }
-
- /**
- * Configures Gson to output Json that fits in a page for pretty printing. This option only
- * affects Json serialization.
- *
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- */
- public JcloudsGsonBuilder setPrettyPrinting() {
- prettyPrinting = true;
- return this;
- }
-
- /**
- * By default, Gson escapes HTML characters such as < > etc. Use this option to configure
- * Gson to pass-through HTML characters as is.
- *
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.3
- */
- public JcloudsGsonBuilder disableHtmlEscaping() {
- this.escapeHtmlChars = false;
- return this;
- }
-
- /**
- * Configures Gson to serialize {@code Date} objects according to the pattern provided. You can
- * call this method or {@link #setDateFormat(int)} multiple times, but only the last invocation
- * will be used to decide the serialization format.
- *
- *
Note that this pattern must abide by the convention provided by {@code SimpleDateFormat}
- * class. See the documentation in {@link java.text.SimpleDateFormat} for more information on
- * valid date and time patterns.
- *
- * @param pattern the pattern that dates will be serialized/deserialized to/from
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.2
- */
- public JcloudsGsonBuilder setDateFormat(String pattern) {
- // TODO(Joel): Make this fail fast if it is an invalid date format
- this.datePattern = pattern;
- return this;
- }
-
- /**
- * Configures Gson to to serialize {@code Date} objects according to the style value provided.
- * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last
- * invocation will be used to decide the serialization format.
- *
- *
Note that this style value should be one of the predefined constants in the
- * {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more
- * information on the valid style constants.
- *
- * @param style the predefined date style that date objects will be serialized/deserialized
- * to/from
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.2
- */
- public JcloudsGsonBuilder setDateFormat(int style) {
- this.dateStyle = style;
- this.datePattern = null;
- return this;
- }
-
- /**
- * Configures Gson to to serialize {@code Date} objects according to the style value provided.
- * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last
- * invocation will be used to decide the serialization format.
- *
- *
Note that this style value should be one of the predefined constants in the
- * {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more
- * information on the valid style constants.
- *
- * @param dateStyle the predefined date style that date objects will be serialized/deserialized
- * to/from
- * @param timeStyle the predefined style for the time portion of the date objects
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.2
- */
- public JcloudsGsonBuilder setDateFormat(int dateStyle, int timeStyle) {
- this.dateStyle = dateStyle;
- this.timeStyle = timeStyle;
- this.datePattern = null;
- return this;
- }
-
- /**
- * Configures Gson for custom serialization or deserialization. This method combines the
- * registration of an {@link InstanceCreator}, {@link JsonSerializer}, and a
- * {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements
- * all the required interfaces for custom serialization with Gson. If an instance creator,
- * serializer or deserializer was previously registered for the specified {@code type}, it is
- * overwritten.
- *
- * @param type the type definition for the type adapter being registered
- * @param typeAdapter This object must implement at least one of the {@link InstanceCreator},
- * {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- */
- public JcloudsGsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
- Preconditions.checkArgument(typeAdapter instanceof JsonSerializer>
- || typeAdapter instanceof JsonDeserializer> || typeAdapter instanceof InstanceCreator>);
- if (typeAdapter instanceof InstanceCreator>) {
- registerInstanceCreator(type, (InstanceCreator>) typeAdapter);
- }
- if (typeAdapter instanceof JsonSerializer>) {
- registerSerializer(type, (JsonSerializer>) typeAdapter);
- }
- if (typeAdapter instanceof JsonDeserializer>) {
- registerDeserializer(type, (JsonDeserializer>) typeAdapter);
- }
- return this;
- }
-
- /**
- * Configures Gson to use a custom {@link InstanceCreator} for the specified type. If an instance
- * creator was previously registered for the specified class, it is overwritten. Since this method
- * takes a type instead of a Class object, it can be used to register a specific handler for a
- * generic type corresponding to a raw type.
- *
- * @param the type for which instance creator is being registered
- * @param typeOfT The Type definition for T
- * @param instanceCreator the instance creator for T
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- */
- private JcloudsGsonBuilder registerInstanceCreator(Type typeOfT,
- InstanceCreator extends T> instanceCreator) {
- instanceCreators.register(typeOfT, instanceCreator);
- return this;
- }
-
- /**
- * Configures Gson to use a custom JSON serializer for the specified type. You should use this
- * method if you want to register different serializers for different generic types corresponding
- * to a raw type.
- *
- * @param the type for which the serializer is being registered
- * @param typeOfT The type definition for T
- * @param serializer the custom serializer
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- */
- private JcloudsGsonBuilder registerSerializer(Type typeOfT, final JsonSerializer serializer) {
- serializers.register(typeOfT, serializer);
- return this;
- }
-
- /**
- * Configures Gson to use a custom JSON deserializer for the specified type. You should use this
- * method if you want to register different deserializers for different generic types
- * corresponding to a raw type.
- *
- * @param the type for which the deserializer is being registered
- * @param typeOfT The type definition for T
- * @param deserializer the custom deserializer
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- */
- private JcloudsGsonBuilder registerDeserializer(Type typeOfT, JsonDeserializer deserializer) {
- deserializers.register(typeOfT, new JsonDeserializerExceptionWrapper(deserializer));
- return this;
- }
-
- /**
- * Configures Gson for custom serialization or deserialization for an inheritance type hierarchy.
- * This method combines the registration of an {@link InstanceCreator}, {@link JsonSerializer},
- * and a {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter}
- * implements all the required interfaces for custom serialization with Gson.
- * If an instance creator, serializer or deserializer was previously registered for the specified
- * type hierarchy, it is overwritten. If an instance creator, serializer or deserializer is
- * registered for a specific type in the type hierarchy, it will be invoked instead of the one
- * registered for the type hierarchy.
- *
- * @param baseType the class definition for the type adapter being registered for the base class
- * or interface
- * @param typeAdapter This object must implement at least one of the {@link InstanceCreator},
- * {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.5
- */
- JcloudsGsonBuilder registerTypeHierarchyAdapter(Class> baseType, Object typeAdapter) {
- Preconditions.checkArgument(typeAdapter instanceof JsonSerializer>
- || typeAdapter instanceof JsonDeserializer> || typeAdapter instanceof InstanceCreator>);
- if (typeAdapter instanceof InstanceCreator>) {
- registerInstanceCreatorForTypeHierarchy(baseType, (InstanceCreator>) typeAdapter);
- }
- if (typeAdapter instanceof JsonSerializer>) {
- registerSerializerForTypeHierarchy(baseType, (JsonSerializer>) typeAdapter);
- }
- if (typeAdapter instanceof JsonDeserializer>) {
- registerDeserializerForTypeHierarchy(baseType, (JsonDeserializer>) typeAdapter);
- }
- return this;
- }
-
- private JcloudsGsonBuilder registerInstanceCreatorForTypeHierarchy(Class> classOfT,
- InstanceCreator extends T> instanceCreator) {
- instanceCreators.registerForTypeHierarchy(classOfT, instanceCreator);
- return this;
- }
-
- private JcloudsGsonBuilder registerSerializerForTypeHierarchy(Class> classOfT,
- final JsonSerializer serializer) {
- serializers.registerForTypeHierarchy(classOfT, serializer);
- return this;
- }
-
- private JcloudsGsonBuilder registerDeserializerForTypeHierarchy(Class> classOfT,
- JsonDeserializer deserializer) {
- deserializers.registerForTypeHierarchy(classOfT,
- new JsonDeserializerExceptionWrapper(deserializer));
- return this;
- }
-
- /**
- * Section 2.4 of JSON specification disallows
- * special double values (NaN, Infinity, -Infinity). However,
- * Javascript
- * specification (see section 4.3.20, 4.3.22, 4.3.23) allows these values as valid Javascript
- * values. Moreover, most JavaScript engines will accept these special values in JSON without
- * problem. So, at a practical level, it makes sense to accept these values as valid JSON even
- * though JSON specification disallows them.
- *
- *
Gson always accepts these special values during deserialization. However, it outputs
- * strictly compliant JSON. Hence, if it encounters a float value {@link Float#NaN},
- * {@link Float#POSITIVE_INFINITY}, {@link Float#NEGATIVE_INFINITY}, or a double value
- * {@link Double#NaN}, {@link Double#POSITIVE_INFINITY}, {@link Double#NEGATIVE_INFINITY}, it
- * will throw an {@link IllegalArgumentException}. This method provides a way to override the
- * default behavior when you know that the JSON receiver will be able to handle these special
- * values.
- *
- * @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.3
- */
- public JcloudsGsonBuilder serializeSpecialFloatingPointValues() {
- this.serializeSpecialFloatingPointValues = true;
- return this;
- }
-
- /**
- * Creates a {@link Gson} instance based on the current configuration. This method is free of
- * side-effects to this {@code JcloudsGsonBuilder} instance and hence can be called multiple times.
- *
- * @return an instance of Gson configured with the options currently set in this builder
- */
- public Gson create() {
- List serializationStrategies =
- new LinkedList(exclusionStrategies);
- List deserializationStrategies =
- new LinkedList(exclusionStrategies);
-
- serializationStrategies.add(modifierBasedExclusionStrategy);
- deserializationStrategies.add(modifierBasedExclusionStrategy);
-
- if (!serializeInnerClasses) {
- serializationStrategies.add(innerClassExclusionStrategy);
- deserializationStrategies.add(innerClassExclusionStrategy);
- }
- if (ignoreVersionsAfter != VersionConstants.IGNORE_VERSIONS) {
- serializationStrategies.add(new VersionExclusionStrategy(ignoreVersionsAfter));
- deserializationStrategies.add(new VersionExclusionStrategy(ignoreVersionsAfter));
- }
- if (excludeFieldsWithoutExposeAnnotation) {
- serializationStrategies.add(exposeAnnotationSerializationExclusionStrategy);
- deserializationStrategies.add(exposeAnnotationDeserializationExclusionStrategy);
- }
- ExclusionStrategy serializationExclusionStrategy =
- new DisjunctionExclusionStrategy(serializationStrategies);
- ExclusionStrategy deserializationExclusionStrategy =
- new DisjunctionExclusionStrategy(deserializationStrategies);
-
- ParameterizedTypeHandlerMap> customSerializers = serializers.copyOf();
- ParameterizedTypeHandlerMap> customDeserializers = deserializers.copyOf();
- addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, customSerializers,
- customDeserializers);
-
- customSerializers.registerIfAbsent(JcloudsDefaultTypeAdapters.getDefaultSerializers(
- serializeSpecialFloatingPointValues, longSerializationPolicy));
-
- customDeserializers.registerIfAbsent(JcloudsDefaultTypeAdapters.getDefaultDeserializers());
-
- ParameterizedTypeHandlerMap> customInstanceCreators =
- instanceCreators.copyOf();
- customInstanceCreators.registerIfAbsent(JcloudsDefaultTypeAdapters.getDefaultInstanceCreators());
-
- customSerializers.makeUnmodifiable();
- customDeserializers.makeUnmodifiable();
- instanceCreators.makeUnmodifiable();
-
- MappedObjectConstructor objConstructor = new MappedObjectConstructor(customInstanceCreators);
-
- JsonFormatter formatter = prettyPrinting ?
- new JsonPrintFormatter(escapeHtmlChars) : new JsonCompactFormatter(escapeHtmlChars);
- Gson gson = new Gson(serializationExclusionStrategy, deserializationExclusionStrategy,
- fieldNamingPolicy, objConstructor, formatter, serializeNulls, customSerializers,
- customDeserializers, generateNonExecutableJson);
- return gson;
- }
-
- private static void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
- ParameterizedTypeHandlerMap> serializers,
- ParameterizedTypeHandlerMap> deserializers) {
- DefaultDateTypeAdapter dateTypeAdapter = null;
- if (datePattern != null && !"".equals(datePattern.trim())) {
- dateTypeAdapter = new DefaultDateTypeAdapter(datePattern);
- } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) {
- dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle);
- }
-
- if (dateTypeAdapter != null) {
- if (!serializers.hasSpecificHandlerFor(Date.class)) {
- serializers.register(Date.class, dateTypeAdapter);
- }
- if (!deserializers.hasSpecificHandlerFor(Date.class)) {
- deserializers.register(Date.class, dateTypeAdapter);
- }
- }
- }
-}
diff --git a/core/src/main/java/com/google/gson/JcloudsGsonPackageAccessor.java b/core/src/main/java/com/google/gson/JcloudsGsonPackageAccessor.java
new file mode 100644
index 0000000000..20b7bbdf26
--- /dev/null
+++ b/core/src/main/java/com/google/gson/JcloudsGsonPackageAccessor.java
@@ -0,0 +1,33 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * 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;
+
+/**
+ * handles the limitation in gson 1.6 where
+ * {@link GsonBuilder#registerTypeHierarchyAdapter(Class, Object)} is package private.
+ *
+ * @see gson issue 271
+ * @author Adrian Cole
+ */
+public class JcloudsGsonPackageAccessor {
+ public static GsonBuilder registerTypeHierarchyAdapter(GsonBuilder builder, Class> baseType, Object typeAdapter) {
+ return builder.registerTypeHierarchyAdapter(baseType, typeAdapter);
+ }
+}
diff --git a/core/src/main/java/com/google/gson/JcloudsTreeNavigator.java b/core/src/main/java/com/google/gson/JcloudsTreeNavigator.java
deleted file mode 100644
index 57447a5b12..0000000000
--- a/core/src/main/java/com/google/gson/JcloudsTreeNavigator.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/**
- *
- * Copyright (C) 2010 Cloud Conscious, LLC.
- *
- * ====================================================================
- * 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.
- * ====================================================================
- */
-
-/*
- * Copyright (C) 2010 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 java.io.IOException;
-import java.util.Map;
-
-import com.google.gson.JcloudsCompactFormatter.FormattingVisitor;
-
-/**
- * from JsonTreeNavigator modified so that we can use JsonLiteral.
- *
- * @author Inderjeet Singh
- */
-public class JcloudsTreeNavigator {
- private final FormattingVisitor visitor;
- private final boolean visitNulls;
-
- JcloudsTreeNavigator(FormattingVisitor visitor, boolean visitNulls) {
- this.visitor = visitor;
- this.visitNulls = visitNulls;
- }
-
- public void navigate(JsonElement element) throws IOException {
- if (element.isJsonNull()) {
- visitor.visitNull();
- } else if (element.isJsonArray()) {
- JsonArray array = element.getAsJsonArray();
- visitor.startArray(array);
- boolean isFirst = true;
- for (JsonElement child : array) {
- visitChild(array, child, isFirst);
- if (isFirst) {
- isFirst = false;
- }
- }
- visitor.endArray(array);
- } else if (element.isJsonObject()) {
- JsonObject object = element.getAsJsonObject();
- visitor.startObject(object);
- boolean isFirst = true;
- for (Map.Entry member : object.entrySet()) {
- boolean visited = visitChild(object, member.getKey(), member.getValue(), isFirst);
- if (visited && isFirst) {
- isFirst = false;
- }
- }
- visitor.endObject(object);
- } else if (element instanceof JsonLiteral) {
- visitor.visitLiteral(JsonLiteral.class.cast(element));
- } else { // must be JsonPrimitive
- visitor.visitPrimitive(element.getAsJsonPrimitive());
- }
- }
-
- /**
- * Returns true if the child was visited, false if it was skipped.
- */
- private boolean visitChild(JsonObject parent, String childName, JsonElement child, boolean isFirst)
- throws IOException {
- if (child.isJsonNull()) {
- if (visitNulls) {
- visitor.visitNullObjectMember(parent, childName, isFirst);
- navigate(child.getAsJsonNull());
- } else { // Null value is being skipped.
- return false;
- }
- } else if (child.isJsonArray()) {
- JsonArray childAsArray = child.getAsJsonArray();
- visitor.visitObjectMember(parent, childName, childAsArray, isFirst);
- navigate(childAsArray);
- } else if (child.isJsonObject()) {
- JsonObject childAsObject = child.getAsJsonObject();
- visitor.visitObjectMember(parent, childName, childAsObject, isFirst);
- navigate(childAsObject);
- } else if (child instanceof JsonLiteral) {
- visitor.visitObjectMember(parent, childName, JsonLiteral.class.cast(child), isFirst);
- } else { // is a JsonPrimitive
- visitor.visitObjectMember(parent, childName, child.getAsJsonPrimitive(), isFirst);
- }
- return true;
- }
-
- /**
- * Returns true if the child was visited, false if it was skipped.
- */
- private void visitChild(JsonArray parent, JsonElement child, boolean isFirst) throws IOException {
- if (child instanceof JsonLiteral) {
- visitor.visitLiteral(JsonLiteral.class.cast(child));
- } else if (child.isJsonNull()) {
- visitor.visitNullArrayMember(parent, isFirst);
- navigate(child);
- } else if (child.isJsonArray()) {
- JsonArray childAsArray = child.getAsJsonArray();
- visitor.visitArrayMember(parent, childAsArray, isFirst);
- navigate(childAsArray);
- } else if (child.isJsonObject()) {
- JsonObject childAsObject = child.getAsJsonObject();
- visitor.visitArrayMember(parent, childAsObject, isFirst);
- navigate(childAsObject);
- } else { // is a JsonPrimitive
- visitor.visitArrayMember(parent, child.getAsJsonPrimitive(), isFirst);
- }
- }
-}
diff --git a/core/src/main/java/com/google/gson/MapTypeAdapter.java b/core/src/main/java/com/google/gson/MapTypeAdapter.java
new file mode 100644
index 0000000000..34e5a1cf1a
--- /dev/null
+++ b/core/src/main/java/com/google/gson/MapTypeAdapter.java
@@ -0,0 +1,71 @@
+package com.google.gson;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.json.internal.ParseObjectFromElement;
+
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public class MapTypeAdapter implements JsonSerializer, JsonDeserializer, InstanceCreator {
+
+ public JsonElement serialize(Map src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject map = new JsonObject();
+ Type childGenericType = null;
+ if (typeOfSrc instanceof ParameterizedType) {
+ childGenericType = new TypeInfoMap(typeOfSrc).getValueType();
+ }
+
+ for (Map.Entry entry : (Set) src.entrySet()) {
+ Object value = entry.getValue();
+
+ JsonElement valueElement;
+ if (value == null) {
+ valueElement = JsonNull.createJsonNull();
+ } else {
+ Type childType = (childGenericType == null) ? value.getClass() : childGenericType;
+ valueElement = context.serialize(value, childType);
+ }
+ map.add(String.valueOf(entry.getKey()), valueElement);
+ }
+ return map;
+ }
+
+ public Map deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ // Use ObjectConstructor to create instance instead of hard-coding a specific type.
+ // This handles cases where users are using their own subclass of Map.
+ Map map = constructMapType(typeOfT, context);
+ TypeInfoMap mapTypeInfo = new TypeInfoMap(typeOfT);
+ for (Map.Entry entry : json.getAsJsonObject().entrySet()) {
+ Object key = context.deserialize(new JsonPrimitive(entry.getKey()), mapTypeInfo.getKeyType());
+ // START JCLOUDS PATCH
+ Object value = null;
+ if (mapTypeInfo.getValueType() == Object.class) {
+ value = ParseObjectFromElement.SINGLETON.apply(entry.getValue());
+ }
+ if (value == null) {
+ value = context.deserialize(entry.getValue(), mapTypeInfo.getValueType());
+ }
+ // END JCLOUDS PATCH
+ map.put(key, value);
+ }
+ return map;
+ }
+
+ private Map constructMapType(Type mapType, JsonDeserializationContext context) {
+ JsonDeserializationContextDefault contextImpl = (JsonDeserializationContextDefault) context;
+ ObjectConstructor objectConstructor = contextImpl.getObjectConstructor();
+ return (Map) objectConstructor.construct(mapType);
+ }
+
+ public Map createInstance(Type type) {
+ return new LinkedHashMap();
+ }
+
+ @Override
+ public String toString() {
+ return MapTypeAdapter.class.getSimpleName();
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/google/gson/Streams.java b/core/src/main/java/com/google/gson/Streams.java
new file mode 100644
index 0000000000..89dc9ba2b4
--- /dev/null
+++ b/core/src/main/java/com/google/gson/Streams.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2010 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.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import com.google.gson.stream.MalformedJsonException;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+
+/**
+ * Reads and writes GSON parse trees over streams.
+ */
+final class Streams {
+
+ /**
+ * Takes a reader in any state and returns the next value as a JsonElement.
+ */
+ static JsonElement parse(JsonReader reader) throws JsonParseException {
+ boolean isEmpty = true;
+ try {
+ reader.peek();
+ isEmpty = false;
+ return parseRecursive(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.createJsonNull();
+ }
+ throw new JsonIOException(e);
+ } catch (MalformedJsonException e) {
+ throw new JsonSyntaxException(e);
+ } catch (IOException e) {
+ throw new JsonIOException(e);
+ } catch (NumberFormatException e) {
+ throw new JsonSyntaxException(e);
+ }
+ }
+
+ private static JsonElement parseRecursive(JsonReader reader) throws IOException {
+ switch (reader.peek()) {
+ case STRING:
+ return new JsonPrimitive(reader.nextString());
+ case NUMBER:
+ String number = reader.nextString();
+ return new JsonPrimitive(JsonPrimitive.stringToNumber(number));
+ case BOOLEAN:
+ return new JsonPrimitive(reader.nextBoolean());
+ case NULL:
+ reader.nextNull();
+ return JsonNull.createJsonNull();
+ case BEGIN_ARRAY:
+ JsonArray array = new JsonArray();
+ reader.beginArray();
+ while (reader.hasNext()) {
+ array.add(parseRecursive(reader));
+ }
+ reader.endArray();
+ return array;
+ case BEGIN_OBJECT:
+ JsonObject object = new JsonObject();
+ reader.beginObject();
+ while (reader.hasNext()) {
+ object.add(reader.nextName(), parseRecursive(reader));
+ }
+ reader.endObject();
+ return object;
+ case END_DOCUMENT:
+ case NAME:
+ case END_OBJECT:
+ case END_ARRAY:
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Writes the JSON element to the writer, recursively.
+ */
+ static void write(JsonElement element, boolean serializeNulls, JsonWriter writer)
+ throws IOException {
+ if (element == null || element.isJsonNull()) {
+ if (serializeNulls) {
+ writer.nullValue();
+ }
+//BEGIN JCLOUDS PATCH
+ } else if (element instanceof JsonLiteral ) {
+ writer.value(JsonLiteral.class.cast(element));
+ //END JCLOUDS PATCH
+ } else if (element.isJsonPrimitive()) {
+ JsonPrimitive primitive = element.getAsJsonPrimitive();
+ if (primitive.isNumber()) {
+ writer.value(primitive.getAsNumber());
+ } else if (primitive.isBoolean()) {
+ writer.value(primitive.getAsBoolean());
+ } else {
+ writer.value(primitive.getAsString());
+ }
+
+ } else if (element.isJsonArray()) {
+ writer.beginArray();
+ for (JsonElement e : element.getAsJsonArray()) {
+ /* always print null when its parent element is an array! */
+ if (e.isJsonNull()) {
+ writer.nullValue();
+ continue;
+ }
+ write(e, serializeNulls, writer);
+ }
+ writer.endArray();
+
+ } else if (element.isJsonObject()) {
+ writer.beginObject();
+ for (Map.Entry e : element.getAsJsonObject().entrySet()) {
+ JsonElement value = e.getValue();
+ if (!serializeNulls && value.isJsonNull()) {
+ continue;
+ }
+ writer.name(e.getKey());
+ write(value, serializeNulls, writer);
+ }
+ writer.endObject();
+
+ } else {
+ throw new IllegalArgumentException("Couldn't write " + element.getClass());
+ }
+ }
+
+ static Writer writerForAppendable(Appendable appendable) {
+ return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
+ }
+
+ /**
+ * Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer}
+ * is used.
+ */
+ private static class AppendableWriter extends Writer {
+ private final Appendable appendable;
+ private final CurrentWrite currentWrite = new CurrentWrite();
+
+ private AppendableWriter(Appendable appendable) {
+ this.appendable = appendable;
+ }
+
+ @Override public void write(char[] chars, int offset, int length) throws IOException {
+ currentWrite.chars = chars;
+ appendable.append(currentWrite, offset, offset + length);
+ }
+
+ @Override public void write(int i) throws IOException {
+ appendable.append((char) i);
+ }
+
+ @Override public void flush() {}
+ @Override public void close() {}
+
+ /**
+ * A mutable char sequence pointing at a single char[].
+ */
+ static class CurrentWrite implements CharSequence {
+ char[] chars;
+ public int length() {
+ return chars.length;
+ }
+ public char charAt(int i) {
+ return chars[i];
+ }
+ public CharSequence subSequence(int start, int end) {
+ return new String(chars, start, end - start);
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/com/google/gson/stream/JsonWriter.java b/core/src/main/java/com/google/gson/stream/JsonWriter.java
new file mode 100644
index 0000000000..75a35a8018
--- /dev/null
+++ b/core/src/main/java/com/google/gson/stream/JsonWriter.java
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2010 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.stream;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gson.JsonLiteral;
+
+/**
+ * Writes a JSON (RFC 4627)
+ * encoded value to a stream, one token at a time. The stream includes both
+ * literal values (strings, numbers, booleans and nulls) as well as the begin
+ * and end delimiters of objects and arrays.
+ *
+ *
Encoding JSON
+ * To encode your data as JSON, create a new {@code JsonWriter}. Each JSON
+ * document must contain one top-level array or object. Call methods on the
+ * writer as you walk the structure's contents, nesting arrays and objects as
+ * necessary:
+ *
+ *
To write arrays, first call {@link #beginArray()}.
+ * Write each of the array's elements with the appropriate {@link #value}
+ * methods or by nesting other arrays and objects. Finally close the array
+ * using {@link #endArray()}.
+ *
To write objects, first call {@link #beginObject()}.
+ * Write each of the object's properties by alternating calls to
+ * {@link #name} with the property's value. Write property values with the
+ * appropriate {@link #value} method or by nesting other objects or arrays.
+ * Finally close the object using {@link #endObject()}.
+ *
+ *
+ *
Example
+ * Suppose we'd like to encode a stream of messages such as the following:
Each {@code JsonWriter} may be used to write a single JSON stream.
+ * Instances of this class are not thread safe. Calls that would result in a
+ * malformed JSON string will fail with an {@link IllegalStateException}.
+ *
+ * @author Jesse Wilson
+ * @since 1.6
+ */
+public final class JsonWriter implements Closeable {
+
+ /** The output data, containing at most one top-level array or object. */
+ private final Writer out;
+
+ private final List stack = new ArrayList();
+ {
+ stack.add(JsonScope.EMPTY_DOCUMENT);
+ }
+
+ /**
+ * A string containing a full set of spaces for a single level of
+ * indentation, or null for no pretty printing.
+ */
+ private String indent;
+
+ /**
+ * The name/value separator; either ":" or ": ".
+ */
+ private String separator = ":";
+
+ private boolean lenient;
+
+ private boolean htmlSafe;
+
+ /**
+ * Creates a new instance that writes a JSON-encoded stream to {@code out}.
+ * For best performance, ensure {@link Writer} is buffered; wrapping in
+ * {@link java.io.BufferedWriter BufferedWriter} if necessary.
+ */
+ public JsonWriter(Writer out) {
+ if (out == null) {
+ throw new NullPointerException("out == null");
+ }
+ this.out = out;
+ }
+
+ /**
+ * Sets the indentation string to be repeated for each level of indentation
+ * in the encoded document. If {@code indent.isEmpty()} the encoded document
+ * will be compact. Otherwise the encoded document will be more
+ * human-readable.
+ *
+ * @param indent a string containing only whitespace.
+ */
+ public void setIndent(String indent) {
+ if (indent.length() == 0) {
+ this.indent = null;
+ this.separator = ":";
+ } else {
+ this.indent = indent;
+ this.separator = ": ";
+ }
+ }
+
+ /**
+ * Configure this writer to relax its syntax rules. By default, this writer
+ * only emits well-formed JSON as specified by RFC 4627. Setting the writer
+ * to lenient permits the following:
+ *
+ *
Top-level values of any type. With strict writing, the top-level
+ * value must be an object or an array.
+ *
Numbers may be {@link Double#isNaN() NaNs} or {@link
+ * Double#isInfinite() infinities}.
+ *
+ */
+ public void setLenient(boolean lenient) {
+ this.lenient = lenient;
+ }
+
+ /**
+ * Returns true if this writer has relaxed syntax rules.
+ */
+ public boolean isLenient() {
+ return lenient;
+ }
+
+ /**
+ * Configure this writer to emit JSON that's safe for direct inclusion in HTML
+ * and XML documents. This escapes the HTML characters {@code <}, {@code >},
+ * {@code &} and {@code =} before writing them to the stream. Without this
+ * setting, your XML/HTML encoder should replace these characters with the
+ * corresponding escape sequences.
+ */
+ public void setHtmlSafe(boolean htmlSafe) {
+ this.htmlSafe = htmlSafe;
+ }
+
+ /**
+ * Returns true if this writer writes JSON that's safe for inclusion in HTML
+ * and XML documents.
+ */
+ public boolean isHtmlSafe() {
+ return htmlSafe;
+ }
+
+ /**
+ * Begins encoding a new array. Each call to this method must be paired with
+ * a call to {@link #endArray}.
+ *
+ * @return this writer.
+ */
+ public JsonWriter beginArray() throws IOException {
+ return open(JsonScope.EMPTY_ARRAY, "[");
+ }
+
+ /**
+ * Ends encoding the current array.
+ *
+ * @return this writer.
+ */
+ public JsonWriter endArray() throws IOException {
+ return close(JsonScope.EMPTY_ARRAY, JsonScope.NONEMPTY_ARRAY, "]");
+ }
+
+ /**
+ * Begins encoding a new object. Each call to this method must be paired
+ * with a call to {@link #endObject}.
+ *
+ * @return this writer.
+ */
+ public JsonWriter beginObject() throws IOException {
+ return open(JsonScope.EMPTY_OBJECT, "{");
+ }
+
+ /**
+ * Ends encoding the current object.
+ *
+ * @return this writer.
+ */
+ public JsonWriter endObject() throws IOException {
+ return close(JsonScope.EMPTY_OBJECT, JsonScope.NONEMPTY_OBJECT, "}");
+ }
+
+ /**
+ * Enters a new scope by appending any necessary whitespace and the given
+ * bracket.
+ */
+ private JsonWriter open(JsonScope empty, String openBracket) throws IOException {
+ beforeValue(true);
+ stack.add(empty);
+ out.write(openBracket);
+ return this;
+ }
+
+ /**
+ * Closes the current scope by appending any necessary whitespace and the
+ * given bracket.
+ */
+ private JsonWriter close(JsonScope empty, JsonScope nonempty, String closeBracket)
+ throws IOException {
+ JsonScope context = peek();
+ if (context != nonempty && context != empty) {
+ throw new IllegalStateException("Nesting problem: " + stack);
+ }
+
+ stack.remove(stack.size() - 1);
+ if (context == nonempty) {
+ newline();
+ }
+ out.write(closeBracket);
+ return this;
+ }
+
+ /**
+ * Returns the value on the top of the stack.
+ */
+ private JsonScope peek() {
+ return stack.get(stack.size() - 1);
+ }
+
+ /**
+ * Replace the value on the top of the stack with the given value.
+ */
+ private void replaceTop(JsonScope topOfStack) {
+ stack.set(stack.size() - 1, topOfStack);
+ }
+
+ /**
+ * Encodes the property name.
+ *
+ * @param name the name of the forthcoming value. May not be null.
+ * @return this writer.
+ */
+ public JsonWriter name(String name) throws IOException {
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
+ beforeName();
+ string(name);
+ return this;
+ }
+
+ /**
+ * Encodes {@code value}.
+ *
+ * @param value the literal string value, or null to encode a null literal.
+ * @return this writer.
+ */
+ public JsonWriter value(String value) throws IOException {
+ if (value == null) {
+ return nullValue();
+ }
+ beforeValue(false);
+ string(value);
+ return this;
+ }
+
+ /**
+ * Encodes {@code null}.
+ *
+ * @return this writer.
+ */
+ public JsonWriter nullValue() throws IOException {
+ beforeValue(false);
+ out.write("null");
+ return this;
+ }
+//BEGIN JCLOUDS PATCH
+
+ /**
+ * Writes {@code value} literally
+ *
+ * @return this writer.
+ */
+ public JsonWriter value(JsonLiteral value) throws IOException {
+ beforeValue(false);
+ out.write(value.toString());
+ return this;
+ }
+ //END JCLOUDS PATCH
+
+ /**
+ * Encodes {@code value}.
+ *
+ * @return this writer.
+ */
+ public JsonWriter value(boolean value) throws IOException {
+ beforeValue(false);
+ out.write(value ? "true" : "false");
+ return this;
+ }
+
+ /**
+ * Encodes {@code value}.
+ *
+ * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
+ * {@link Double#isInfinite() infinities}.
+ * @return this writer.
+ */
+ public JsonWriter value(double value) throws IOException {
+ if (Double.isNaN(value) || Double.isInfinite(value)) {
+ throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
+ }
+ beforeValue(false);
+ out.append(Double.toString(value));
+ return this;
+ }
+
+ /**
+ * Encodes {@code value}.
+ *
+ * @return this writer.
+ */
+ public JsonWriter value(long value) throws IOException {
+ beforeValue(false);
+ out.write(Long.toString(value));
+ return this;
+ }
+
+ /**
+ * Encodes {@code value}.
+ *
+ * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
+ * {@link Double#isInfinite() infinities}.
+ * @return this writer.
+ */
+ public JsonWriter value(Number value) throws IOException {
+ if (value == null) {
+ return nullValue();
+ }
+
+ String string = value.toString();
+ if (!lenient
+ && (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
+ throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
+ }
+ beforeValue(false);
+ out.append(string);
+ return this;
+ }
+
+ /**
+ * Ensures all buffered data is written to the underlying {@link Writer}
+ * and flushes that writer.
+ */
+ public void flush() throws IOException {
+ out.flush();
+ }
+
+ /**
+ * Flushes and closes this writer and the underlying {@link Writer}.
+ *
+ * @throws IOException if the JSON document is incomplete.
+ */
+ public void close() throws IOException {
+ out.close();
+
+ if (peek() != JsonScope.NONEMPTY_DOCUMENT) {
+ throw new IOException("Incomplete document");
+ }
+ }
+
+ private void string(String value) throws IOException {
+ out.write("\"");
+ for (int i = 0, length = value.length(); i < length; i++) {
+ char c = value.charAt(i);
+
+ /*
+ * From RFC 4627, "All Unicode characters may be placed within the
+ * quotation marks except for the characters that must be escaped:
+ * quotation mark, reverse solidus, and the control characters
+ * (U+0000 through U+001F)."
+ */
+ switch (c) {
+ case '"':
+ case '\\':
+ out.write('\\');
+ out.write(c);
+ break;
+
+ case '\t':
+ out.write("\\t");
+ break;
+
+ case '\b':
+ out.write("\\b");
+ break;
+
+ case '\n':
+ out.write("\\n");
+ break;
+
+ case '\r':
+ out.write("\\r");
+ break;
+
+ case '\f':
+ out.write("\\f");
+ break;
+
+ case '<':
+ case '>':
+ case '&':
+ case '=':
+ case '\'':
+ if (htmlSafe) {
+ out.write(String.format("\\u%04x", (int) c));
+ } else {
+ out.write(c);
+ }
+ break;
+
+ default:
+ if (c <= 0x1F) {
+ out.write(String.format("\\u%04x", (int) c));
+ } else {
+ out.write(c);
+ }
+ break;
+ }
+ }
+ out.write("\"");
+ }
+
+ private void newline() throws IOException {
+ if (indent == null) {
+ return;
+ }
+
+ out.write("\n");
+ for (int i = 1; i < stack.size(); i++) {
+ out.write(indent);
+ }
+ }
+
+ /**
+ * Inserts any necessary separators and whitespace before a name. Also
+ * adjusts the stack to expect the name's value.
+ */
+ private void beforeName() throws IOException {
+ JsonScope context = peek();
+ if (context == JsonScope.NONEMPTY_OBJECT) { // first in object
+ out.write(',');
+ } else if (context != JsonScope.EMPTY_OBJECT) { // not in an object!
+ throw new IllegalStateException("Nesting problem: " + stack);
+ }
+ newline();
+ replaceTop(JsonScope.DANGLING_NAME);
+ }
+
+ /**
+ * Inserts any necessary separators and whitespace before a literal value,
+ * inline array, or inline object. Also adjusts the stack to expect either a
+ * closing bracket or another element.
+ *
+ * @param root true if the value is a new array or object, the two values
+ * permitted as top-level elements.
+ */
+ private void beforeValue(boolean root) throws IOException {
+ switch (peek()) {
+ case EMPTY_DOCUMENT: // first in document
+ if (!lenient && !root) {
+ throw new IllegalStateException(
+ "JSON must start with an array or an object.");
+ }
+ replaceTop(JsonScope.NONEMPTY_DOCUMENT);
+ break;
+
+ case EMPTY_ARRAY: // first in array
+ replaceTop(JsonScope.NONEMPTY_ARRAY);
+ newline();
+ break;
+
+ case NONEMPTY_ARRAY: // another in array
+ out.append(',');
+ newline();
+ break;
+
+ case DANGLING_NAME: // value for name
+ out.append(separator);
+ replaceTop(JsonScope.NONEMPTY_OBJECT);
+ break;
+
+ case NONEMPTY_DOCUMENT:
+ throw new IllegalStateException(
+ "JSON must have only one top-level value.");
+
+ default:
+ throw new IllegalStateException("Nesting problem: " + stack);
+ }
+ }
+}
diff --git a/core/src/main/java/org/jclouds/Constants.java b/core/src/main/java/org/jclouds/Constants.java
index f4e8207aa5..fd7dff67bb 100644
--- a/core/src/main/java/org/jclouds/Constants.java
+++ b/core/src/main/java/org/jclouds/Constants.java
@@ -164,10 +164,6 @@ public interface Constants {
* Name of the logger that records the steps of the request signing process of the HTTP_service.
*/
public static final String LOGGER_SIGNATURE = "jclouds.signature";
- /**
- * Name of the custom adapter bindings map for Json
- */
- public static final String PROPERTY_GSON_ADAPTERS = "jclouds.gson-adapters";
/**
* String property.
diff --git a/core/src/main/java/org/jclouds/json/config/GsonModule.java b/core/src/main/java/org/jclouds/json/config/GsonModule.java
index 55c3836b9b..c3dbdebe6c 100644
--- a/core/src/main/java/org/jclouds/json/config/GsonModule.java
+++ b/core/src/main/java/org/jclouds/json/config/GsonModule.java
@@ -19,7 +19,6 @@
package org.jclouds.json.config;
-import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.List;
@@ -28,18 +27,18 @@ import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
-import org.jclouds.Constants;
import org.jclouds.crypto.CryptoStreams;
import org.jclouds.date.DateService;
import org.jclouds.domain.JsonBall;
import org.jclouds.json.Json;
+import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue;
import org.jclouds.json.internal.GsonWrapper;
import com.google.common.collect.Maps;
import com.google.common.primitives.Bytes;
import com.google.gson.Gson;
-import com.google.gson.JcloudsCompactFormatter;
-import com.google.gson.JcloudsGsonBuilder;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JcloudsGsonPackageAccessor;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
@@ -48,11 +47,11 @@ import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
+import com.google.gson.MapTypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.inject.AbstractModule;
import com.google.inject.ImplementedBy;
import com.google.inject.Provides;
-import com.google.inject.name.Named;
/**
* Contains logic for parsing objects from Strings.
@@ -61,13 +60,16 @@ import com.google.inject.name.Named;
*/
public class GsonModule extends AbstractModule {
+ @SuppressWarnings("rawtypes")
@Provides
@Singleton
- Gson provideGson(JsonBallAdapter jsonObjectAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
+ Gson provideGson(JsonBallAdapter jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
ByteArrayAdapter byteArrayAdapter, JsonAdapterBindings bindings) throws ClassNotFoundException, Exception {
- JcloudsGsonBuilder builder = new JcloudsGsonBuilder();
-
- builder.registerTypeAdapter(JsonBall.class, jsonObjectAdapter);
+ GsonBuilder builder = new GsonBuilder();
+ JcloudsGsonPackageAccessor.registerTypeHierarchyAdapter(builder, Enum.class,
+ new EnumTypeAdapterThatReturnsFromValue());
+ JcloudsGsonPackageAccessor.registerTypeHierarchyAdapter(builder, Map.class, new MapTypeAdapter());
+ builder.registerTypeAdapter(JsonBall.class, jsonAdapter);
builder.registerTypeAdapter(Date.class, adapter);
builder.registerTypeAdapter(new TypeToken>() {
}.getType(), byteListAdapter);
@@ -75,14 +77,26 @@ public class GsonModule extends AbstractModule {
for (Map.Entry binding : bindings.getBindings().entrySet()) {
builder.registerTypeAdapter(binding.getKey(), binding.getValue());
}
- JcloudsCompactFormatter formatter = new JcloudsCompactFormatter();
+ return builder.create();
+ }
+
+ @ImplementedBy(JsonBallAdapterImpl.class)
+ public static interface JsonBallAdapter extends JsonSerializer, JsonDeserializer {
+
+ }
+
+ @Singleton
+ public static class JsonBallAdapterImpl implements JsonBallAdapter {
+
+ public JsonElement serialize(JsonBall src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonLiteral(src);
+ }
+
+ public JsonBall deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ return new JsonBall(json.toString());
+ }
- Gson gson = builder.create();
- // allow us to print json literals
- Field statFinField = Gson.class.getDeclaredField("formatter");
- statFinField.setAccessible(true);
- statFinField.set(gson, formatter);
- return gson;
}
@ImplementedBy(CDateAdapter.class)
@@ -178,25 +192,6 @@ public class GsonModule extends AbstractModule {
}
- @ImplementedBy(JsonBallAdapterImpl.class)
- public static interface JsonBallAdapter extends JsonSerializer, JsonDeserializer {
-
- }
-
- @Singleton
- public static class JsonBallAdapterImpl implements JsonBallAdapter {
-
- public JsonElement serialize(JsonBall src, Type typeOfSrc, JsonSerializationContext context) {
- return new JsonLiteral(src);
- }
-
- public JsonBall deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- return new JsonBall(json.toString());
- }
-
- }
-
@Singleton
public static class LongDateAdapter implements DateAdapter {
@@ -219,7 +214,7 @@ public class GsonModule extends AbstractModule {
private final Map bindings = Maps.newHashMap();
@com.google.inject.Inject(optional = true)
- public void setBindings(@Named(Constants.PROPERTY_GSON_ADAPTERS) Map bindings) {
+ public void setBindings(Map bindings) {
this.bindings.putAll(bindings);
}
diff --git a/core/src/main/java/org/jclouds/json/internal/EnumTypeAdapterThatReturnsFromValue.java b/core/src/main/java/org/jclouds/json/internal/EnumTypeAdapterThatReturnsFromValue.java
new file mode 100644
index 0000000000..5a3337a958
--- /dev/null
+++ b/core/src/main/java/org/jclouds/json/internal/EnumTypeAdapterThatReturnsFromValue.java
@@ -0,0 +1,82 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * 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 org.jclouds.json.internal;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import com.google.common.base.Function;
+import com.google.common.collect.MapMaker;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+
+/**
+ * @author Adrian Cole
+ */
+@SuppressWarnings("unchecked")
+public class EnumTypeAdapterThatReturnsFromValue> implements JsonSerializer, JsonDeserializer {
+ public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonPrimitive(src.name());
+ }
+
+ @SuppressWarnings("cast")
+ public T deserialize(JsonElement json, Type classOfT, JsonDeserializationContext context) throws JsonParseException {
+ try {
+ return (T) Enum.valueOf((Class) classOfT, json.getAsString());
+ } catch (IllegalArgumentException e) {
+ Method converter = classToConvert.get(classOfT);
+ if (converter != null)
+ try {
+ return (T) converter.invoke(null, json.getAsString());
+ } catch (Exception e1) {
+ throw e;
+ }
+ else
+ throw e;
+ }
+ }
+
+ private final static Map, Method> classToConvert = new MapMaker()
+ .makeComputingMap(new Function, Method>() {
+
+ @Override
+ public Method apply(Class> from) {
+ try {
+ Method method = from.getMethod("fromValue", String.class);
+ method.setAccessible(true);
+ return method;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ });
+
+ @Override
+ public String toString() {
+ return EnumTypeAdapterThatReturnsFromValue.class.getSimpleName();
+ }
+}
diff --git a/core/src/main/java/org/jclouds/json/internal/JsonObjectAsMap.java b/core/src/main/java/org/jclouds/json/internal/JsonObjectAsMap.java
new file mode 100644
index 0000000000..6cbca70974
--- /dev/null
+++ b/core/src/main/java/org/jclouds/json/internal/JsonObjectAsMap.java
@@ -0,0 +1,59 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * 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 org.jclouds.json.internal;
+
+import java.lang.reflect.Field;
+import java.util.Map;
+
+import com.google.common.base.Function;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+/**
+ * Exposes the JsonObject as a map so that we can use gauva apis on it.
+ *
+ * @author Adrian Cole
+ */
+public enum JsonObjectAsMap implements Function> {
+ INSTANCE;
+
+ private final Field members;
+
+ JsonObjectAsMap() {
+ try {
+ members = JsonObject.class.getDeclaredField("members");
+ members.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ throw new UnsupportedOperationException("cannot access gson internals", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Map apply(JsonObject in) {
+ try {
+ return (Map) members.get(in);
+ } catch (IllegalArgumentException e) {
+ throw new UnsupportedOperationException("cannot access gson internals", e);
+ } catch (IllegalAccessException e) {
+ throw new UnsupportedOperationException("cannot access gson internals", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/json/internal/ParseObjectFromElement.java b/core/src/main/java/org/jclouds/json/internal/ParseObjectFromElement.java
new file mode 100644
index 0000000000..b2412d0303
--- /dev/null
+++ b/core/src/main/java/org/jclouds/json/internal/ParseObjectFromElement.java
@@ -0,0 +1,59 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * 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 org.jclouds.json.internal;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.MapTypeAdapter;
+
+/**
+ * This is a class that helps the default {@link MapTypeAdapter} make a sane object graph when the
+ * value is set to {@code Object}
+ *
+ * @author Adrian Cole
+ */
+public enum ParseObjectFromElement implements Function {
+ SINGLETON;
+ public Object apply(JsonElement input) {
+ Object value = null;
+ if (input == null || input.isJsonNull()) {
+ value = null;
+ } else if (input.isJsonPrimitive()) {
+ JsonPrimitive primitive = input.getAsJsonPrimitive();
+ if (primitive.isNumber()) {
+ value = primitive.getAsNumber();
+ } else if (primitive.isBoolean()) {
+ value = primitive.getAsBoolean();
+ } else {
+ value = primitive.getAsString();
+ }
+ } else if (input.isJsonArray()) {
+ value = Lists.newArrayList(Iterables.transform(input.getAsJsonArray(), this));
+ } else if (input.isJsonObject()) {
+ value = Maps.newLinkedHashMap(Maps.transformValues(JsonObjectAsMap.INSTANCE.apply(input.getAsJsonObject()),
+ this));
+ }
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/jclouds/domain/JsonBallTest.java b/core/src/test/java/org/jclouds/domain/JsonBallTest.java
index f0ac8a39ba..30322ba2a4 100644
--- a/core/src/test/java/org/jclouds/domain/JsonBallTest.java
+++ b/core/src/test/java/org/jclouds/domain/JsonBallTest.java
@@ -60,20 +60,17 @@ public class JsonBallTest {
Map map = ImmutableMap. of("tomcat6", new JsonBall("{\"ssl_port\":8433}"));
- assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads
- .newStringPayload(json))), map);
+ assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
assertEquals(mapper.toJson(map), json);
}
-
public void testList() {
String json = "{\"list\":[8431,8433]}";
Map map = ImmutableMap. of("list", new JsonBall("[8431,8433]"));
- assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads
- .newStringPayload(json))), map);
+ assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
assertEquals(mapper.toJson(map), json);
}
@@ -83,20 +80,17 @@ public class JsonBallTest {
Map map = ImmutableMap. of("name", new JsonBall("fooy"));
- assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads
- .newStringPayload(json))), map);
+ assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
assertEquals(mapper.toJson(map), json);
}
-
public void testNumber() {
String json = "{\"number\":1}";
Map map = ImmutableMap. of("number", new JsonBall("1"));
- assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads
- .newStringPayload(json))), map);
+ assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
assertEquals(mapper.toJson(map), json);
}
diff --git a/core/src/test/java/org/jclouds/domain/JsonObjectTest.java b/core/src/test/java/org/jclouds/domain/JsonObjectTest.java
new file mode 100644
index 0000000000..34e50c0c5e
--- /dev/null
+++ b/core/src/test/java/org/jclouds/domain/JsonObjectTest.java
@@ -0,0 +1,98 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * 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 org.jclouds.domain;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.io.Payloads;
+import org.jclouds.json.Json;
+import org.jclouds.json.config.GsonModule;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "jclouds.ObjectTest")
+public class JsonObjectTest {
+ private ParseJson> handler;
+ private Json mapper;
+
+ @BeforeTest
+ protected void setUpInjector() throws IOException {
+ Injector injector = Guice.createInjector(new GsonModule());
+ handler = injector.getInstance(Key.get(new TypeLiteral>>() {
+ }));
+ mapper = injector.getInstance(Json.class);
+
+ }
+
+ public void testHash() {
+ String json = "{\"tomcat6\":{\"ssl_port\":8433}}";
+
+ Map map = ImmutableMap. of("tomcat6", ImmutableMap.of("ssl_port", 8433));
+
+ assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
+ assertEquals(mapper.toJson(map), json);
+
+ }
+
+ public void testList() {
+ String json = "{\"list\":[8431,8433]}";
+
+ Map map = ImmutableMap. of("list", ImmutableList.of(8431, 8433));
+
+ assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
+ assertEquals(mapper.toJson(map), json);
+
+ }
+
+ public void testString() {
+ String json = "{\"name\":\"fooy\"}";
+
+ Map map = ImmutableMap. of("name", "fooy");
+
+ assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
+ assertEquals(mapper.toJson(map), json);
+
+ }
+
+ public void testNumber() {
+ String json = "{\"number\":1}";
+
+ Map map = ImmutableMap. of("number", 1);
+
+ assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
+ assertEquals(mapper.toJson(map), json);
+
+ }
+}
diff --git a/core/src/test/java/org/jclouds/json/JsonTest.java b/core/src/test/java/org/jclouds/json/JsonTest.java
index 16f7e1eeee..2de88e7d03 100644
--- a/core/src/test/java/org/jclouds/json/JsonTest.java
+++ b/core/src/test/java/org/jclouds/json/JsonTest.java
@@ -21,11 +21,17 @@ package org.jclouds.json;
import static org.testng.Assert.assertEquals;
+import java.util.Map;
+
import org.jclouds.json.config.GsonModule;
import org.testng.annotations.Test;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
import com.google.gson.JsonParseException;
import com.google.inject.Guice;
+import com.google.inject.TypeLiteral;
@Test
public class JsonTest {
@@ -39,6 +45,41 @@ public class JsonTest {
private Test enumValue;
}
+ public void testMapStringObjectWithAllValidValuesOneDeep() {
+ Map map = Maps.newHashMap();
+ map.put("string", "string");
+ map.put("number", 1);
+ map.put("boolean", true);
+ map.put("map", ImmutableMap.of("key", "value"));
+ map.put("list", ImmutableList.of("key", "value"));
+ assertEquals(json.toJson(map),
+ "{\"string\":\"string\",\"map\":{\"key\":\"value\"},\"list\":[\"key\",\"value\"],\"boolean\":true,\"number\":1}");
+ Map map2 = json.fromJson(json.toJson(map), new TypeLiteral>() {
+ }.getType());
+ assertEquals(map2, map);
+ assertEquals(json.toJson(map2), json.toJson(map));
+ }
+
+ public void testMapStringObjectWithNumericalKeysConvertToStrings() {
+ Map map = ImmutableMap. of("map", ImmutableMap.of(1, "value"));
+ assertEquals(json.toJson(map), "{\"map\":{\"1\":\"value\"}}");
+ Map map2 = json.fromJson(json.toJson(map), new TypeLiteral>() {
+ }.getType());
+ // note conversion.. ensures valid
+ assertEquals(map2, ImmutableMap. of("map", ImmutableMap.of("1", "value")));
+ assertEquals(json.toJson(map2), json.toJson(map));
+ }
+
+ public void testMapStringObjectWithBooleanKeysConvertToStrings() {
+ Map map = ImmutableMap. of("map", ImmutableMap.of(true, "value"));
+ assertEquals(json.toJson(map), "{\"map\":{\"true\":\"value\"}}");
+ Map map2 = json.fromJson(json.toJson(map), new TypeLiteral>() {
+ }.getType());
+ // note conversion.. ensures valid
+ assertEquals(map2, ImmutableMap. of("map", ImmutableMap.of("true", "value")));
+ assertEquals(json.toJson(map2), json.toJson(map));
+ }
+
public void testDeserializeEnum() {
assertEquals(json.fromJson("{enumValue : \"FOO\"}", EnumInside.class).enumValue, EnumInside.Test.FOO);
}
diff --git a/gogrid/src/main/java/org/jclouds/gogrid/config/GoGridParserModule.java b/gogrid/src/main/java/org/jclouds/gogrid/config/GoGridParserModule.java
index 547b3c7478..2fd53c3d03 100644
--- a/gogrid/src/main/java/org/jclouds/gogrid/config/GoGridParserModule.java
+++ b/gogrid/src/main/java/org/jclouds/gogrid/config/GoGridParserModule.java
@@ -19,8 +19,6 @@
package org.jclouds.gogrid.config;
-import static org.jclouds.Constants.PROPERTY_GSON_ADAPTERS;
-
import java.lang.reflect.Type;
import java.util.Map;
@@ -54,7 +52,6 @@ public class GoGridParserModule extends AbstractModule {
@Provides
@Singleton
- @com.google.inject.name.Named(PROPERTY_GSON_ADAPTERS)
public Map provideCustomAdapterBindings() {
Map bindings = Maps.newHashMap();
bindings.put(ObjectType.class, new CustomDeserializers.ObjectTypeAdapter());
diff --git a/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseCredentialsFromJsonResponseTest.java b/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseCredentialsFromJsonResponseTest.java
index 0ce6b8bd4a..8f66232c2e 100644
--- a/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseCredentialsFromJsonResponseTest.java
+++ b/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseCredentialsFromJsonResponseTest.java
@@ -28,7 +28,6 @@ import java.util.Map;
import javax.inject.Singleton;
-import org.jclouds.Constants;
import org.jclouds.domain.Credentials;
import org.jclouds.gogrid.config.DateSecondsAdapter;
import org.jclouds.gogrid.domain.IpState;
@@ -85,7 +84,6 @@ public class ParseCredentialsFromJsonResponseTest {
@Provides
@Singleton
@SuppressWarnings("unused")
- @com.google.inject.name.Named(Constants.PROPERTY_GSON_ADAPTERS)
public Map provideCustomAdapterBindings() {
Map bindings = Maps.newHashMap();
bindings.put(IpState.class, new CustomDeserializers.IpStateAdapter());
diff --git a/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseJobsFromJsonResponseTest.java b/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseJobsFromJsonResponseTest.java
index 5ee866dbeb..79750bb345 100644
--- a/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseJobsFromJsonResponseTest.java
+++ b/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseJobsFromJsonResponseTest.java
@@ -30,7 +30,6 @@ import java.util.SortedSet;
import javax.inject.Singleton;
-import org.jclouds.Constants;
import org.jclouds.gogrid.config.DateSecondsAdapter;
import org.jclouds.gogrid.domain.Job;
import org.jclouds.gogrid.domain.JobProperties;
@@ -88,7 +87,6 @@ public class ParseJobsFromJsonResponseTest {
@SuppressWarnings("unused")
@Provides
@Singleton
- @com.google.inject.name.Named(Constants.PROPERTY_GSON_ADAPTERS)
public Map provideCustomAdapterBindings() {
Map bindings = Maps.newHashMap();
bindings.put(ObjectType.class, new CustomDeserializers.ObjectTypeAdapter());
diff --git a/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseLoadBalancersFromJsonResponseTest.java b/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseLoadBalancersFromJsonResponseTest.java
index 3c3950de2a..c75b1c745b 100644
--- a/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseLoadBalancersFromJsonResponseTest.java
+++ b/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseLoadBalancersFromJsonResponseTest.java
@@ -29,7 +29,6 @@ import java.util.SortedSet;
import javax.inject.Singleton;
-import org.jclouds.Constants;
import org.jclouds.gogrid.config.DateSecondsAdapter;
import org.jclouds.gogrid.domain.Ip;
import org.jclouds.gogrid.domain.IpPortPair;
@@ -87,7 +86,6 @@ public class ParseLoadBalancersFromJsonResponseTest {
@Provides
@Singleton
@SuppressWarnings( { "unused" })
- @com.google.inject.name.Named(Constants.PROPERTY_GSON_ADAPTERS)
public Map provideCustomAdapterBindings() {
Map bindings = Maps.newHashMap();
bindings.put(LoadBalancerOs.class, new CustomDeserializers.LoadBalancerOsAdapter());
diff --git a/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseServerNameToCredentialsMapFromJsonResponseTest.java b/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseServerNameToCredentialsMapFromJsonResponseTest.java
index d184a5c42a..c208d4480e 100644
--- a/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseServerNameToCredentialsMapFromJsonResponseTest.java
+++ b/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseServerNameToCredentialsMapFromJsonResponseTest.java
@@ -28,7 +28,6 @@ import java.util.Map;
import javax.inject.Singleton;
-import org.jclouds.Constants;
import org.jclouds.domain.Credentials;
import org.jclouds.gogrid.config.DateSecondsAdapter;
import org.jclouds.gogrid.domain.IpState;
@@ -73,7 +72,6 @@ public class ParseServerNameToCredentialsMapFromJsonResponseTest {
@Provides
@Singleton
@SuppressWarnings("unused")
- @com.google.inject.name.Named(Constants.PROPERTY_GSON_ADAPTERS)
public Map provideCustomAdapterBindings() {
Map bindings = Maps.newHashMap();
bindings.put(IpState.class, new CustomDeserializers.IpStateAdapter());
diff --git a/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseServersFromJsonResponseTest.java b/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseServersFromJsonResponseTest.java
index 15c2666419..eb574604a3 100644
--- a/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseServersFromJsonResponseTest.java
+++ b/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseServersFromJsonResponseTest.java
@@ -29,7 +29,6 @@ import java.util.SortedSet;
import javax.inject.Singleton;
-import org.jclouds.Constants;
import org.jclouds.gogrid.config.GoGridParserModule;
import org.jclouds.gogrid.domain.BillingToken;
import org.jclouds.gogrid.domain.Customer;
@@ -94,7 +93,6 @@ public class ParseServersFromJsonResponseTest {
@Provides
@Singleton
@SuppressWarnings( { "unused", "unchecked" })
- @com.google.inject.name.Named(Constants.PROPERTY_GSON_ADAPTERS)
public Map provideCustomAdapterBindings() {
Map bindings = Maps.newHashMap();
bindings.put(IpState.class, new CustomDeserializers.IpStateAdapter());