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 generateNonExecutableJson;
|
||||
private final boolean prettyPrinting;
|
||||
private final FieldNamingStrategy fieldNamingPolicy;
|
||||
|
||||
final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -193,10 +194,18 @@ public final class Gson {
|
||||
this.generateNonExecutableJson = generateNonExecutableGson;
|
||||
this.htmlSafe = htmlSafe;
|
||||
this.prettyPrinting = prettyPrinting;
|
||||
this.fieldNamingPolicy = fieldNamingPolicy;
|
||||
|
||||
List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
|
||||
|
||||
// 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.INTEGER_FACTORY);
|
||||
factories.add(TypeAdapters.BOOLEAN_FACTORY);
|
||||
@ -208,21 +217,12 @@ public final class Gson {
|
||||
doubleAdapter(serializeSpecialFloatingPointValues)));
|
||||
factories.add(TypeAdapters.newFactory(float.class, Float.class,
|
||||
floatAdapter(serializeSpecialFloatingPointValues)));
|
||||
factories.add(excluder);
|
||||
factories.add(TypeAdapters.NUMBER_FACTORY);
|
||||
factories.add(TypeAdapters.CHARACTER_FACTORY);
|
||||
factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
|
||||
factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
|
||||
factories.add(TypeAdapters.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(BigInteger.class, TypeAdapters.BIG_INTEGER));
|
||||
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
|
||||
factories.add(TypeAdapters.URL_FACTORY);
|
||||
factories.add(TypeAdapters.URI_FACTORY);
|
||||
factories.add(TypeAdapters.UUID_FACTORY);
|
||||
@ -234,16 +234,30 @@ public final class Gson {
|
||||
factories.add(TimeTypeAdapter.FACTORY);
|
||||
factories.add(SqlDateTypeAdapter.FACTORY);
|
||||
factories.add(TypeAdapters.TIMESTAMP_FACTORY);
|
||||
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
|
||||
factories.add(ArrayTypeAdapter.FACTORY);
|
||||
factories.add(TypeAdapters.ENUM_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(
|
||||
constructorConstructor, fieldNamingPolicy, excluder));
|
||||
|
||||
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) {
|
||||
if (serializeSpecialFloatingPointValues) {
|
||||
return TypeAdapters.DOUBLE;
|
||||
|
@ -28,7 +28,6 @@ import java.util.Map;
|
||||
|
||||
import com.google.gson.internal.$Gson$Preconditions;
|
||||
import com.google.gson.internal.Excluder;
|
||||
import com.google.gson.internal.Primitives;
|
||||
import com.google.gson.internal.bind.TypeAdapters;
|
||||
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
|
||||
* 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 typeAdapter This object must implement at least one of the {@link TypeAdapter},
|
||||
* {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
|
||||
@ -446,10 +449,6 @@ public final class GsonBuilder {
|
||||
|| typeAdapter instanceof JsonDeserializer<?>
|
||||
|| typeAdapter instanceof InstanceCreator<?>
|
||||
|| 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<?>) {
|
||||
instanceCreators.put(type, (InstanceCreator) typeAdapter);
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ public class GsonBuilderTest extends TestCase {
|
||||
assertEquals("{\"d\":\"d\"}", gson.toJson(new HasModifiers()));
|
||||
}
|
||||
|
||||
public void testRegisterTypeAdapterForUnsupportedType() {
|
||||
public void testRegisterTypeAdapterForCoreType() {
|
||||
Type[] types = {
|
||||
byte.class,
|
||||
int.class,
|
||||
@ -62,11 +62,7 @@ public class GsonBuilderTest extends TestCase {
|
||||
String.class,
|
||||
};
|
||||
for (Type type : types) {
|
||||
try {
|
||||
new GsonBuilder().registerTypeAdapter(type, NULL_TYPE_ADAPTER);
|
||||
fail(type.toString());
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
new GsonBuilder().registerTypeAdapter(type, NULL_TYPE_ADAPTER);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
try {
|
||||
new GsonBuilder().registerTypeAdapter(long.class, new JsonSerializer<Long>() {
|
||||
public JsonElement serialize(Long s, Type t, JsonSerializationContext c) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
});
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
public void testCustomSerializerInvokedForPrimitives() {
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(boolean.class, new JsonSerializer<Boolean>() {
|
||||
public JsonElement serialize(Boolean s, Type t, JsonSerializationContext c) {
|
||||
return new JsonPrimitive(s ? 1 : 0);
|
||||
}
|
||||
})
|
||||
.create();
|
||||
assertEquals("1", gson.toJson(true, boolean.class));
|
||||
assertEquals("true", gson.toJson(true, Boolean.class));
|
||||
}
|
||||
|
||||
public void testCustomDeserializerForbiddenForPrimitives() {
|
||||
try {
|
||||
new GsonBuilder().registerTypeAdapter(long.class, new JsonDeserializer<Long>() {
|
||||
public Long deserialize(JsonElement json, Type t, JsonDeserializationContext c) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
});
|
||||
fail();
|
||||
} catch (Exception expected) {
|
||||
}
|
||||
public void testCustomDeserializerInvokedForPrimitives() {
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(boolean.class, new JsonDeserializer() {
|
||||
public Object deserialize(JsonElement json, Type t, JsonDeserializationContext context) {
|
||||
return json.getAsInt() != 0;
|
||||
}
|
||||
})
|
||||
.create();
|
||||
assertEquals(Boolean.TRUE, gson.fromJson("1", boolean.class));
|
||||
assertEquals(Boolean.TRUE, gson.fromJson("true", Boolean.class));
|
||||
}
|
||||
|
||||
public void testCustomByteArraySerializer() {
|
||||
|
@ -54,19 +54,18 @@ public class DelegateTypeAdapterTest extends TestCase {
|
||||
}
|
||||
String json = gson.toJson(bags);
|
||||
bags = gson.fromJson(json, new TypeToken<List<BagOfPrimitives>>(){}.getType());
|
||||
// 11: 1 list object, and 10 entries. stats not invoked on individual fields of
|
||||
// BagOfPrimitives since those are primitives.
|
||||
assertEquals(11, stats.numReads);
|
||||
assertEquals(11, stats.numWrites);
|
||||
// 11: 1 list object, and 10 entries. stats invoked on all 5 fields
|
||||
assertEquals(51, stats.numReads);
|
||||
assertEquals(51, stats.numWrites);
|
||||
}
|
||||
|
||||
public void testDelegateNotInvokedOnStrings() {
|
||||
public void testDelegateInvokedOnStrings() {
|
||||
String[] bags = {"1", "2", "3", "4"};
|
||||
String json = gson.toJson(bags);
|
||||
bags = gson.fromJson(json, String[].class);
|
||||
// Only 1 array object. stats not invoked on individual strings.
|
||||
assertEquals(1, stats.numReads);
|
||||
assertEquals(1, stats.numWrites);
|
||||
// 1 array object with 4 elements.
|
||||
assertEquals(5, stats.numReads);
|
||||
assertEquals(5, stats.numWrites);
|
||||
}
|
||||
|
||||
private static class StatsTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
Loading…
Reference in New Issue
Block a user