Permit users to define type adapters for primitive types and strings.
Also expose an API to get the field naming strategy.
This commit is contained in:
parent
c3ada66749
commit
dc4e43bb23
@ -124,6 +124,7 @@ public final class Gson {
|
|||||||
private final boolean htmlSafe;
|
private final boolean htmlSafe;
|
||||||
private final boolean generateNonExecutableJson;
|
private final boolean generateNonExecutableJson;
|
||||||
private final boolean prettyPrinting;
|
private final boolean prettyPrinting;
|
||||||
|
private final FieldNamingStrategy fieldNamingPolicy;
|
||||||
|
|
||||||
final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() {
|
final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -193,10 +194,18 @@ public final class Gson {
|
|||||||
this.generateNonExecutableJson = generateNonExecutableGson;
|
this.generateNonExecutableJson = generateNonExecutableGson;
|
||||||
this.htmlSafe = htmlSafe;
|
this.htmlSafe = htmlSafe;
|
||||||
this.prettyPrinting = prettyPrinting;
|
this.prettyPrinting = prettyPrinting;
|
||||||
|
this.fieldNamingPolicy = fieldNamingPolicy;
|
||||||
|
|
||||||
List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
|
List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
|
||||||
|
|
||||||
// built-in type adapters that cannot be overridden
|
// built-in type adapters that cannot be overridden
|
||||||
|
factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
|
||||||
|
factories.add(ObjectTypeAdapter.FACTORY);
|
||||||
|
|
||||||
|
// user's type adapters
|
||||||
|
factories.addAll(typeAdapterFactories);
|
||||||
|
|
||||||
|
// type adapters for basic platform types
|
||||||
factories.add(TypeAdapters.STRING_FACTORY);
|
factories.add(TypeAdapters.STRING_FACTORY);
|
||||||
factories.add(TypeAdapters.INTEGER_FACTORY);
|
factories.add(TypeAdapters.INTEGER_FACTORY);
|
||||||
factories.add(TypeAdapters.BOOLEAN_FACTORY);
|
factories.add(TypeAdapters.BOOLEAN_FACTORY);
|
||||||
@ -208,21 +217,12 @@ public final class Gson {
|
|||||||
doubleAdapter(serializeSpecialFloatingPointValues)));
|
doubleAdapter(serializeSpecialFloatingPointValues)));
|
||||||
factories.add(TypeAdapters.newFactory(float.class, Float.class,
|
factories.add(TypeAdapters.newFactory(float.class, Float.class,
|
||||||
floatAdapter(serializeSpecialFloatingPointValues)));
|
floatAdapter(serializeSpecialFloatingPointValues)));
|
||||||
factories.add(excluder);
|
|
||||||
factories.add(TypeAdapters.NUMBER_FACTORY);
|
factories.add(TypeAdapters.NUMBER_FACTORY);
|
||||||
factories.add(TypeAdapters.CHARACTER_FACTORY);
|
factories.add(TypeAdapters.CHARACTER_FACTORY);
|
||||||
factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
|
factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
|
||||||
factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
|
factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
|
||||||
factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
|
|
||||||
factories.add(ObjectTypeAdapter.FACTORY);
|
|
||||||
|
|
||||||
// user's type adapters
|
|
||||||
factories.addAll(typeAdapterFactories);
|
|
||||||
|
|
||||||
// built-in type adapters that can be overridden
|
|
||||||
factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
|
factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
|
||||||
factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
|
factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
|
||||||
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
|
|
||||||
factories.add(TypeAdapters.URL_FACTORY);
|
factories.add(TypeAdapters.URL_FACTORY);
|
||||||
factories.add(TypeAdapters.URI_FACTORY);
|
factories.add(TypeAdapters.URI_FACTORY);
|
||||||
factories.add(TypeAdapters.UUID_FACTORY);
|
factories.add(TypeAdapters.UUID_FACTORY);
|
||||||
@ -234,16 +234,30 @@ public final class Gson {
|
|||||||
factories.add(TimeTypeAdapter.FACTORY);
|
factories.add(TimeTypeAdapter.FACTORY);
|
||||||
factories.add(SqlDateTypeAdapter.FACTORY);
|
factories.add(SqlDateTypeAdapter.FACTORY);
|
||||||
factories.add(TypeAdapters.TIMESTAMP_FACTORY);
|
factories.add(TypeAdapters.TIMESTAMP_FACTORY);
|
||||||
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
|
|
||||||
factories.add(ArrayTypeAdapter.FACTORY);
|
factories.add(ArrayTypeAdapter.FACTORY);
|
||||||
factories.add(TypeAdapters.ENUM_FACTORY);
|
factories.add(TypeAdapters.ENUM_FACTORY);
|
||||||
factories.add(TypeAdapters.CLASS_FACTORY);
|
factories.add(TypeAdapters.CLASS_FACTORY);
|
||||||
|
|
||||||
|
// the excluder must precede all adapters that handle user-defined types
|
||||||
|
factories.add(excluder);
|
||||||
|
|
||||||
|
// type adapters for composite and user-defined types
|
||||||
|
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
|
||||||
|
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
|
||||||
factories.add(new ReflectiveTypeAdapterFactory(
|
factories.add(new ReflectiveTypeAdapterFactory(
|
||||||
constructorConstructor, fieldNamingPolicy, excluder));
|
constructorConstructor, fieldNamingPolicy, excluder));
|
||||||
|
|
||||||
this.factories = Collections.unmodifiableList(factories);
|
this.factories = Collections.unmodifiableList(factories);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the field naming policy used to translate Java field names to JSON
|
||||||
|
* property names.
|
||||||
|
*/
|
||||||
|
public FieldNamingStrategy getFieldNamingPolicy() {
|
||||||
|
return fieldNamingPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
private TypeAdapter<Number> doubleAdapter(boolean serializeSpecialFloatingPointValues) {
|
private TypeAdapter<Number> doubleAdapter(boolean serializeSpecialFloatingPointValues) {
|
||||||
if (serializeSpecialFloatingPointValues) {
|
if (serializeSpecialFloatingPointValues) {
|
||||||
return TypeAdapters.DOUBLE;
|
return TypeAdapters.DOUBLE;
|
||||||
|
@ -28,7 +28,6 @@ import java.util.Map;
|
|||||||
|
|
||||||
import com.google.gson.internal.$Gson$Preconditions;
|
import com.google.gson.internal.$Gson$Preconditions;
|
||||||
import com.google.gson.internal.Excluder;
|
import com.google.gson.internal.Excluder;
|
||||||
import com.google.gson.internal.Primitives;
|
|
||||||
import com.google.gson.internal.bind.TypeAdapters;
|
import com.google.gson.internal.bind.TypeAdapters;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
@ -435,6 +434,10 @@ public final class GsonBuilder {
|
|||||||
* all the required interfaces for custom serialization with Gson. If a type adapter was
|
* all the required interfaces for custom serialization with Gson. If a type adapter was
|
||||||
* previously registered for the specified {@code type}, it is overwritten.
|
* previously registered for the specified {@code type}, it is overwritten.
|
||||||
*
|
*
|
||||||
|
* <p>This registers the type specified and no other types: you must manually register related
|
||||||
|
* types! For example, applications registering {@code boolean.class} should also register {@code
|
||||||
|
* Boolean.class}.
|
||||||
|
*
|
||||||
* @param type the type definition for the type adapter being registered
|
* @param type the type definition for the type adapter being registered
|
||||||
* @param typeAdapter This object must implement at least one of the {@link TypeAdapter},
|
* @param typeAdapter This object must implement at least one of the {@link TypeAdapter},
|
||||||
* {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
|
* {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
|
||||||
@ -446,10 +449,6 @@ public final class GsonBuilder {
|
|||||||
|| typeAdapter instanceof JsonDeserializer<?>
|
|| typeAdapter instanceof JsonDeserializer<?>
|
||||||
|| typeAdapter instanceof InstanceCreator<?>
|
|| typeAdapter instanceof InstanceCreator<?>
|
||||||
|| typeAdapter instanceof TypeAdapter<?>);
|
|| typeAdapter instanceof TypeAdapter<?>);
|
||||||
if (Primitives.isPrimitive(type) || Primitives.isWrapperType(type) || type == String.class) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Cannot register type adapters for " + type);
|
|
||||||
}
|
|
||||||
if (typeAdapter instanceof InstanceCreator<?>) {
|
if (typeAdapter instanceof InstanceCreator<?>) {
|
||||||
instanceCreators.put(type, (InstanceCreator) typeAdapter);
|
instanceCreators.put(type, (InstanceCreator) typeAdapter);
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ public class GsonBuilderTest extends TestCase {
|
|||||||
assertEquals("{\"d\":\"d\"}", gson.toJson(new HasModifiers()));
|
assertEquals("{\"d\":\"d\"}", gson.toJson(new HasModifiers()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRegisterTypeAdapterForUnsupportedType() {
|
public void testRegisterTypeAdapterForCoreType() {
|
||||||
Type[] types = {
|
Type[] types = {
|
||||||
byte.class,
|
byte.class,
|
||||||
int.class,
|
int.class,
|
||||||
@ -62,11 +62,7 @@ public class GsonBuilderTest extends TestCase {
|
|||||||
String.class,
|
String.class,
|
||||||
};
|
};
|
||||||
for (Type type : types) {
|
for (Type type : types) {
|
||||||
try {
|
new GsonBuilder().registerTypeAdapter(type, NULL_TYPE_ADAPTER);
|
||||||
new GsonBuilder().registerTypeAdapter(type, NULL_TYPE_ADAPTER);
|
|
||||||
fail(type.toString());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 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 java.io.IOException;
|
||||||
|
import java.util.Locale;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jesse Wilson
|
||||||
|
*/
|
||||||
|
public class OverrideCoreTypeAdaptersTest extends TestCase {
|
||||||
|
private static final TypeAdapter<Boolean> booleanAsIntAdapter = new TypeAdapter<Boolean>() {
|
||||||
|
@Override public void write(JsonWriter out, Boolean value) throws IOException {
|
||||||
|
out.value(value ? 1 : 0);
|
||||||
|
}
|
||||||
|
@Override public Boolean read(JsonReader in) throws IOException {
|
||||||
|
int value = in.nextInt();
|
||||||
|
return value != 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final TypeAdapter<String> swapCaseStringAdapter = new TypeAdapter<String>() {
|
||||||
|
@Override public void write(JsonWriter out, String value) throws IOException {
|
||||||
|
out.value(value.toUpperCase(Locale.US));
|
||||||
|
}
|
||||||
|
@Override public String read(JsonReader in) throws IOException {
|
||||||
|
return in.nextString().toLowerCase(Locale.US);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public void testOverrideWrapperBooleanAdapter() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Boolean.class, booleanAsIntAdapter)
|
||||||
|
.create();
|
||||||
|
assertEquals("true", gson.toJson(true, boolean.class));
|
||||||
|
assertEquals("1", gson.toJson(true, Boolean.class));
|
||||||
|
assertEquals(Boolean.TRUE, gson.fromJson("true", boolean.class));
|
||||||
|
assertEquals(Boolean.TRUE, gson.fromJson("1", Boolean.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testOverridePrimitiveBooleanAdapter() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(boolean.class, booleanAsIntAdapter)
|
||||||
|
.create();
|
||||||
|
assertEquals("1", gson.toJson(true, boolean.class));
|
||||||
|
assertEquals("true", gson.toJson(true, Boolean.class));
|
||||||
|
assertEquals(Boolean.TRUE, gson.fromJson("1", boolean.class));
|
||||||
|
assertEquals(Boolean.TRUE, gson.fromJson("true", Boolean.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testOverrideStringAdapter() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(String.class, swapCaseStringAdapter)
|
||||||
|
.create();
|
||||||
|
assertEquals("\"HELLO\"", gson.toJson("Hello", String.class));
|
||||||
|
assertEquals("hello", gson.fromJson("\"Hello\"", String.class));
|
||||||
|
}
|
||||||
|
}
|
@ -204,28 +204,28 @@ public class CustomTypeAdaptersTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCustomSerializerForbiddenForPrimitives() {
|
public void testCustomSerializerInvokedForPrimitives() {
|
||||||
try {
|
Gson gson = new GsonBuilder()
|
||||||
new GsonBuilder().registerTypeAdapter(long.class, new JsonSerializer<Long>() {
|
.registerTypeAdapter(boolean.class, new JsonSerializer<Boolean>() {
|
||||||
public JsonElement serialize(Long s, Type t, JsonSerializationContext c) {
|
public JsonElement serialize(Boolean s, Type t, JsonSerializationContext c) {
|
||||||
throw new AssertionError();
|
return new JsonPrimitive(s ? 1 : 0);
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
fail();
|
.create();
|
||||||
} catch (IllegalArgumentException expected) {
|
assertEquals("1", gson.toJson(true, boolean.class));
|
||||||
}
|
assertEquals("true", gson.toJson(true, Boolean.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCustomDeserializerForbiddenForPrimitives() {
|
public void testCustomDeserializerInvokedForPrimitives() {
|
||||||
try {
|
Gson gson = new GsonBuilder()
|
||||||
new GsonBuilder().registerTypeAdapter(long.class, new JsonDeserializer<Long>() {
|
.registerTypeAdapter(boolean.class, new JsonDeserializer() {
|
||||||
public Long deserialize(JsonElement json, Type t, JsonDeserializationContext c) {
|
public Object deserialize(JsonElement json, Type t, JsonDeserializationContext context) {
|
||||||
throw new AssertionError();
|
return json.getAsInt() != 0;
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
fail();
|
.create();
|
||||||
} catch (Exception expected) {
|
assertEquals(Boolean.TRUE, gson.fromJson("1", boolean.class));
|
||||||
}
|
assertEquals(Boolean.TRUE, gson.fromJson("true", Boolean.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCustomByteArraySerializer() {
|
public void testCustomByteArraySerializer() {
|
||||||
|
@ -54,19 +54,18 @@ public class DelegateTypeAdapterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
String json = gson.toJson(bags);
|
String json = gson.toJson(bags);
|
||||||
bags = gson.fromJson(json, new TypeToken<List<BagOfPrimitives>>(){}.getType());
|
bags = gson.fromJson(json, new TypeToken<List<BagOfPrimitives>>(){}.getType());
|
||||||
// 11: 1 list object, and 10 entries. stats not invoked on individual fields of
|
// 11: 1 list object, and 10 entries. stats invoked on all 5 fields
|
||||||
// BagOfPrimitives since those are primitives.
|
assertEquals(51, stats.numReads);
|
||||||
assertEquals(11, stats.numReads);
|
assertEquals(51, stats.numWrites);
|
||||||
assertEquals(11, stats.numWrites);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDelegateNotInvokedOnStrings() {
|
public void testDelegateInvokedOnStrings() {
|
||||||
String[] bags = {"1", "2", "3", "4"};
|
String[] bags = {"1", "2", "3", "4"};
|
||||||
String json = gson.toJson(bags);
|
String json = gson.toJson(bags);
|
||||||
bags = gson.fromJson(json, String[].class);
|
bags = gson.fromJson(json, String[].class);
|
||||||
// Only 1 array object. stats not invoked on individual strings.
|
// 1 array object with 4 elements.
|
||||||
assertEquals(1, stats.numReads);
|
assertEquals(5, stats.numReads);
|
||||||
assertEquals(1, stats.numWrites);
|
assertEquals(5, stats.numWrites);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class StatsTypeAdapterFactory implements TypeAdapterFactory {
|
private static class StatsTypeAdapterFactory implements TypeAdapterFactory {
|
||||||
|
Loading…
Reference in New Issue
Block a user