Skip to content

Commit

Permalink
Fix for owlike#132: RuntimeTypeConverter should always be used
Browse files Browse the repository at this point in the history
  • Loading branch information
aseovic committed Jun 1, 2018
1 parent 07719ab commit 076f5ba
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 34 deletions.
4 changes: 1 addition & 3 deletions genson/src/main/java/com/owlike/genson/GensonBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -839,10 +839,8 @@ protected Factory<Converter<?>> createConverterFactory() {
ChainedFactory chainHead = new CircularClassReferenceConverterFactory();

chainHead.append(new NullConverterFactory(failOnNullPrimitive));

if (useRuntimeTypeForSerialization) chainHead.append(new RuntimeTypeConverter.RuntimeTypeConverterFactory());

chainHead.append(new ClassMetadataConverter.ClassMetadataConverterFactory(classMetadataWithStaticType));
chainHead.append(new RuntimeTypeConverter.RuntimeTypeConverterFactory());

if (customFactoryChain != null) chainHead.append(customFactoryChain);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.owlike.genson.convert;

import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;

import com.owlike.genson.Context;
import com.owlike.genson.Converter;
Expand All @@ -17,34 +19,43 @@
* @author eugen
*/
public class RuntimeTypeConverter<T> extends Wrapper<Converter<T>> implements Converter<T> {
public static class RuntimeTypeConverterFactory extends ChainedFactory {
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
protected Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) {
if (nextConverter == null)
throw new IllegalArgumentException(
"RuntimeTypeConverter can not be last Converter in the chain.");
return (Converter<?>) new RuntimeTypeConverter(TypeUtil.getRawClass(type),
nextConverter);

public static class RuntimeTypeConverterFactory extends ChainedFactory {
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
protected Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) {
if (nextConverter == null)
throw new IllegalArgumentException(
"RuntimeTypeConverter can not be last Converter in the chain.");
return (Converter<?>) new RuntimeTypeConverter(TypeUtil.getRawClass(type),
nextConverter);
}
}
}

private final Class<T> tClass;
private final Class<T> tClass;

public RuntimeTypeConverter(Class<T> tClass, Converter<T> next) {
super(next);
this.tClass = tClass;
}
@SuppressWarnings("unchecked")
public RuntimeTypeConverter(Class<T> tClass, Converter<T> next) {
super(next);
this.tClass = tClass.isPrimitive()
? (Class<T>) TypeUtil.wrap(tClass)
: tClass;
}

public void serialize(T obj, ObjectWriter writer, Context ctx) throws Exception {
if (obj != null && !tClass.equals(obj.getClass()))
ctx.genson.serialize(obj, obj.getClass(), writer, ctx);
else
wrapped.serialize(obj, writer, ctx);
}
public void serialize(T obj, ObjectWriter writer, Context ctx) throws Exception {
if (obj != null && !tClass.equals(obj.getClass()) && !isContainer(obj))
ctx.genson.serialize(obj, obj.getClass(), writer, ctx);
else
wrapped.serialize(obj, writer, ctx);
}

public T deserialize(ObjectReader reader, Context ctx) throws Exception {
return wrapped.deserialize(reader, ctx);
}
public T deserialize(ObjectReader reader, Context ctx) throws Exception {
return wrapped.deserialize(reader, ctx);
}

private boolean isContainer(T obj) {
return obj.getClass().isArray() ||
obj instanceof Collection ||
obj instanceof Map;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import com.owlike.genson.stream.ObjectWriter;
import com.owlike.genson.stream.ValueType;

import java.io.IOException;
import java.lang.reflect.Type;

@HandleNull
Expand All @@ -17,7 +16,9 @@ static class OptionalConverterFactory implements Factory<Converter<Optional<Obje

@Override
public Converter<Optional<Object>> create(Type type, Genson genson) {
Type typeOfValue = TypeUtil.typeOf(0, type);
Type typeOfValue = type.equals(Optional.absent().getClass())
? Object.class
: TypeUtil.typeOf(0, type);

return new OptionalConverter<Object>(genson.provideConverter(typeOfValue));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,13 +337,16 @@ public BeanCreator createCreator(Type ofType, Method method, String[] resolvedNa
private Type getType(AccessibleObject object, Type objectType, Type contextType) {
XmlElement el = object.getAnnotation(XmlElement.class);
if (el != null && el.type() != XmlElement.DEFAULT.class) {
if (!TypeUtil.getRawClass(objectType).isAssignableFrom(el.type())) {
if (TypeUtil.getRawClass(objectType).isAssignableFrom(el.type())) {
return el.type();
}
else {
XmlJavaTypeAdapter ad = object.getAnnotation(XmlJavaTypeAdapter.class);
if (ad == null)
throw new ClassCastException("Inavlid XmlElement annotation, " + objectType
throw new ClassCastException("Invalid XmlElement annotation, " + objectType
+ " is not assignable from " + el.type());
return objectType;
}
return el.type();
} else
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,16 @@ public void testCircularReferencingClasses() {
}

@Test
@SuppressWarnings({"unchecked", "rawtypes"})
public void testUnwrapAnnotations() throws Exception {
Genson genson = new GensonBuilder().withConverters(new ClassMetadataConverter()).create();
@SuppressWarnings({"unchecked", "rawtypes"}) // argh its ugly with those warnings...
Wrapper<Converter<A>> wrapper = (Wrapper) genson.provideConverter(A.class);

Wrapper<Converter<A>> wrapper = (Wrapper) genson.provideConverter(A.class);
Converter<A> converter = wrapper.unwrap();
while (converter instanceof Wrapper) {
converter = (Converter<A>) ((Wrapper) converter).unwrap();
}

assertTrue(converter instanceof ClassMetadataConverter);
assertFalse(ClassMetadataConverter.used);
converter.serialize(new A(), null, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,30 @@ public String getNoField() {
return name + age;
}
}

@Test
public void testSerializeNestedPolymorphicType() {
String expected = "{\"pet\":{\"breed\":\"Boxer\",\"name\":\"Fido\"}}";

Dog dog = new Dog();
dog.name = "Fido";
dog.breed = "Boxer";

Person p = new Person();
p.pet = dog;

assertEquals(expected, genson.serialize(p));
}

private static class Pet {
public String name;
}

private static class Dog extends Pet{
public String breed;
}

private static class Person {
public Pet pet;
}
}

0 comments on commit 076f5ba

Please sign in to comment.