mirror of https://github.com/apache/jclouds.git
JCLOUDS-750 allow apis to bind FieldNamingPolicy, which controls fallback naming policy of serialized fields.
This commit is contained in:
parent
0b88dadb8f
commit
5b6f1e929e
|
@ -24,6 +24,7 @@ import com.google.common.collect.Sets;
|
|||
import com.google.common.primitives.Bytes;
|
||||
import com.google.gson.ExclusionStrategy;
|
||||
import com.google.gson.FieldAttributes;
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.FieldNamingStrategy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
@ -38,6 +39,7 @@ import com.google.gson.stream.JsonReader;
|
|||
import com.google.gson.stream.JsonWriter;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.ImplementedBy;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provides;
|
||||
import org.jclouds.date.DateService;
|
||||
import org.jclouds.domain.JsonBall;
|
||||
|
@ -63,7 +65,6 @@ import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.SetTypeAdapte
|
|||
import org.jclouds.json.internal.NullHackJsonLiteralAdapter;
|
||||
import org.jclouds.json.internal.OptionalTypeAdapterFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
import java.beans.ConstructorProperties;
|
||||
|
@ -78,11 +79,14 @@ import java.util.Set;
|
|||
|
||||
import static com.google.common.io.BaseEncoding.base16;
|
||||
|
||||
/**
|
||||
* Contains logic for parsing objects from Strings.
|
||||
*/
|
||||
public class GsonModule extends AbstractModule {
|
||||
|
||||
/** Optionally override the fallback policy when name annotations aren't on fields. */
|
||||
private static class FallbackFieldNamingPolicy {
|
||||
@Inject(optional = true)
|
||||
FieldNamingPolicy fallback = FieldNamingPolicy.IDENTITY;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Provides
|
||||
@Singleton
|
||||
|
@ -92,10 +96,11 @@ public class GsonModule extends AbstractModule {
|
|||
MapTypeAdapterFactory map, MultimapTypeAdapterFactory multimap, IterableTypeAdapterFactory iterable,
|
||||
CollectionTypeAdapterFactory collection, ListTypeAdapterFactory list,
|
||||
ImmutableListTypeAdapterFactory immutableList, FluentIterableTypeAdapterFactory fluentIterable,
|
||||
ImmutableMapTypeAdapterFactory immutableMap, DefaultExclusionStrategy exclusionStrategy) {
|
||||
ImmutableMapTypeAdapterFactory immutableMap, DefaultExclusionStrategy exclusionStrategy,
|
||||
FallbackFieldNamingPolicy fallbackFieldNamingPolicy) {
|
||||
|
||||
FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(ImmutableSet.of(
|
||||
new ExtractSerializedName(), new ExtractNamed()));
|
||||
new ExtractSerializedName(), new ExtractNamed()), fallbackFieldNamingPolicy.fallback);
|
||||
|
||||
GsonBuilder builder = new GsonBuilder().setFieldNamingStrategy(serializationPolicy)
|
||||
.setExclusionStrategies(exclusionStrategy);
|
||||
|
|
|
@ -43,6 +43,7 @@ import com.google.common.collect.FluentIterable;
|
|||
import com.google.common.collect.Maps;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.FieldNamingStrategy;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
|
@ -162,10 +163,14 @@ public class NamingStrategies {
|
|||
public static class AnnotationFieldNamingStrategy extends AnnotationBasedNamingStrategy implements
|
||||
FieldNamingStrategy {
|
||||
|
||||
public AnnotationFieldNamingStrategy(Iterable<? extends NameExtractor<?>> extractors) {
|
||||
private final FieldNamingPolicy fallback;
|
||||
|
||||
public AnnotationFieldNamingStrategy(Iterable<? extends NameExtractor<?>> extractors,
|
||||
FieldNamingPolicy fallback) {
|
||||
super(extractors);
|
||||
checkArgument(extractors.iterator().hasNext(), "you must supply at least one name extractor, for example: "
|
||||
+ ExtractSerializedName.class.getSimpleName());
|
||||
this.fallback = checkNotNull(fallback, "fallback fieldNamingPolicy");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -175,15 +180,16 @@ public class NamingStrategies {
|
|||
return annotationToNameExtractor.get(annotation.annotationType()).apply(annotation);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return fallback.translateName(f);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AnnotationOrNameFieldNamingStrategy extends AnnotationFieldNamingStrategy implements
|
||||
FieldNamingStrategy {
|
||||
|
||||
public AnnotationOrNameFieldNamingStrategy(Iterable<? extends NameExtractor<?>> extractors) {
|
||||
super(extractors);
|
||||
public AnnotationOrNameFieldNamingStrategy(Iterable<? extends NameExtractor<?>> extractors,
|
||||
FieldNamingPolicy fallback) {
|
||||
super(extractors, fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.testng.annotations.Test;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.FieldAttributes;
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
@ -209,4 +210,67 @@ public class JsonTest {
|
|||
EnumInsideWithParser.Test.UNRECOGNIZED);
|
||||
}
|
||||
|
||||
private abstract static class SpinalCasedType {
|
||||
abstract String id();
|
||||
|
||||
abstract String contentType();
|
||||
|
||||
// Currently, this only works for deserialization. Need to set a naming policy for serialization!
|
||||
@SerializedNames({ "id", "content-type" })
|
||||
private static SpinalCasedType create(String id, String contentType) {
|
||||
return new SpinalCasedTypeImpl(id, contentType);
|
||||
}
|
||||
}
|
||||
|
||||
public void spinalCaseWithSerializedNames() {
|
||||
Json json = Guice.createInjector(new GsonModule(), new AbstractModule() {
|
||||
@Override protected void configure() {
|
||||
bind(FieldNamingPolicy.class).toInstance(FieldNamingPolicy.LOWER_CASE_WITH_DASHES);
|
||||
}
|
||||
}).getInstance(Json.class);
|
||||
|
||||
SpinalCasedType resource = SpinalCasedType.create("1234", "application/json");
|
||||
String spinalJson = "{\"id\":\"1234\",\"content-type\":\"application/json\"}";
|
||||
|
||||
assertEquals(json.toJson(resource), spinalJson);
|
||||
assertEquals(spinalJson, json.toJson(resource));
|
||||
}
|
||||
|
||||
private static class SpinalCasedTypeImpl extends SpinalCasedType {
|
||||
private final String id;
|
||||
private final String contentType;
|
||||
|
||||
private SpinalCasedTypeImpl(String id, String contentType) {
|
||||
this.id = id;
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
@Override String id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override String contentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof SpinalCasedType) {
|
||||
SpinalCasedType that = (SpinalCasedType) o;
|
||||
return (this.id.equals(that.id())) && (this.contentType.equals(that.contentType()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
int h = 1;
|
||||
h *= 1000003;
|
||||
h ^= id.hashCode();
|
||||
h *= 1000003;
|
||||
h ^= contentType.hashCode();
|
||||
return h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import com.google.common.collect.ImmutableMap;
|
|||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.FieldNamingStrategy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
@ -62,7 +63,7 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
|||
|
||||
static DeserializationConstructorAndReflectiveTypeAdapterFactory parameterizedCtorFactory() {
|
||||
FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(ImmutableSet.of(
|
||||
new ExtractSerializedName(), new ExtractNamed()));
|
||||
new ExtractSerializedName(), new ExtractNamed()), FieldNamingPolicy.IDENTITY);
|
||||
AnnotationConstructorNamingStrategy deserializationPolicy = new AnnotationConstructorNamingStrategy(
|
||||
ImmutableSet.of(ConstructorProperties.class, SerializedNames.class, Inject.class),
|
||||
ImmutableSet.of(new ExtractNamed()));
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.jclouds.json.internal;
|
|||
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertNull;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
|
@ -37,6 +36,7 @@ import org.testng.annotations.Test;
|
|||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.FieldNamingStrategy;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
|
@ -101,16 +101,18 @@ public final class NamingStrategiesTest {
|
|||
}
|
||||
|
||||
public void testAnnotationFieldNamingStrategy() throws Exception {
|
||||
FieldNamingStrategy strategy = new AnnotationFieldNamingStrategy(ImmutableSet.of(new ExtractNamed()));
|
||||
FieldNamingStrategy strategy = new AnnotationFieldNamingStrategy(ImmutableSet.of(new ExtractNamed()),
|
||||
FieldNamingPolicy.UPPER_CAMEL_CASE);
|
||||
|
||||
assertNull(strategy.translateName(SimpleTest.class.getDeclaredField("a")));
|
||||
assertNull(strategy.translateName(SimpleTest.class.getDeclaredField("b")));
|
||||
assertEquals(strategy.translateName(SimpleTest.class.getDeclaredField("a")), "A"); // Per fallback!
|
||||
assertEquals(strategy.translateName(SimpleTest.class.getDeclaredField("b")), "B"); // Per fallback!
|
||||
assertEquals(strategy.translateName(SimpleTest.class.getDeclaredField("c")), "cat");
|
||||
assertEquals(strategy.translateName(SimpleTest.class.getDeclaredField("d")), "dog");
|
||||
}
|
||||
|
||||
public void testAnnotationOrNameFieldNamingStrategy() throws Exception {
|
||||
FieldNamingStrategy strategy = new AnnotationOrNameFieldNamingStrategy(ImmutableSet.of(new ExtractNamed()));
|
||||
FieldNamingStrategy strategy = new AnnotationOrNameFieldNamingStrategy(ImmutableSet.of(new ExtractNamed()),
|
||||
FieldNamingPolicy.IDENTITY);
|
||||
|
||||
assertEquals(strategy.translateName(SimpleTest.class.getDeclaredField("a")), "a");
|
||||
assertEquals(strategy.translateName(SimpleTest.class.getDeclaredField("b")), "b");
|
||||
|
|
Loading…
Reference in New Issue