diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/AppImport.java b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/AppImport.java new file mode 100644 index 000000000..1c43ba767 --- /dev/null +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/AppImport.java @@ -0,0 +1,13 @@ +package io.micronaut.serde.jackson.mixin; + +import io.micronaut.serde.annotation.SerdeImport; + +@SerdeImport( + value = Request.class, + mixin = RequestMixin.class +) +@SerdeImport( + value = Message.class, + mixin = MessageMixin.class +) +class AppImport {} diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/FooMessage.java b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/FooMessage.java new file mode 100644 index 000000000..6b3f53278 --- /dev/null +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/FooMessage.java @@ -0,0 +1,8 @@ +package io.micronaut.serde.jackson.mixin; + +import io.micronaut.serde.annotation.Serdeable; + +@Serdeable +public record FooMessage( + T payload +) implements Message { } diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/Message.java b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/Message.java new file mode 100644 index 000000000..186ad1836 --- /dev/null +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/Message.java @@ -0,0 +1,5 @@ +package io.micronaut.serde.jackson.mixin; + +public interface Message { + T payload(); +} diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/MessageMixin.java b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/MessageMixin.java new file mode 100644 index 000000000..e67c76f2e --- /dev/null +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/MessageMixin.java @@ -0,0 +1,9 @@ +package io.micronaut.serde.jackson.mixin; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.micronaut.serde.annotation.Serdeable; + +@Serdeable(validate = false) +@JsonDeserialize(as = FooMessage.class) +public interface MessageMixin { +} diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/MyTestClass.java b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/MyTestClass.java new file mode 100644 index 000000000..dc378731b --- /dev/null +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/MyTestClass.java @@ -0,0 +1,5 @@ +package io.micronaut.serde.jackson.mixin; + +public record MyTestClass( + String name +) implements Request { } diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/Request.java b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/Request.java new file mode 100644 index 000000000..1860ad9fe --- /dev/null +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/Request.java @@ -0,0 +1,4 @@ +package io.micronaut.serde.jackson.mixin; + +public interface Request { +} diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/RequestMixin.java b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/RequestMixin.java new file mode 100644 index 000000000..4a2e6ddfe --- /dev/null +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/RequestMixin.java @@ -0,0 +1,14 @@ +package io.micronaut.serde.jackson.mixin; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(MyTestClass.class), +}) +public interface RequestMixin { +} diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/SerdeMixinSpec.groovy b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/SerdeMixinSpec.groovy new file mode 100644 index 000000000..6888e26e9 --- /dev/null +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/mixin/SerdeMixinSpec.groovy @@ -0,0 +1,22 @@ +package io.micronaut.serde.jackson.mixin + +import io.micronaut.context.ApplicationContext +import io.micronaut.serde.ObjectMapper +import spock.lang.Specification + +class SerdeMixinSpec extends Specification { + + void "should deserialize"() { + given: + def context = ApplicationContext.run() + expect: + def read = context.getBean(ObjectMapper).readValue('{"payload": {"type": "MyTestClass", "name": "Some name"}}', FooMessage) + + read.getClass().name.endsWith 'FooMessage' + read.payload().getClass().name.endsWith 'MyTestClass' + read.payload().name() == 'Some name' + + cleanup: + context.close() + } +} diff --git a/serde-processor/src/main/java/io/micronaut/serde/processor/SerdeAnnotationVisitor.java b/serde-processor/src/main/java/io/micronaut/serde/processor/SerdeAnnotationVisitor.java index 591fdc47e..70d2bafcf 100644 --- a/serde-processor/src/main/java/io/micronaut/serde/processor/SerdeAnnotationVisitor.java +++ b/serde-processor/src/main/java/io/micronaut/serde/processor/SerdeAnnotationVisitor.java @@ -727,7 +727,7 @@ private void visitClassInternal(ClassElement element, VisitorContext context, bo String serializeAs = declaredAnnotation.stringValue(SerdeConfig.SERIALIZE_AS).orElse(null); if (serializeAs != null) { ClassElement thatType = context.getClassElement(serializeAs).orElse(null); - if (thatType != null && !thatType.isAssignable(element)) { + if (thatType != null && !thatType.isAssignable(element) && failOnError) { throw new ProcessingException(element, "Type to serialize as [" + serializeAs + "], must be a subtype of the annotated type: " + element.getName()); } } @@ -735,7 +735,7 @@ private void visitClassInternal(ClassElement element, VisitorContext context, bo String deserializeAs = declaredAnnotation.stringValue(SerdeConfig.DESERIALIZE_AS).orElse(null); if (deserializeAs != null) { ClassElement thatType = context.getClassElement(deserializeAs).orElse(null); - if (thatType != null && !thatType.isAssignable(element)) { + if (thatType != null && !thatType.isAssignable(element) && failOnError) { throw new ProcessingException(element, "Type to deserialize as [" + deserializeAs + "], must be a subtype of the annotated type: " + element.getName()); } } @@ -1065,7 +1065,10 @@ private void handleJsonIgnoreType(VisitorContext context, TypedElement beanPrope private void resetForNewClass(ClassElement element) { this.currentClass = element; - this.failOnError = element.booleanValue(SerdeConfig.class, "validate").orElse(true); + // TODO: Investigate why the `AliasFor` doesn't work here + this.failOnError = element.booleanValue(SerdeConfig.class, SerdeConfig.VALIDATE) + .or(() -> element.booleanValue(Serdeable.class, SerdeConfig.VALIDATE)) + .orElse(true); this.creatorMode = SerdeConfig.SerCreatorMode.PROPERTIES; this.anyGetterMethod = null; this.anySetterMethod = null;