mirror of https://github.com/apache/jclouds.git
openstack: adjusting deserialization to treat {} as null if (and ONLY if) a null pointer is thrown when attempting to construct an object from the empty json object ({})
This commit is contained in:
parent
908e164698
commit
3243325878
|
@ -52,6 +52,9 @@ import com.google.gson.stream.JsonWriter;
|
|||
* If there's an annotation designating a parameterized constructor, invoke that for fields
|
||||
* correlating to named parameter annotations. Otherwise, use {@link ConstructorConstructor}, and
|
||||
* set fields via reflection.
|
||||
* <p/>
|
||||
* Notes: primitive constructor params are set to the Java defaults (0 or false) if not present; and
|
||||
* the empty object ({}) is treated as a null if the constructor for the object throws an NPE.
|
||||
* <li>Serialization</li>
|
||||
* Serialize based on reflective access to fields, delegating to ReflectiveTypeAdaptor.
|
||||
* </ul>
|
||||
|
@ -155,8 +158,9 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
|||
|
||||
Class<?>[] paramTypes = parameterizedCtor.getParameterTypes();
|
||||
Object[] ctorParams = new Object[paramTypes.length];
|
||||
boolean empty = true;
|
||||
|
||||
// TODO determine if we can drop this
|
||||
// Set all primitive constructor params to defaults
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
if (paramTypes[i] == boolean.class) {
|
||||
ctorParams[i] = Boolean.FALSE;
|
||||
|
@ -168,6 +172,7 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
|||
try {
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
empty = false;
|
||||
String name = in.nextName();
|
||||
ParameterReader parameter = parameterReaders.get(name);
|
||||
if (parameter == null) {
|
||||
|
@ -183,12 +188,21 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
|||
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
if (paramTypes[i].isPrimitive()) {
|
||||
checkArgument(ctorParams[i] != null, "Primative param[" + i + "] in constructor " + parameterizedCtor
|
||||
checkArgument(ctorParams[i] != null, "Primitive param[" + i + "] in constructor " + parameterizedCtor
|
||||
+ " cannot be absent!");
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
|
||||
try {
|
||||
return newInstance(ctorParams);
|
||||
} catch (NullPointerException ex) {
|
||||
// If {} was found and constructor threw NPE, we treat the field as null
|
||||
if (empty && paramTypes.length > 0) {
|
||||
return null;
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.jclouds.json.internal;
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotSame;
|
||||
import static org.testng.Assert.assertNull;
|
||||
|
@ -38,6 +39,7 @@ import org.jclouds.json.internal.NamingStrategies.ExtractNamed;
|
|||
import org.jclouds.json.internal.NamingStrategies.ExtractSerializedName;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.FieldNamingStrategy;
|
||||
import com.google.gson.Gson;
|
||||
|
@ -164,6 +166,8 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return "ValidatedConstructor[foo=" + foo + ",bar=" + bar + "]"; }
|
||||
}
|
||||
|
||||
public void testValidatedConstructor() throws IOException {
|
||||
|
@ -227,7 +231,6 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
|||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void testRenamedFields() throws IOException {
|
||||
|
@ -236,6 +239,44 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
|||
assertEquals(adapter.toJson(new RenamedFields(0, 1)), "{\"foo\":0,\"_bar\":1}");
|
||||
}
|
||||
|
||||
private static class ComposedObjects {
|
||||
final ValidatedConstructor x;
|
||||
final ValidatedConstructor y;
|
||||
|
||||
@ConstructorProperties({"x", "y"})
|
||||
ComposedObjects(ValidatedConstructor x, ValidatedConstructor y) {
|
||||
this.x = checkNotNull(x);
|
||||
this.y = checkNotNull(y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
ComposedObjects other = ComposedObjects.class.cast(obj);
|
||||
return other != null && Objects.equal(x, other.x) && Objects.equal(y, other.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return "ComposedObjects[x=" + x.toString() + ";y=" + y.toString() + "]"; }
|
||||
}
|
||||
|
||||
public void checkSimpleComposedObject() throws IOException {
|
||||
ValidatedConstructor x = new ValidatedConstructor(0,1);
|
||||
ValidatedConstructor y = new ValidatedConstructor(1,2);
|
||||
TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(ComposedObjects.class));
|
||||
assertEquals(new ComposedObjects(x, y), adapter.fromJson("{\"x\":{\"foo\":0,\"bar\":1},\"y\":{\"foo\":1,\"bar\":2}}"));
|
||||
}
|
||||
|
||||
public void testEmptyObjectIsNull() throws IOException {
|
||||
TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(ComposedObjects.class));
|
||||
assertNull(adapter.fromJson("{}"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testPartialObjectStillThrows() throws IOException {
|
||||
TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(ComposedObjects.class));
|
||||
assertNull(adapter.fromJson("{\"x\":{\"foo\":0,\"bar\":1}}"));
|
||||
}
|
||||
|
||||
public void testCanOverrideDefault() throws IOException {
|
||||
Gson gson = new GsonBuilder().registerTypeAdapterFactory(parameterizedCtorFactory).create();
|
||||
|
||||
|
|
Loading…
Reference in New Issue