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>
|
<dependency>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.google.code.gson</groupId>
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>gson</artifactId>
|
||||||
<version>1.5</version>
|
<version>1.6</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<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.
|
* 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";
|
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.
|
* String property.
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
package org.jclouds.json.config;
|
package org.jclouds.json.config;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -28,18 +27,18 @@ import java.util.Map;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
|
||||||
import org.jclouds.crypto.CryptoStreams;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
import org.jclouds.domain.JsonBall;
|
import org.jclouds.domain.JsonBall;
|
||||||
import org.jclouds.json.Json;
|
import org.jclouds.json.Json;
|
||||||
|
import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue;
|
||||||
import org.jclouds.json.internal.GsonWrapper;
|
import org.jclouds.json.internal.GsonWrapper;
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JcloudsCompactFormatter;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JcloudsGsonBuilder;
|
import com.google.gson.JcloudsGsonPackageAccessor;
|
||||||
import com.google.gson.JsonDeserializationContext;
|
import com.google.gson.JsonDeserializationContext;
|
||||||
import com.google.gson.JsonDeserializer;
|
import com.google.gson.JsonDeserializer;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
|
@ -48,11 +47,11 @@ import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.JsonPrimitive;
|
import com.google.gson.JsonPrimitive;
|
||||||
import com.google.gson.JsonSerializationContext;
|
import com.google.gson.JsonSerializationContext;
|
||||||
import com.google.gson.JsonSerializer;
|
import com.google.gson.JsonSerializer;
|
||||||
|
import com.google.gson.MapTypeAdapter;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.ImplementedBy;
|
import com.google.inject.ImplementedBy;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import com.google.inject.name.Named;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains logic for parsing objects from Strings.
|
* Contains logic for parsing objects from Strings.
|
||||||
|
@ -61,13 +60,16 @@ import com.google.inject.name.Named;
|
||||||
*/
|
*/
|
||||||
public class GsonModule extends AbstractModule {
|
public class GsonModule extends AbstractModule {
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
Gson provideGson(JsonBallAdapter jsonObjectAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
|
Gson provideGson(JsonBallAdapter jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
|
||||||
ByteArrayAdapter byteArrayAdapter, JsonAdapterBindings bindings) throws ClassNotFoundException, Exception {
|
ByteArrayAdapter byteArrayAdapter, JsonAdapterBindings bindings) throws ClassNotFoundException, Exception {
|
||||||
JcloudsGsonBuilder builder = new JcloudsGsonBuilder();
|
GsonBuilder builder = new GsonBuilder();
|
||||||
|
JcloudsGsonPackageAccessor.registerTypeHierarchyAdapter(builder, Enum.class,
|
||||||
builder.registerTypeAdapter(JsonBall.class, jsonObjectAdapter);
|
new EnumTypeAdapterThatReturnsFromValue());
|
||||||
|
JcloudsGsonPackageAccessor.registerTypeHierarchyAdapter(builder, Map.class, new MapTypeAdapter());
|
||||||
|
builder.registerTypeAdapter(JsonBall.class, jsonAdapter);
|
||||||
builder.registerTypeAdapter(Date.class, adapter);
|
builder.registerTypeAdapter(Date.class, adapter);
|
||||||
builder.registerTypeAdapter(new TypeToken<List<Byte>>() {
|
builder.registerTypeAdapter(new TypeToken<List<Byte>>() {
|
||||||
}.getType(), byteListAdapter);
|
}.getType(), byteListAdapter);
|
||||||
|
@ -75,14 +77,26 @@ public class GsonModule extends AbstractModule {
|
||||||
for (Map.Entry<Type, Object> binding : bindings.getBindings().entrySet()) {
|
for (Map.Entry<Type, Object> binding : bindings.getBindings().entrySet()) {
|
||||||
builder.registerTypeAdapter(binding.getKey(), binding.getValue());
|
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)
|
@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
|
@Singleton
|
||||||
public static class LongDateAdapter implements DateAdapter {
|
public static class LongDateAdapter implements DateAdapter {
|
||||||
|
|
||||||
|
@ -219,7 +214,7 @@ public class GsonModule extends AbstractModule {
|
||||||
private final Map<Type, Object> bindings = Maps.newHashMap();
|
private final Map<Type, Object> bindings = Maps.newHashMap();
|
||||||
|
|
||||||
@com.google.inject.Inject(optional = true)
|
@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);
|
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}"));
|
Map<String, JsonBall> map = ImmutableMap.<String, JsonBall> of("tomcat6", new JsonBall("{\"ssl_port\":8433}"));
|
||||||
|
|
||||||
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads
|
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
|
||||||
.newStringPayload(json))), map);
|
|
||||||
assertEquals(mapper.toJson(map), json);
|
assertEquals(mapper.toJson(map), json);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void testList() {
|
public void testList() {
|
||||||
String json = "{\"list\":[8431,8433]}";
|
String json = "{\"list\":[8431,8433]}";
|
||||||
|
|
||||||
Map<String, JsonBall> map = ImmutableMap.<String, JsonBall> of("list", new JsonBall("[8431,8433]"));
|
Map<String, JsonBall> map = ImmutableMap.<String, JsonBall> of("list", new JsonBall("[8431,8433]"));
|
||||||
|
|
||||||
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads
|
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
|
||||||
.newStringPayload(json))), map);
|
|
||||||
assertEquals(mapper.toJson(map), json);
|
assertEquals(mapper.toJson(map), json);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -83,20 +80,17 @@ public class JsonBallTest {
|
||||||
|
|
||||||
Map<String, JsonBall> map = ImmutableMap.<String, JsonBall> of("name", new JsonBall("fooy"));
|
Map<String, JsonBall> map = ImmutableMap.<String, JsonBall> of("name", new JsonBall("fooy"));
|
||||||
|
|
||||||
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads
|
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
|
||||||
.newStringPayload(json))), map);
|
|
||||||
assertEquals(mapper.toJson(map), json);
|
assertEquals(mapper.toJson(map), json);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void testNumber() {
|
public void testNumber() {
|
||||||
String json = "{\"number\":1}";
|
String json = "{\"number\":1}";
|
||||||
|
|
||||||
Map<String, JsonBall> map = ImmutableMap.<String, JsonBall> of("number", new JsonBall("1"));
|
Map<String, JsonBall> map = ImmutableMap.<String, JsonBall> of("number", new JsonBall("1"));
|
||||||
|
|
||||||
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads
|
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
|
||||||
.newStringPayload(json))), map);
|
|
||||||
assertEquals(mapper.toJson(map), json);
|
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 static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jclouds.json.config.GsonModule;
|
import org.jclouds.json.config.GsonModule;
|
||||||
import org.testng.annotations.Test;
|
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.gson.JsonParseException;
|
||||||
import com.google.inject.Guice;
|
import com.google.inject.Guice;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public class JsonTest {
|
public class JsonTest {
|
||||||
|
@ -39,6 +45,41 @@ public class JsonTest {
|
||||||
private Test enumValue;
|
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() {
|
public void testDeserializeEnum() {
|
||||||
assertEquals(json.fromJson("{enumValue : \"FOO\"}", EnumInside.class).enumValue, EnumInside.Test.FOO);
|
assertEquals(json.fromJson("{enumValue : \"FOO\"}", EnumInside.class).enumValue, EnumInside.Test.FOO);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
|
|
||||||
package org.jclouds.gogrid.config;
|
package org.jclouds.gogrid.config;
|
||||||
|
|
||||||
import static org.jclouds.Constants.PROPERTY_GSON_ADAPTERS;
|
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -54,7 +52,6 @@ public class GoGridParserModule extends AbstractModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@com.google.inject.name.Named(PROPERTY_GSON_ADAPTERS)
|
|
||||||
public Map<Type, Object> provideCustomAdapterBindings() {
|
public Map<Type, Object> provideCustomAdapterBindings() {
|
||||||
Map<Type, Object> bindings = Maps.newHashMap();
|
Map<Type, Object> bindings = Maps.newHashMap();
|
||||||
bindings.put(ObjectType.class, new CustomDeserializers.ObjectTypeAdapter());
|
bindings.put(ObjectType.class, new CustomDeserializers.ObjectTypeAdapter());
|
||||||
|
|
|
@ -28,7 +28,6 @@ import java.util.Map;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
|
||||||
import org.jclouds.domain.Credentials;
|
import org.jclouds.domain.Credentials;
|
||||||
import org.jclouds.gogrid.config.DateSecondsAdapter;
|
import org.jclouds.gogrid.config.DateSecondsAdapter;
|
||||||
import org.jclouds.gogrid.domain.IpState;
|
import org.jclouds.gogrid.domain.IpState;
|
||||||
|
@ -85,7 +84,6 @@ public class ParseCredentialsFromJsonResponseTest {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@com.google.inject.name.Named(Constants.PROPERTY_GSON_ADAPTERS)
|
|
||||||
public Map<Type, Object> provideCustomAdapterBindings() {
|
public Map<Type, Object> provideCustomAdapterBindings() {
|
||||||
Map<Type, Object> bindings = Maps.newHashMap();
|
Map<Type, Object> bindings = Maps.newHashMap();
|
||||||
bindings.put(IpState.class, new CustomDeserializers.IpStateAdapter());
|
bindings.put(IpState.class, new CustomDeserializers.IpStateAdapter());
|
||||||
|
|
|
@ -30,7 +30,6 @@ import java.util.SortedSet;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
|
||||||
import org.jclouds.gogrid.config.DateSecondsAdapter;
|
import org.jclouds.gogrid.config.DateSecondsAdapter;
|
||||||
import org.jclouds.gogrid.domain.Job;
|
import org.jclouds.gogrid.domain.Job;
|
||||||
import org.jclouds.gogrid.domain.JobProperties;
|
import org.jclouds.gogrid.domain.JobProperties;
|
||||||
|
@ -88,7 +87,6 @@ public class ParseJobsFromJsonResponseTest {
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@com.google.inject.name.Named(Constants.PROPERTY_GSON_ADAPTERS)
|
|
||||||
public Map<Type, Object> provideCustomAdapterBindings() {
|
public Map<Type, Object> provideCustomAdapterBindings() {
|
||||||
Map<Type, Object> bindings = Maps.newHashMap();
|
Map<Type, Object> bindings = Maps.newHashMap();
|
||||||
bindings.put(ObjectType.class, new CustomDeserializers.ObjectTypeAdapter());
|
bindings.put(ObjectType.class, new CustomDeserializers.ObjectTypeAdapter());
|
||||||
|
|
|
@ -29,7 +29,6 @@ import java.util.SortedSet;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
|
||||||
import org.jclouds.gogrid.config.DateSecondsAdapter;
|
import org.jclouds.gogrid.config.DateSecondsAdapter;
|
||||||
import org.jclouds.gogrid.domain.Ip;
|
import org.jclouds.gogrid.domain.Ip;
|
||||||
import org.jclouds.gogrid.domain.IpPortPair;
|
import org.jclouds.gogrid.domain.IpPortPair;
|
||||||
|
@ -87,7 +86,6 @@ public class ParseLoadBalancersFromJsonResponseTest {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@SuppressWarnings( { "unused" })
|
@SuppressWarnings( { "unused" })
|
||||||
@com.google.inject.name.Named(Constants.PROPERTY_GSON_ADAPTERS)
|
|
||||||
public Map<Type, Object> provideCustomAdapterBindings() {
|
public Map<Type, Object> provideCustomAdapterBindings() {
|
||||||
Map<Type, Object> bindings = Maps.newHashMap();
|
Map<Type, Object> bindings = Maps.newHashMap();
|
||||||
bindings.put(LoadBalancerOs.class, new CustomDeserializers.LoadBalancerOsAdapter());
|
bindings.put(LoadBalancerOs.class, new CustomDeserializers.LoadBalancerOsAdapter());
|
||||||
|
|
|
@ -28,7 +28,6 @@ import java.util.Map;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
|
||||||
import org.jclouds.domain.Credentials;
|
import org.jclouds.domain.Credentials;
|
||||||
import org.jclouds.gogrid.config.DateSecondsAdapter;
|
import org.jclouds.gogrid.config.DateSecondsAdapter;
|
||||||
import org.jclouds.gogrid.domain.IpState;
|
import org.jclouds.gogrid.domain.IpState;
|
||||||
|
@ -73,7 +72,6 @@ public class ParseServerNameToCredentialsMapFromJsonResponseTest {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@com.google.inject.name.Named(Constants.PROPERTY_GSON_ADAPTERS)
|
|
||||||
public Map<Type, Object> provideCustomAdapterBindings() {
|
public Map<Type, Object> provideCustomAdapterBindings() {
|
||||||
Map<Type, Object> bindings = Maps.newHashMap();
|
Map<Type, Object> bindings = Maps.newHashMap();
|
||||||
bindings.put(IpState.class, new CustomDeserializers.IpStateAdapter());
|
bindings.put(IpState.class, new CustomDeserializers.IpStateAdapter());
|
||||||
|
|
|
@ -29,7 +29,6 @@ import java.util.SortedSet;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
|
||||||
import org.jclouds.gogrid.config.GoGridParserModule;
|
import org.jclouds.gogrid.config.GoGridParserModule;
|
||||||
import org.jclouds.gogrid.domain.BillingToken;
|
import org.jclouds.gogrid.domain.BillingToken;
|
||||||
import org.jclouds.gogrid.domain.Customer;
|
import org.jclouds.gogrid.domain.Customer;
|
||||||
|
@ -94,7 +93,6 @@ public class ParseServersFromJsonResponseTest {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@SuppressWarnings( { "unused", "unchecked" })
|
@SuppressWarnings( { "unused", "unchecked" })
|
||||||
@com.google.inject.name.Named(Constants.PROPERTY_GSON_ADAPTERS)
|
|
||||||
public Map<Class, Object> provideCustomAdapterBindings() {
|
public Map<Class, Object> provideCustomAdapterBindings() {
|
||||||
Map<Class, Object> bindings = Maps.newHashMap();
|
Map<Class, Object> bindings = Maps.newHashMap();
|
||||||
bindings.put(IpState.class, new CustomDeserializers.IpStateAdapter());
|
bindings.put(IpState.class, new CustomDeserializers.IpStateAdapter());
|
||||||
|
|
Loading…
Reference in New Issue