From c9772f081de89c3e971b47b79e3b718281cf6e86 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Wed, 10 Aug 2016 14:11:47 -0400 Subject: [PATCH] Fixed bug that caused error while serializing models using @JsonSubTypes. Fixed detection of multiple @JsonSerializeToVersion properties; getters and setters now allowed. --- CHANGELOG.md | 12 ++++ .../versioning/VersionedModelSerializer.java | 16 ++++- .../VersioningBeanSerializationModifier.java | 4 +- .../versioning/VersioningModuleTest.groovy | 71 ++++++++++++++++--- 4 files changed, 92 insertions(+), 11 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e61feea --- /dev/null +++ b/CHANGELOG.md @@ -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. \ No newline at end of file diff --git a/src/main/java/com/github/jonpeterson/jackson/module/versioning/VersionedModelSerializer.java b/src/main/java/com/github/jonpeterson/jackson/module/versioning/VersionedModelSerializer.java index e056602..43cec97 100644 --- a/src/main/java/com/github/jonpeterson/jackson/module/versioning/VersionedModelSerializer.java +++ b/src/main/java/com/github/jonpeterson/jackson/module/versioning/VersionedModelSerializer.java @@ -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; @@ -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 diff --git a/src/main/java/com/github/jonpeterson/jackson/module/versioning/VersioningBeanSerializationModifier.java b/src/main/java/com/github/jonpeterson/jackson/module/versioning/VersioningBeanSerializationModifier.java index 71cc02c..f84832f 100644 --- a/src/main/java/com/github/jonpeterson/jackson/module/versioning/VersioningBeanSerializationModifier.java +++ b/src/main/java/com/github/jonpeterson/jackson/module/versioning/VersioningBeanSerializationModifier.java @@ -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; } } diff --git a/src/test/groovy/com/github/jonpeterson/jackson/module/versioning/VersioningModuleTest.groovy b/src/test/groovy/com/github/jonpeterson/jackson/module/versioning/VersioningModuleTest.groovy index e25488f..40abb82 100644 --- a/src/test/groovy/com/github/jonpeterson/jackson/module/versioning/VersioningModuleTest.groovy +++ b/src/test/groovy/com/github/jonpeterson/jackson/module/versioning/VersioningModuleTest.groovy @@ -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 @@ -49,6 +51,7 @@ class VersioningModuleTest extends Specification { String model int year boolean used + String _debugPreSerializationVersion String _debugPreDeserializationVersion } @@ -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 { @@ -78,7 +86,7 @@ class VersioningModuleTest extends Specification { String s2v } - static class MultipleSerializeToCar extends Car { + static class MultipleSerializeToCar1 extends Car { @JsonSerializeToVersion String s2v @@ -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 *| @@ -222,6 +263,7 @@ class VersioningModuleTest extends Specification { model: 'civic', used: false, year: 2016, + _debugPreSerializationVersion: null, _debugPreDeserializationVersion: '1' ], [ modelVersion: '3', @@ -229,6 +271,7 @@ class VersioningModuleTest extends Specification { model: 'camry', used: true, year: 2012, + _debugPreSerializationVersion: null, _debugPreDeserializationVersion: '2' ], [ modelVersion: '3', @@ -236,6 +279,7 @@ class VersioningModuleTest extends Specification { model: '6', used: false, year: 2017, + _debugPreSerializationVersion: null, _debugPreDeserializationVersion: null ], [ modelVersion: '3', @@ -243,6 +287,7 @@ class VersioningModuleTest extends Specification { model: 'fusion', used: true, year: 2013, + _debugPreSerializationVersion: null, _debugPreDeserializationVersion: '4' ] ], @@ -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' + } } } \ No newline at end of file