mirror of https://github.com/apache/jclouds.git
Issue 421: Updated to google gson 1.6, and patched their classes to support natural parsing of Map<String, Object>
This commit is contained in:
parent
bf325b1126
commit
84b76c0606
|
@ -101,7 +101,7 @@
|
|||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>1.5</version>
|
||||
<version>1.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
|
|
|
@ -1,182 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -1,928 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* 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.
|
||||
*
|
||||
* <h4>Note!</h4>
|
||||
* 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<JsonSerializer<?>> DEFAULT_SERIALIZERS = createDefaultSerializers();
|
||||
private static final ParameterizedTypeHandlerMap<JsonDeserializer<?>> DEFAULT_DESERIALIZERS = createDefaultDeserializers();
|
||||
private static final ParameterizedTypeHandlerMap<InstanceCreator<?>> DEFAULT_INSTANCE_CREATORS = createDefaultInstanceCreators();
|
||||
|
||||
private static ParameterizedTypeHandlerMap<JsonSerializer<?>> createDefaultSerializers() {
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> map = new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
|
||||
|
||||
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<JsonDeserializer<?>> createDefaultDeserializers() {
|
||||
ParameterizedTypeHandlerMap<JsonDeserializer<?>> map = new ParameterizedTypeHandlerMap<JsonDeserializer<?>>();
|
||||
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<InstanceCreator<?>> createDefaultInstanceCreators() {
|
||||
ParameterizedTypeHandlerMap<InstanceCreator<?>> map = new ParameterizedTypeHandlerMap<InstanceCreator<?>>();
|
||||
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<JsonSerializer<?>> getDefaultSerializers() {
|
||||
return getDefaultSerializers(false, LongSerializationPolicy.DEFAULT);
|
||||
}
|
||||
|
||||
static ParameterizedTypeHandlerMap<JsonSerializer<?>> getDefaultSerializers(
|
||||
boolean serializeSpecialFloatingPointValues, LongSerializationPolicy longSerializationPolicy) {
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers = new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
|
||||
|
||||
// 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<JsonDeserializer<?>> getDefaultDeserializers() {
|
||||
return DEFAULT_DESERIALIZERS;
|
||||
}
|
||||
|
||||
static ParameterizedTypeHandlerMap<InstanceCreator<?>> getDefaultInstanceCreators() {
|
||||
return DEFAULT_INSTANCE_CREATORS;
|
||||
}
|
||||
|
||||
static class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
|
||||
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<java.sql.Date>, JsonDeserializer<java.sql.Date> {
|
||||
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<Timestamp> {
|
||||
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<Time>, JsonDeserializer<Time> {
|
||||
private final DateFormat format;
|
||||
|
||||
DefaultTimeTypeAdapter() {
|
||||
this.format = new SimpleDateFormat("hh:mm:ss a");
|
||||
}
|
||||
|
||||
public JsonElement serialize(Time src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
synchronized (format) {
|
||||
String dateFormatAsString = format.format(src);
|
||||
return new JsonPrimitive(dateFormatAsString);
|
||||
}
|
||||
}
|
||||
|
||||
public Time 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 Time(date.getTime());
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
throw new JsonParseException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class GregorianCalendarTypeAdapter implements JsonSerializer<GregorianCalendar>,
|
||||
JsonDeserializer<GregorianCalendar> {
|
||||
|
||||
private static final String YEAR = "year";
|
||||
private static final String MONTH = "month";
|
||||
private static final String DAY_OF_MONTH = "dayOfMonth";
|
||||
private static final String HOUR_OF_DAY = "hourOfDay";
|
||||
private static final String MINUTE = "minute";
|
||||
private static final String SECOND = "second";
|
||||
|
||||
public JsonElement serialize(GregorianCalendar src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty(YEAR, src.get(Calendar.YEAR));
|
||||
obj.addProperty(MONTH, src.get(Calendar.MONTH));
|
||||
obj.addProperty(DAY_OF_MONTH, src.get(Calendar.DAY_OF_MONTH));
|
||||
obj.addProperty(HOUR_OF_DAY, src.get(Calendar.HOUR_OF_DAY));
|
||||
obj.addProperty(MINUTE, src.get(Calendar.MINUTE));
|
||||
obj.addProperty(SECOND, src.get(Calendar.SECOND));
|
||||
return obj;
|
||||
}
|
||||
|
||||
public GregorianCalendar deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
JsonObject obj = json.getAsJsonObject();
|
||||
int year = obj.get(YEAR).getAsInt();
|
||||
int month = obj.get(MONTH).getAsInt();
|
||||
int dayOfMonth = obj.get(DAY_OF_MONTH).getAsInt();
|
||||
int hourOfDay = obj.get(HOUR_OF_DAY).getAsInt();
|
||||
int minute = obj.get(MINUTE).getAsInt();
|
||||
int second = obj.get(SECOND).getAsInt();
|
||||
return new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return GregorianCalendarTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static class EnumTypeAdapter<T extends Enum<T>> implements JsonSerializer<T>, JsonDeserializer<T> {
|
||||
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<T>) 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<Class<?>, Method> classToConvert = new MapMaker()
|
||||
.makeComputingMap(new Function<Class<?>, 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 EnumTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class UrlTypeAdapter implements JsonSerializer<URL>, JsonDeserializer<URL> {
|
||||
public JsonElement serialize(URL src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.toExternalForm());
|
||||
}
|
||||
|
||||
public URL deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
try {
|
||||
return new URL(json.getAsString());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new JsonParseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return UrlTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class UriTypeAdapter implements JsonSerializer<URI>, JsonDeserializer<URI> {
|
||||
public JsonElement serialize(URI src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.toASCIIString());
|
||||
}
|
||||
|
||||
public URI deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
try {
|
||||
return new URI(json.getAsString());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new JsonParseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return UriTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class UuidTypeAdapter implements JsonSerializer<UUID>, JsonDeserializer<UUID> {
|
||||
public JsonElement serialize(UUID src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.toString());
|
||||
}
|
||||
|
||||
public UUID deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return UUID.fromString(json.getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return UuidTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class LocaleTypeAdapter implements JsonSerializer<Locale>, JsonDeserializer<Locale> {
|
||||
public JsonElement serialize(Locale src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.toString());
|
||||
}
|
||||
|
||||
public Locale deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
String locale = json.getAsString();
|
||||
StringTokenizer tokenizer = new StringTokenizer(locale, "_");
|
||||
String language = null;
|
||||
String country = null;
|
||||
String variant = null;
|
||||
if (tokenizer.hasMoreElements()) {
|
||||
language = tokenizer.nextToken();
|
||||
}
|
||||
if (tokenizer.hasMoreElements()) {
|
||||
country = tokenizer.nextToken();
|
||||
}
|
||||
if (tokenizer.hasMoreElements()) {
|
||||
variant = tokenizer.nextToken();
|
||||
}
|
||||
if (country == null && variant == null) {
|
||||
return new Locale(language);
|
||||
} else if (variant == null) {
|
||||
return new Locale(language, country);
|
||||
} else {
|
||||
return new Locale(language, country, variant);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return LocaleTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private static class CollectionTypeAdapter implements JsonSerializer<Collection>, JsonDeserializer<Collection>,
|
||||
InstanceCreator<Collection> {
|
||||
public JsonElement serialize(Collection src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
if (src == null) {
|
||||
return JsonNull.createJsonNull();
|
||||
}
|
||||
JsonArray array = new JsonArray();
|
||||
Type childGenericType = null;
|
||||
if (typeOfSrc instanceof ParameterizedType) {
|
||||
childGenericType = new TypeInfoCollection(typeOfSrc).getElementType();
|
||||
}
|
||||
for (Object child : src) {
|
||||
if (child == null) {
|
||||
array.add(JsonNull.createJsonNull());
|
||||
} else {
|
||||
Type childType = (childGenericType == null || childGenericType == Object.class) ? child.getClass()
|
||||
: childGenericType;
|
||||
JsonElement element = context.serialize(child, childType);
|
||||
array.add(element);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public Collection deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
if (json.isJsonNull()) {
|
||||
return null;
|
||||
}
|
||||
// Use ObjectConstructor to create instance instead of hard-coding a specific type.
|
||||
// This handles cases where users are using their own subclass of Collection.
|
||||
Collection collection = constructCollectionType(typeOfT, context);
|
||||
Type childType = new TypeInfoCollection(typeOfT).getElementType();
|
||||
for (JsonElement childElement : json.getAsJsonArray()) {
|
||||
if (childElement == null || childElement.isJsonNull()) {
|
||||
collection.add(null);
|
||||
} else {
|
||||
Object value = context.deserialize(childElement, childType);
|
||||
collection.add(value);
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
private Collection constructCollectionType(Type collectionType, JsonDeserializationContext context) {
|
||||
JsonDeserializationContextDefault contextImpl = (JsonDeserializationContextDefault) context;
|
||||
ObjectConstructor objectConstructor = contextImpl.getObjectConstructor();
|
||||
return (Collection) objectConstructor.construct(collectionType);
|
||||
}
|
||||
|
||||
public Collection createInstance(Type type) {
|
||||
return new LinkedList();
|
||||
}
|
||||
}
|
||||
|
||||
private static class PropertiesCreator implements InstanceCreator<Properties> {
|
||||
public Properties createInstance(Type type) {
|
||||
return new Properties();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
static class MapTypeAdapter implements JsonSerializer<Map>, JsonDeserializer<Map>, InstanceCreator<Map> {
|
||||
|
||||
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<Map.Entry>) 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<Object, Object> map = constructMapType(typeOfT, context);
|
||||
TypeInfoMap mapTypeInfo = new TypeInfoMap(typeOfT);
|
||||
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
|
||||
Object key = context.deserialize(new JsonPrimitive(entry.getKey()), mapTypeInfo.getKeyType());
|
||||
Object value = context.deserialize(entry.getValue(), mapTypeInfo.getValueType());
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private static class BigDecimalTypeAdapter implements JsonSerializer<BigDecimal>, JsonDeserializer<BigDecimal> {
|
||||
public JsonElement serialize(BigDecimal src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
|
||||
public BigDecimal deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsBigDecimal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return BigDecimalTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class BigIntegerTypeAdapter implements JsonSerializer<BigInteger>, JsonDeserializer<BigInteger> {
|
||||
|
||||
public JsonElement serialize(BigInteger src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
|
||||
public BigInteger deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsBigInteger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return BigIntegerTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class NumberTypeAdapter implements JsonSerializer<Number>, JsonDeserializer<Number> {
|
||||
public JsonElement serialize(Number src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
|
||||
public Number deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return NumberTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class LongSerializer implements JsonSerializer<Long> {
|
||||
private final LongSerializationPolicy longSerializationPolicy;
|
||||
|
||||
private LongSerializer(LongSerializationPolicy longSerializationPolicy) {
|
||||
this.longSerializationPolicy = longSerializationPolicy;
|
||||
}
|
||||
|
||||
public JsonElement serialize(Long src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return longSerializationPolicy.serialize(src);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return LongSerializer.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class LongDeserializer implements JsonDeserializer<Long> {
|
||||
public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return LongDeserializer.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class IntegerTypeAdapter implements JsonSerializer<Integer>, JsonDeserializer<Integer> {
|
||||
public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
|
||||
public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return IntegerTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ShortTypeAdapter implements JsonSerializer<Short>, JsonDeserializer<Short> {
|
||||
public JsonElement serialize(Short src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
|
||||
public Short deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ShortTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ByteTypeAdapter implements JsonSerializer<Byte>, JsonDeserializer<Byte> {
|
||||
public JsonElement serialize(Byte src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
|
||||
public Byte deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ByteTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
static class FloatSerializer implements JsonSerializer<Float> {
|
||||
private final boolean serializeSpecialFloatingPointValues;
|
||||
|
||||
FloatSerializer(boolean serializeSpecialDoubleValues) {
|
||||
this.serializeSpecialFloatingPointValues = serializeSpecialDoubleValues;
|
||||
}
|
||||
|
||||
public JsonElement serialize(Float src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
if (!serializeSpecialFloatingPointValues) {
|
||||
if (Float.isNaN(src) || Float.isInfinite(src)) {
|
||||
throw new IllegalArgumentException(src
|
||||
+ " is not a valid float value as per JSON specification. To override this"
|
||||
+ " behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method.");
|
||||
}
|
||||
}
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FloatDeserializer implements JsonDeserializer<Float> {
|
||||
public Float deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return FloatDeserializer.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
static class DoubleSerializer implements JsonSerializer<Double> {
|
||||
private final boolean serializeSpecialFloatingPointValues;
|
||||
|
||||
DoubleSerializer(boolean serializeSpecialDoubleValues) {
|
||||
this.serializeSpecialFloatingPointValues = serializeSpecialDoubleValues;
|
||||
}
|
||||
|
||||
public JsonElement serialize(Double src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
if (!serializeSpecialFloatingPointValues) {
|
||||
if (Double.isNaN(src) || Double.isInfinite(src)) {
|
||||
throw new IllegalArgumentException(src
|
||||
+ " is not a valid double value as per JSON specification. To override this"
|
||||
+ " behavior, use GsonBuilder.serializeSpecialDoubleValues() method.");
|
||||
}
|
||||
}
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DoubleDeserializer implements JsonDeserializer<Double> {
|
||||
public Double deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DoubleDeserializer.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class CharacterTypeAdapter implements JsonSerializer<Character>, JsonDeserializer<Character> {
|
||||
public JsonElement serialize(Character src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
|
||||
public Character deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsCharacter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return CharacterTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class StringTypeAdapter implements JsonSerializer<String>, JsonDeserializer<String> {
|
||||
public JsonElement serialize(String src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
|
||||
public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class BooleanTypeAdapter implements JsonSerializer<Boolean>, JsonDeserializer<Boolean> {
|
||||
public JsonElement serialize(Boolean src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src);
|
||||
}
|
||||
|
||||
public Boolean deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return json.getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return BooleanTypeAdapter.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class TreeSetCreator implements InstanceCreator<TreeSet<?>> {
|
||||
public TreeSet<?> createInstance(Type type) {
|
||||
return new TreeSet<Object>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return TreeSetCreator.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class HashSetCreator implements InstanceCreator<HashSet<?>> {
|
||||
public HashSet<?> createInstance(Type type) {
|
||||
return new HashSet<Object>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return HashSetCreator.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,591 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* 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.Type;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.JcloudsDefaultTypeAdapters.DefaultDateTypeAdapter;
|
||||
|
||||
/**
|
||||
* <p>Use this builder to construct a {@link Gson} instance when you need to set configuration
|
||||
* options other than the default. For {@link Gson} with default configuration, it is simpler to
|
||||
* use {@code new Gson()}. {@code JcloudsGsonBuilder} is best used by creating it, and then invoking its
|
||||
* various configuration methods, and finally calling create.</p>
|
||||
*
|
||||
* <p>The following is an example shows how to use the {@code JcloudsGsonBuilder} to construct a Gson
|
||||
* instance:
|
||||
*
|
||||
* <pre>
|
||||
* Gson gson = new JcloudsGsonBuilder()
|
||||
* .registerTypeAdapter(Id.class, new IdTypeAdapter())
|
||||
* .serializeNulls()
|
||||
* .setDateFormat(DateFormat.LONG)
|
||||
* .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
|
||||
* .setPrettyPrinting()
|
||||
* .setVersion(1.0)
|
||||
* .create();
|
||||
* </pre></p>
|
||||
*
|
||||
* <p>NOTE: the order of invocation of configuration methods does not matter.</p>
|
||||
*
|
||||
* @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<ExclusionStrategy> exclusionStrategies =
|
||||
new HashSet<ExclusionStrategy>();
|
||||
|
||||
private double ignoreVersionsAfter;
|
||||
private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy;
|
||||
private boolean serializeInnerClasses;
|
||||
private boolean excludeFieldsWithoutExposeAnnotation;
|
||||
private LongSerializationPolicy longSerializationPolicy;
|
||||
private FieldNamingStrategy2 fieldNamingPolicy;
|
||||
private final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators;
|
||||
private final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers;
|
||||
private final ParameterizedTypeHandlerMap<JsonDeserializer<?>> 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<InstanceCreator<?>>();
|
||||
serializers = new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
|
||||
deserializers = new ParameterizedTypeHandlerMap<JsonDeserializer<?>>();
|
||||
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
|
||||
* <a href="http://code.google.com/p/google-gson/issues/detail?id=42">Gson Issue 42</a>
|
||||
* 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.
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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 <T> 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 <T> 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 <T> 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 <T> JcloudsGsonBuilder registerSerializer(Type typeOfT, final JsonSerializer<T> 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 <T> 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 <T> JcloudsGsonBuilder registerDeserializer(Type typeOfT, JsonDeserializer<T> deserializer) {
|
||||
deserializers.register(typeOfT, new JsonDeserializerExceptionWrapper<T>(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 <T> JcloudsGsonBuilder registerInstanceCreatorForTypeHierarchy(Class<?> classOfT,
|
||||
InstanceCreator<? extends T> instanceCreator) {
|
||||
instanceCreators.registerForTypeHierarchy(classOfT, instanceCreator);
|
||||
return this;
|
||||
}
|
||||
|
||||
private <T> JcloudsGsonBuilder registerSerializerForTypeHierarchy(Class<?> classOfT,
|
||||
final JsonSerializer<T> serializer) {
|
||||
serializers.registerForTypeHierarchy(classOfT, serializer);
|
||||
return this;
|
||||
}
|
||||
|
||||
private <T> JcloudsGsonBuilder registerDeserializerForTypeHierarchy(Class<?> classOfT,
|
||||
JsonDeserializer<T> deserializer) {
|
||||
deserializers.registerForTypeHierarchy(classOfT,
|
||||
new JsonDeserializerExceptionWrapper<T>(deserializer));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Section 2.4 of <a href="http://www.ietf.org/rfc/rfc4627.txt">JSON specification</a> disallows
|
||||
* special double values (NaN, Infinity, -Infinity). However,
|
||||
* <a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf">Javascript
|
||||
* specification</a> (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.
|
||||
*
|
||||
* <p>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<ExclusionStrategy> serializationStrategies =
|
||||
new LinkedList<ExclusionStrategy>(exclusionStrategies);
|
||||
List<ExclusionStrategy> deserializationStrategies =
|
||||
new LinkedList<ExclusionStrategy>(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<JsonSerializer<?>> customSerializers = serializers.copyOf();
|
||||
ParameterizedTypeHandlerMap<JsonDeserializer<?>> customDeserializers = deserializers.copyOf();
|
||||
addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, customSerializers,
|
||||
customDeserializers);
|
||||
|
||||
customSerializers.registerIfAbsent(JcloudsDefaultTypeAdapters.getDefaultSerializers(
|
||||
serializeSpecialFloatingPointValues, longSerializationPolicy));
|
||||
|
||||
customDeserializers.registerIfAbsent(JcloudsDefaultTypeAdapters.getDefaultDeserializers());
|
||||
|
||||
ParameterizedTypeHandlerMap<InstanceCreator<?>> 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<JsonSerializer<?>> serializers,
|
||||
ParameterizedTypeHandlerMap<JsonDeserializer<?>> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* 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 <a href="http://code.google.com/p/google-gson/issues/detail?id=271" >gson issue 271</a>
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class JcloudsGsonPackageAccessor {
|
||||
public static GsonBuilder registerTypeHierarchyAdapter(GsonBuilder builder, Class<?> baseType, Object typeAdapter) {
|
||||
return builder.registerTypeHierarchyAdapter(baseType, typeAdapter);
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* 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<String, JsonElement> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<Map>, JsonDeserializer<Map>, InstanceCreator<Map> {
|
||||
|
||||
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<Map.Entry>) 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<Object, Object> map = constructMapType(typeOfT, context);
|
||||
TypeInfoMap mapTypeInfo = new TypeInfoMap(typeOfT);
|
||||
for (Map.Entry<String, JsonElement> 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();
|
||||
}
|
||||
}
|
|
@ -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<String, JsonElement> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
|
||||
* 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.
|
||||
*
|
||||
* <h3>Encoding JSON</h3>
|
||||
* 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:
|
||||
* <ul>
|
||||
* <li>To write <strong>arrays</strong>, 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()}.
|
||||
* <li>To write <strong>objects</strong>, 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()}.
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Example</h3>
|
||||
* Suppose we'd like to encode a stream of messages such as the following: <pre> {@code
|
||||
* [
|
||||
* {
|
||||
* "id": 912345678901,
|
||||
* "text": "How do I stream JSON in Java?",
|
||||
* "geo": null,
|
||||
* "user": {
|
||||
* "name": "json_newb",
|
||||
* "followers_count": 41
|
||||
* }
|
||||
* },
|
||||
* {
|
||||
* "id": 912345678902,
|
||||
* "text": "@json_newb just use JsonWriter!",
|
||||
* "geo": [50.454722, -104.606667],
|
||||
* "user": {
|
||||
* "name": "jesse",
|
||||
* "followers_count": 2
|
||||
* }
|
||||
* }
|
||||
* ]}</pre>
|
||||
* This code encodes the above structure: <pre> {@code
|
||||
* public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException {
|
||||
* JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
|
||||
* writer.setIndentSpaces(4);
|
||||
* writeMessagesArray(writer, messages);
|
||||
* writer.close();
|
||||
* }
|
||||
*
|
||||
* public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException {
|
||||
* writer.beginArray();
|
||||
* for (Message message : messages) {
|
||||
* writeMessage(writer, message);
|
||||
* }
|
||||
* writer.endArray();
|
||||
* }
|
||||
*
|
||||
* public void writeMessage(JsonWriter writer, Message message) throws IOException {
|
||||
* writer.beginObject();
|
||||
* writer.name("id").value(message.getId());
|
||||
* writer.name("text").value(message.getText());
|
||||
* if (message.getGeo() != null) {
|
||||
* writer.name("geo");
|
||||
* writeDoublesArray(writer, message.getGeo());
|
||||
* } else {
|
||||
* writer.name("geo").nullValue();
|
||||
* }
|
||||
* writer.name("user");
|
||||
* writeUser(writer, message.getUser());
|
||||
* writer.endObject();
|
||||
* }
|
||||
*
|
||||
* public void writeUser(JsonWriter writer, User user) throws IOException {
|
||||
* writer.beginObject();
|
||||
* writer.name("name").value(user.getName());
|
||||
* writer.name("followers_count").value(user.getFollowersCount());
|
||||
* writer.endObject();
|
||||
* }
|
||||
*
|
||||
* public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
|
||||
* writer.beginArray();
|
||||
* for (Double value : doubles) {
|
||||
* writer.value(value);
|
||||
* }
|
||||
* writer.endArray();
|
||||
* }}</pre>
|
||||
*
|
||||
* <p>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<JsonScope> stack = new ArrayList<JsonScope>();
|
||||
{
|
||||
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 <a
|
||||
* href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the writer
|
||||
* to lenient permits the following:
|
||||
* <ul>
|
||||
* <li>Top-level values of any type. With strict writing, the top-level
|
||||
* value must be an object or an array.
|
||||
* <li>Numbers may be {@link Double#isNaN() NaNs} or {@link
|
||||
* Double#isInfinite() infinities}.
|
||||
* </ul>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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<List<Byte>>() {
|
||||
}.getType(), byteListAdapter);
|
||||
|
@ -75,14 +77,26 @@ public class GsonModule extends AbstractModule {
|
|||
for (Map.Entry<Type, Object> 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<JsonBall>, JsonDeserializer<JsonBall> {
|
||||
|
||||
}
|
||||
|
||||
@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<JsonBall>, JsonDeserializer<JsonBall> {
|
||||
|
||||
}
|
||||
|
||||
@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<Type, Object> bindings = Maps.newHashMap();
|
||||
|
||||
@com.google.inject.Inject(optional = true)
|
||||
public void setBindings(@Named(Constants.PROPERTY_GSON_ADAPTERS) Map<Type, Object> bindings) {
|
||||
public void setBindings(Map<Type, Object> bindings) {
|
||||
this.bindings.putAll(bindings);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* 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<T extends Enum<T>> implements JsonSerializer<T>, JsonDeserializer<T> {
|
||||
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<T>) 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<Class<?>, Method> classToConvert = new MapMaker()
|
||||
.makeComputingMap(new Function<Class<?>, 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* 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<JsonObject, Map<String, JsonElement>> {
|
||||
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<String, JsonElement> apply(JsonObject in) {
|
||||
try {
|
||||
return (Map<String, JsonElement>) 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* 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<JsonElement, Object> {
|
||||
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.<String,Object>newLinkedHashMap(Maps.transformValues(JsonObjectAsMap.INSTANCE.apply(input.getAsJsonObject()),
|
||||
this));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -60,20 +60,17 @@ public class JsonBallTest {
|
|||
|
||||
Map<String, JsonBall> map = ImmutableMap.<String, JsonBall> 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<String, JsonBall> map = ImmutableMap.<String, JsonBall> 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<String, JsonBall> map = ImmutableMap.<String, JsonBall> 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<String, JsonBall> map = ImmutableMap.<String, JsonBall> 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);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* 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<Map<String, Object>> handler;
|
||||
private Json mapper;
|
||||
|
||||
@BeforeTest
|
||||
protected void setUpInjector() throws IOException {
|
||||
Injector injector = Guice.createInjector(new GsonModule());
|
||||
handler = injector.getInstance(Key.get(new TypeLiteral<ParseJson<Map<String, Object>>>() {
|
||||
}));
|
||||
mapper = injector.getInstance(Json.class);
|
||||
|
||||
}
|
||||
|
||||
public void testHash() {
|
||||
String json = "{\"tomcat6\":{\"ssl_port\":8433}}";
|
||||
|
||||
Map<String, Object> map = ImmutableMap.<String, Object> 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<String, Object> map = ImmutableMap.<String, Object> 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<String, Object> map = ImmutableMap.<String, Object> 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<String, Object> map = ImmutableMap.<String, Object> of("number", 1);
|
||||
|
||||
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
|
||||
assertEquals(mapper.toJson(map), json);
|
||||
|
||||
}
|
||||
}
|
|
@ -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<String, Object> 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<String, Object> map2 = json.fromJson(json.toJson(map), new TypeLiteral<Map<String, Object>>() {
|
||||
}.getType());
|
||||
assertEquals(map2, map);
|
||||
assertEquals(json.toJson(map2), json.toJson(map));
|
||||
}
|
||||
|
||||
public void testMapStringObjectWithNumericalKeysConvertToStrings() {
|
||||
Map<String, Object> map = ImmutableMap.<String, Object> of("map", ImmutableMap.of(1, "value"));
|
||||
assertEquals(json.toJson(map), "{\"map\":{\"1\":\"value\"}}");
|
||||
Map<String, Object> map2 = json.fromJson(json.toJson(map), new TypeLiteral<Map<String, Object>>() {
|
||||
}.getType());
|
||||
// note conversion.. ensures valid
|
||||
assertEquals(map2, ImmutableMap.<String, Object> of("map", ImmutableMap.of("1", "value")));
|
||||
assertEquals(json.toJson(map2), json.toJson(map));
|
||||
}
|
||||
|
||||
public void testMapStringObjectWithBooleanKeysConvertToStrings() {
|
||||
Map<String, Object> map = ImmutableMap.<String, Object> of("map", ImmutableMap.of(true, "value"));
|
||||
assertEquals(json.toJson(map), "{\"map\":{\"true\":\"value\"}}");
|
||||
Map<String, Object> map2 = json.fromJson(json.toJson(map), new TypeLiteral<Map<String, Object>>() {
|
||||
}.getType());
|
||||
// note conversion.. ensures valid
|
||||
assertEquals(map2, ImmutableMap.<String, Object> 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);
|
||||
}
|
||||
|
|
|
@ -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<Type, Object> provideCustomAdapterBindings() {
|
||||
Map<Type, Object> bindings = Maps.newHashMap();
|
||||
bindings.put(ObjectType.class, new CustomDeserializers.ObjectTypeAdapter());
|
||||
|
|
|
@ -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<Type, Object> provideCustomAdapterBindings() {
|
||||
Map<Type, Object> bindings = Maps.newHashMap();
|
||||
bindings.put(IpState.class, new CustomDeserializers.IpStateAdapter());
|
||||
|
|
|
@ -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<Type, Object> provideCustomAdapterBindings() {
|
||||
Map<Type, Object> bindings = Maps.newHashMap();
|
||||
bindings.put(ObjectType.class, new CustomDeserializers.ObjectTypeAdapter());
|
||||
|
|
|
@ -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<Type, Object> provideCustomAdapterBindings() {
|
||||
Map<Type, Object> bindings = Maps.newHashMap();
|
||||
bindings.put(LoadBalancerOs.class, new CustomDeserializers.LoadBalancerOsAdapter());
|
||||
|
|
|
@ -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<Type, Object> provideCustomAdapterBindings() {
|
||||
Map<Type, Object> bindings = Maps.newHashMap();
|
||||
bindings.put(IpState.class, new CustomDeserializers.IpStateAdapter());
|
||||
|
|
|
@ -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<Class, Object> provideCustomAdapterBindings() {
|
||||
Map<Class, Object> bindings = Maps.newHashMap();
|
||||
bindings.put(IpState.class, new CustomDeserializers.IpStateAdapter());
|
||||
|
|
Loading…
Reference in New Issue