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
|
* If there's an annotation designating a parameterized constructor, invoke that for fields
|
||||||
* correlating to named parameter annotations. Otherwise, use {@link ConstructorConstructor}, and
|
* correlating to named parameter annotations. Otherwise, use {@link ConstructorConstructor}, and
|
||||||
* set fields via reflection.
|
* 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>
|
* <li>Serialization</li>
|
||||||
* Serialize based on reflective access to fields, delegating to ReflectiveTypeAdaptor.
|
* Serialize based on reflective access to fields, delegating to ReflectiveTypeAdaptor.
|
||||||
* </ul>
|
* </ul>
|
||||||
|
@ -155,8 +158,9 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
||||||
|
|
||||||
Class<?>[] paramTypes = parameterizedCtor.getParameterTypes();
|
Class<?>[] paramTypes = parameterizedCtor.getParameterTypes();
|
||||||
Object[] ctorParams = new Object[paramTypes.length];
|
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++) {
|
for (int i = 0; i < paramTypes.length; i++) {
|
||||||
if (paramTypes[i] == boolean.class) {
|
if (paramTypes[i] == boolean.class) {
|
||||||
ctorParams[i] = Boolean.FALSE;
|
ctorParams[i] = Boolean.FALSE;
|
||||||
|
@ -168,6 +172,7 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
||||||
try {
|
try {
|
||||||
in.beginObject();
|
in.beginObject();
|
||||||
while (in.hasNext()) {
|
while (in.hasNext()) {
|
||||||
|
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) {
|
||||||
|
@ -183,12 +188,21 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
||||||
|
|
||||||
for (int i = 0; i < paramTypes.length; i++) {
|
for (int i = 0; i < paramTypes.length; i++) {
|
||||||
if (paramTypes[i].isPrimitive()) {
|
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!");
|
+ " cannot be absent!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.endObject();
|
in.endObject();
|
||||||
|
|
||||||
|
try {
|
||||||
return newInstance(ctorParams);
|
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.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
import static org.testng.Assert.assertNotSame;
|
import static org.testng.Assert.assertNotSame;
|
||||||
import static org.testng.Assert.assertNull;
|
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.jclouds.json.internal.NamingStrategies.ExtractSerializedName;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.gson.FieldNamingStrategy;
|
import com.google.gson.FieldNamingStrategy;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
@ -164,6 +166,8 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() { return "ValidatedConstructor[foo=" + foo + ",bar=" + bar + "]"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testValidatedConstructor() throws IOException {
|
public void testValidatedConstructor() throws IOException {
|
||||||
|
@ -227,7 +231,6 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRenamedFields() throws IOException {
|
public void testRenamedFields() throws IOException {
|
||||||
|
@ -236,6 +239,44 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
||||||
assertEquals(adapter.toJson(new RenamedFields(0, 1)), "{\"foo\":0,\"_bar\":1}");
|
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 {
|
public void testCanOverrideDefault() throws IOException {
|
||||||
Gson gson = new GsonBuilder().registerTypeAdapterFactory(parameterizedCtorFactory).create();
|
Gson gson = new GsonBuilder().registerTypeAdapterFactory(parameterizedCtorFactory).create();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue