Fixed bug found in docker provider where deserialization constructors don't handle json null.

This commit is contained in:
Adrian Cole 2014-10-26 11:44:48 -07:00
parent bfa91d5614
commit efd2beb4c5
3 changed files with 39 additions and 29 deletions

View File

@ -166,7 +166,7 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
empty = false; empty = false;
String name = in.nextName(); String name = in.nextName();
ParameterReader<?> parameter = parameterReaders.get(name); ParameterReader<?> parameter = parameterReaders.get(name);
if (parameter == null) { if (parameter == null || in.peek() == JsonToken.NULL) {
in.skipValue(); in.skipValue();
} else { } else {
Object value = parameter.read(in); Object value = parameter.read(in);

View File

@ -128,8 +128,8 @@ public class Reflection2 {
} }
/** /**
* This gets all declared constructors, not just public ones. makes them accessible, as well. * This gets all declared constructors or factory methods on abstract types, not just public ones, and makes them
* This also includes factory methods on abstract types, defined static methods returning the same type. * accessible.
*/ */
private static LoadingCache<TypeToken<?>, Set<Invokable<?, ?>>> constructorsForTypeToken = CacheBuilder private static LoadingCache<TypeToken<?>, Set<Invokable<?, ?>>> constructorsForTypeToken = CacheBuilder
.newBuilder().build(new CacheLoader<TypeToken<?>, Set<Invokable<?, ?>>>() { .newBuilder().build(new CacheLoader<TypeToken<?>, Set<Invokable<?, ?>>>() {
@ -143,6 +143,7 @@ public class Reflection2 {
if (Modifier.isAbstract(key.getRawType().getModifiers())) { if (Modifier.isAbstract(key.getRawType().getModifiers())) {
for (Invokable<?, Object> method : methods(key.getRawType())){ for (Invokable<?, Object> method : methods(key.getRawType())){
if (method.isStatic() && method.getReturnType().equals(key)) { if (method.isStatic() && method.getReturnType().equals(key)) {
method.setAccessible(true);
builder.add(method); builder.add(method);
} }
} }

View File

@ -20,14 +20,17 @@ import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.primitives.Bytes.asList; import static com.google.common.primitives.Bytes.asList;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.config.GsonModule; import org.jclouds.json.config.GsonModule;
import org.jclouds.json.config.GsonModule.DefaultExclusionStrategy; import org.jclouds.json.config.GsonModule.DefaultExclusionStrategy;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.gson.FieldAttributes; import com.google.gson.FieldAttributes;
@ -210,67 +213,73 @@ public class JsonTest {
EnumInsideWithParser.Test.UNRECOGNIZED); EnumInsideWithParser.Test.UNRECOGNIZED);
} }
private abstract static class SpinalCasedType { private abstract static class UpperCamelCasedType {
abstract String id(); abstract String id();
abstract String contentType(); @Nullable abstract Map<String, String> volumes();
// Currently, this only works for deserialization. Need to set a naming policy for serialization! // Currently, this only works for deserialization. Need to set a naming policy for serialization!
@SerializedNames({ "id", "content-type" }) @SerializedNames({ "Id", "Volumes" })
private static SpinalCasedType create(String id, String contentType) { private static UpperCamelCasedType create(String id, Map<String, String> volumes) {
return new SpinalCasedTypeImpl(id, contentType); return new UpperCamelCasedTypeImpl(id, volumes);
} }
} }
public void spinalCaseWithSerializedNames() { public void upperCamelCaseWithSerializedNames() {
Json json = Guice.createInjector(new GsonModule(), new AbstractModule() { Json json = Guice.createInjector(new GsonModule(), new AbstractModule() {
@Override protected void configure() { @Override protected void configure() {
bind(FieldNamingPolicy.class).toInstance(FieldNamingPolicy.LOWER_CASE_WITH_DASHES); bind(FieldNamingPolicy.class).toInstance(FieldNamingPolicy.UPPER_CAMEL_CASE);
} }
}).getInstance(Json.class); }).getInstance(Json.class);
SpinalCasedType resource = SpinalCasedType.create("1234", "application/json"); UpperCamelCasedType resource = UpperCamelCasedType.create("1234", Collections.<String, String>emptyMap());
String spinalJson = "{\"id\":\"1234\",\"content-type\":\"application/json\"}"; String spinalJson = "{\"Id\":\"1234\",\"Volumes\":{}}";
assertEquals(json.toJson(resource), spinalJson); assertEquals(json.toJson(resource), spinalJson);
assertEquals(spinalJson, json.toJson(resource)); assertEquals(json.fromJson(spinalJson, UpperCamelCasedType.class), resource);
} }
private static class SpinalCasedTypeImpl extends SpinalCasedType { public void upperCamelCaseWithSerializedNamesNullJsonValue() {
private final String id; Json json = Guice.createInjector(new GsonModule(), new AbstractModule() {
private final String contentType; @Override protected void configure() {
bind(FieldNamingPolicy.class).toInstance(FieldNamingPolicy.UPPER_CAMEL_CASE);
}
}).getInstance(Json.class);
private SpinalCasedTypeImpl(String id, String contentType) { assertEquals(json.fromJson("{\"Id\":\"1234\",\"Volumes\":null}", UpperCamelCasedType.class),
UpperCamelCasedType.create("1234", null));
}
private static class UpperCamelCasedTypeImpl extends UpperCamelCasedType {
private final String id;
private final Map<String, String> volumes;
private UpperCamelCasedTypeImpl(String id, Map<String, String> volumes) {
this.id = id; this.id = id;
this.contentType = contentType; this.volumes = volumes;
} }
@Override String id() { @Override String id() {
return id; return id;
} }
@Override String contentType() { @Override @Nullable Map<String, String> volumes() {
return contentType; return volumes;
} }
@Override public boolean equals(Object o) { @Override public boolean equals(Object o) {
if (o == this) { if (o == this) {
return true; return true;
} }
if (o instanceof SpinalCasedType) { if (o instanceof UpperCamelCasedType) {
SpinalCasedType that = (SpinalCasedType) o; UpperCamelCasedType that = (UpperCamelCasedType) o;
return (this.id.equals(that.id())) && (this.contentType.equals(that.contentType())); return Objects.equal(this.id, that.id()) && Objects.equal(this.volumes, that.volumes());
} }
return false; return false;
} }
@Override public int hashCode() { @Override public int hashCode() {
int h = 1; return Objects.hashCode(id, volumes);
h *= 1000003;
h ^= id.hashCode();
h *= 1000003;
h ^= contentType.hashCode();
return h;
} }
} }
} }