From eb5ccebc47b914c34ba62d182347adf0233e6dbe Mon Sep 17 00:00:00 2001 From: Ed Mitchell Date: Wed, 6 Jul 2022 01:44:03 -0500 Subject: [PATCH] fix: #233 Allow @JsonIgnoreProperties and @JsonUnwrapped to work together --- .../annotation/JsonUnwrappedSpec.groovy | 100 +++++++++++++++++- .../serde/support/serializers/SerBean.java | 29 ++--- 2 files changed, 115 insertions(+), 14 deletions(-) diff --git a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/JsonUnwrappedSpec.groovy b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/JsonUnwrappedSpec.groovy index e8e823fe7..80cb3f764 100644 --- a/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/JsonUnwrappedSpec.groovy +++ b/serde-jackson/src/test/groovy/io/micronaut/serde/jackson/annotation/JsonUnwrappedSpec.groovy @@ -72,7 +72,7 @@ class Name { this.first = first; } public String getFirst() { - return first; + return first; } } """) @@ -153,6 +153,102 @@ class Name { context.close() } + void "test @JsonUnwrapped with @JsonIgnoreProperties"() { + given: + def context = buildContext(""" +package unwrapped; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import io.micronaut.core.annotation.Introspected; +import io.micronaut.serde.annotation.Serdeable; + +@Serdeable +@Introspected(accessKind = Introspected.AccessKind.FIELD) +class Parent { + public int age; + @JsonUnwrapped + @JsonIgnoreProperties("ignored") + public Name name; +} + +@Serdeable +@Introspected(accessKind = Introspected.AccessKind.FIELD) +class Name { + public String first, last; + public String ignored; +} +""") + + when: + def name = newInstance(context, 'unwrapped.Name', [first:"Fred", last:"Flinstone", ignored:"Ignored"]) + def parent = newInstance(context, 'unwrapped.Parent', [age:10, name:name]) + + def result = writeJson(jsonMapper, parent) + + then: + result == '{"age":10,"first":"Fred","last":"Flinstone"}' + + when: + def read = jsonMapper.readValue(result, Argument.of(context.classLoader.loadClass('unwrapped.Parent'))) + + then: + read.age == 10 + read.name.first == 'Fred' + read.name.last == "Flinstone" + + cleanup: + context.close() + } + + void "test @JsonUnwrapped with @JsonIgnoreProperties on class"() { + given: + def context = buildContext(""" +package unwrapped; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import io.micronaut.core.annotation.Introspected; +import io.micronaut.serde.annotation.Serdeable; + +@Serdeable +@Introspected(accessKind = Introspected.AccessKind.FIELD) +class Parent { + public int age; + @JsonUnwrapped + public Name name; +} + +@Serdeable +@Introspected(accessKind = Introspected.AccessKind.FIELD) +@JsonIgnoreProperties("ignored") +class Name { + public String first, last; + public String ignored; +} +""") + + when: + def name = newInstance(context, 'unwrapped.Name', [first:"Fred", last:"Flinstone", ignored:"Ignored"]) + def parent = newInstance(context, 'unwrapped.Parent', [age:10, name:name]) + + def result = writeJson(jsonMapper, parent) + + then: + result == '{"age":10,"first":"Fred","last":"Flinstone"}' + + when: + def read = jsonMapper.readValue(result, Argument.of(context.classLoader.loadClass('unwrapped.Parent'))) + + then: + read.age == 10 + read.name.first == 'Fred' + read.name.last == "Flinstone" + + cleanup: + context.close() + } + @Requires({ jvm.isJava17Compatible() }) void "test @JsonUnwrapped records"() { given: @@ -355,7 +451,7 @@ class Parent { public final int age; @JsonUnwrapped public final Name name; - + Parent(int age, @JsonUnwrapped Name name) { this.age = age; this.name = name; diff --git a/serde-support/src/main/java/io/micronaut/serde/support/serializers/SerBean.java b/serde-support/src/main/java/io/micronaut/serde/support/serializers/SerBean.java index 38bb3bd13..29fd8a275 100644 --- a/serde-support/src/main/java/io/micronaut/serde/support/serializers/SerBean.java +++ b/serde-support/src/main/java/io/micronaut/serde/support/serializers/SerBean.java @@ -212,24 +212,29 @@ public void initialize(Serializer.EncoderContext context) { PropertyNamingStrategy propertyNamingStrategy = getPropertyNamingStrategy(property.getAnnotationMetadata(), encoderContext, entityPropertyNamingStrategy); if (unwrapped) { BeanIntrospection propertyIntrospection = introspections.getSerializableIntrospection(property.asArgument()); + Set ignoredProperties = Arrays.stream(argument.getAnnotationMetadata().stringValues(SerdeConfig.SerIgnored.class)).collect(Collectors.toSet()); for (BeanProperty unwrappedProperty : propertyIntrospection.getBeanProperties()) { - Argument unwrappedPropertyArgument = unwrappedProperty.asArgument(); - String n = resolveName(propertyAnnotationMetadata, + if (!ignoredProperties.contains(unwrappedProperty.getName())) { + Argument unwrappedPropertyArgument = unwrappedProperty.asArgument(); + String n = resolveName(propertyAnnotationMetadata, unwrappedProperty.getAnnotationMetadata(), unwrappedPropertyArgument.getName(), true, propertyNamingStrategy); - final AnnotationMetadataHierarchy combinedMetadata = + final AnnotationMetadataHierarchy combinedMetadata = new AnnotationMetadataHierarchy( - argument.getAnnotationMetadata(), - unwrappedProperty.getAnnotationMetadata() + argument.getAnnotationMetadata(), + unwrappedProperty.getAnnotationMetadata() ); - CustomSerProperty prop = new CustomSerProperty<>(SerBean.this, n, - unwrappedPropertyArgument, - combinedMetadata, - bean -> unwrappedProperty.get(property.get(bean)) - ); - writeProperties.add(prop); - initializers.add(ctx -> initProperty(prop, ctx)); + if (!combinedMetadata.booleanValue(SerdeConfig.class, SerdeConfig.IGNORED).orElse(false)) { + CustomSerProperty prop = new CustomSerProperty<>(SerBean.this, n, + unwrappedPropertyArgument, + combinedMetadata, + bean -> unwrappedProperty.get(property.get(bean)) + ); + writeProperties.add(prop); + initializers.add(ctx -> initProperty(prop, ctx)); + } + } } } else { String n = resolveName(annotationMetadata, propertyAnnotationMetadata, defaultPropertyName, false, propertyNamingStrategy);