Skip to content

Commit

Permalink
Fixed bug that caused error while serializing models using @JsonSubTy…
Browse files Browse the repository at this point in the history
…pes.

Fixed detection of multiple @JsonSerializeToVersion properties; getters and setters now allowed.
  • Loading branch information
jonpeterson committed Aug 10, 2016
1 parent 103b8a2 commit c9772f0
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 11 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## 1.1.1 (????-??-??)

- jonpeterson: Fixed bug that caused error while serializing models using @JsonSubTypes.
- jonpeterson: Fixed detection of multiple @JsonSerializeToVersion properties; getters and setters now allowed.

## 1.1.0 (2016-08-04)

- jonpeterson: Added @JsonSerializeToVersion annotation.

## 1.0.0 (2016-08-01)

- jonpeterson: Initial release.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.ResolvableSerializer;
Expand Down Expand Up @@ -68,16 +69,29 @@ public void resolve(SerializerProvider provider) throws JsonMappingException {

@Override
public void serialize(T value, JsonGenerator generator, SerializerProvider provider) throws IOException {
doSerialize(value, generator, provider, null);
}

@Override
public void serializeWithType(T value, JsonGenerator generator, SerializerProvider provider, TypeSerializer typeSerializer) throws IOException {
doSerialize(value, generator, provider, typeSerializer);
}

private void doSerialize(T value, JsonGenerator generator, SerializerProvider provider, TypeSerializer typeSerializer) throws IOException {
// serialize the value into a byte array buffer then parse it back out into a JsonNode tree
// TODO: find a better way to convert the value into a tree
JsonFactory factory = generator.getCodec().getFactory();
ByteArrayOutputStream buffer = new ByteArrayOutputStream(4096);
JsonGenerator bufferGenerator = factory.createGenerator(buffer);
try {
delegate.serialize(value, bufferGenerator, provider);
if(typeSerializer != null)
delegate.serializeWithType(value, bufferGenerator, provider, typeSerializer);
else
delegate.serialize(value, bufferGenerator, provider);
} finally {
bufferGenerator.close();
}

ObjectNode modelData = factory.createParser(buffer.toByteArray()).readValueAsTree();

// set target version to @SerializeToVersion's value, @JsonVersionModel's defaultSerializeToVersion, or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ private static BeanPropertyDefinition getSerializeToVersionProperty(BeanDescript
if(accessor.hasAnnotation(JsonSerializeToVersion.class)) {
if(serializeToVersionProperty != null)
throw new RuntimeException("@" + JsonSerializeToVersion.class.getSimpleName() + " must be present on at most one field or method");
if(accessor.getRawType() != String.class)
throw new RuntimeException("@" + JsonSerializeToVersion.class.getSimpleName() + " must be on a String field or method that returns a String");
if(accessor.getRawType() != String.class || (definition.getField() == null && !definition.hasGetter()))
throw new RuntimeException("@" + JsonSerializeToVersion.class.getSimpleName() + " must be on a field or a getter method that returns a String");
serializeToVersionProperty = definition;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
package com.github.jonpeterson.jackson.module.versioning

import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.JsonNodeFactory
import com.fasterxml.jackson.databind.node.ObjectNode
Expand All @@ -49,6 +51,7 @@ class VersioningModuleTest extends Specification {
String model
int year
boolean used
String _debugPreSerializationVersion
String _debugPreDeserializationVersion
}

Expand All @@ -70,6 +73,11 @@ class VersioningModuleTest extends Specification {
String getSerializeToVersion() {
return s2v
}

@JsonSerializeToVersion
void setSerializeToVersion(String s2v) {
this.s2v = s2v
}
}

static class FieldSerializeToCar extends DefaultSerializeToCar {
Expand All @@ -78,7 +86,7 @@ class VersioningModuleTest extends Specification {
String s2v
}

static class MultipleSerializeToCar extends Car {
static class MultipleSerializeToCar1 extends Car {

@JsonSerializeToVersion
String s2v
Expand All @@ -89,12 +97,45 @@ class VersioningModuleTest extends Specification {
}
}

static class MultipleSerializeToCar2 extends Car {

@JsonSerializeToVersion
String s2vA

@JsonSerializeToVersion
String s2vB
}

static class MultipleSerializeToCar3 extends Car {

@JsonSerializeToVersion
String getSerializeToVersionA() {
return '1'
}

@JsonSerializeToVersion
String getSerializeToVersionB() {
return '1'
}
}

static class WrongTypeSerializeToCar extends Car {

@JsonSerializeToVersion
int s2v
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = '_type')
@JsonSubTypes([
@JsonSubTypes.Type(value = HondaCar.class, name = 'honda')
])
static abstract class AbstractCar extends FieldSerializeToCar {
}

static class HondaCar extends AbstractCar {
String somethingHondaSpecific
}


/***********************************\
|* Test versioned model converters *|
Expand Down Expand Up @@ -222,27 +263,31 @@ class VersioningModuleTest extends Specification {
model: 'civic',
used: false,
year: 2016,
_debugPreSerializationVersion: null,
_debugPreDeserializationVersion: '1'
], [
modelVersion: '3',
make: 'toyota',
model: 'camry',
used: true,
year: 2012,
_debugPreSerializationVersion: null,
_debugPreDeserializationVersion: '2'
], [
modelVersion: '3',
make: 'mazda',
model: '6',
used: false,
year: 2017,
_debugPreSerializationVersion: null,
_debugPreDeserializationVersion: null
], [
modelVersion: '3',
make: 'ford',
model: 'fusion',
used: true,
year: 2013,
_debugPreSerializationVersion: null,
_debugPreDeserializationVersion: '4'
]
],
Expand Down Expand Up @@ -313,17 +358,27 @@ class VersioningModuleTest extends Specification {
}

def 'errors'() {
when: 'multiple @SerializeTo'
mapper.writeValueAsString(new MultipleSerializeToCar())
when:
mapper.writeValueAsString(clazz.newInstance())

then:
thrown RuntimeException

where:
clazz << [MultipleSerializeToCar1, MultipleSerializeToCar2, MultipleSerializeToCar3, WrongTypeSerializeToCar]
}

when: 'wrong return type of @SerializeTo'
mapper.writeValueAsString(new WrongTypeSerializeToCar())

then:
thrown RuntimeException
def 'serialization with Jackson sub-types'() {
expect:
def serialized = mapper.writeValueAsString(new HondaCar(make: 'honda', model: 'civic', used: false, year: 2016, somethingHondaSpecific: 'blah'))
with(mapper.readValue(serialized, AbstractCar)) {
make == 'honda'
model == 'civic'
!used
year == 2016
somethingHondaSpecific == 'blah'
_debugPreSerializationVersion == '3'
_debugPreDeserializationVersion == '2'
}
}
}

0 comments on commit c9772f0

Please sign in to comment.