Skip to content

Commit

Permalink
Support ignoring constructor/record parameter (#809)
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov authored Apr 2, 2024
1 parent 7cd6f89 commit ba9e483
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package example

import com.fasterxml.jackson.annotation.JsonIgnore
import io.micronaut.serde.ObjectMapper
import io.micronaut.serde.annotation.Serdeable
import io.micronaut.test.extensions.junit5.annotation.MicronautTest
import org.junit.jupiter.api.Test

@MicronautTest
class SerdeableWithIgnoredNonSerdeableFieldTest {

@Test
fun test(objectMapper: ObjectMapper) {
val original = SerdeableWithIgnoredNonSerdeable(NonSerdeable("value"))

val serialized = objectMapper.writeValueAsString(original)
val deserialized = objectMapper.readValue(serialized, SerdeableWithIgnoredNonSerdeable::class.java)

assert(deserialized == original.copy(ignoredNonSerdeable = null))
}

}

@Serdeable
data class SerdeableWithIgnoredNonSerdeable(@field:JsonIgnore val ignoredNonSerdeable: NonSerdeable? = null)

data class NonSerdeable(val value: String)
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,36 @@ class Test {
context.close()
}

void "json ignore on a bean"() {
given:
def context = buildContext('example.Test', '''
package example;
import com.fasterxml.jackson.annotation.*;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.serde.annotation.Serdeable;
@JsonIgnoreProperties(ignoreUnknown = true)
@Introspected(accessKind = Introspected.AccessKind.FIELD)
class Test {
@JsonIgnore
public Ignored foo;
public String bar;
}
class Ignored {
}
''', [:])

def des = jsonMapper.readValue('{"foo": "1", "bar": "2"}', typeUnderTest)

expect:
des.foo == null
des.bar == "2"

cleanup:
context.close()
}

void "test @JsonIgnoreType"() {
given:
def context = buildContext("""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,76 @@ class SerdeJsonIgnoreSpec extends JsonIgnoreSpec {
protected String unknownPropertyMessage(String propertyName, String className) {
return "Unknown property [$propertyName] encountered during deserialization of type: ${NameUtils.getSimpleName(className)}"
}

void "json ignore on a constructor parameter"() {
given:
def context = buildContext('example.Test', '''
package example;
import com.fasterxml.jackson.annotation.*;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.serde.annotation.Serdeable;
@JsonIgnoreProperties(ignoreUnknown = true)
class Test{
@JsonIgnore
private final Ignored foo;
private final String bar;
@JsonCreator
public Test(@JsonProperty("foo") Ignored foo, @JsonProperty("bar") String bar) {
this.foo = foo;
this.bar = bar;
}
public example.Ignored getFoo() {
return foo;
}
public String getBar() {
return bar;
}
}
class Ignored {
}
''')

def des = jsonMapper.readValue('{"foo": "1", "bar": "2"}', typeUnderTest)

expect:
des.foo == null
des.bar == "2"

cleanup:
context.close()
}

void "json ignore on a record parameter"() {
given:
def context = buildContext('example.Test', '''
package example;
import com.fasterxml.jackson.annotation.*;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.serde.annotation.Serdeable;
@JsonIgnoreProperties(ignoreUnknown = true)
record Test(@JsonIgnore @JsonProperty("foo") Ignored foo, @JsonProperty("bar") String bar) {
}
class Ignored {
}
''')

def des = jsonMapper.readValue('{"foo": "1", "bar": "2"}', typeUnderTest)

expect:
des.foo == null
des.bar == "2"

cleanup:
context.close()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ public DeserBean(DeserializationConfiguration defaultDeserializationConfiguratio
PropertyNamingStrategy propertyNamingStrategy = getPropertyNamingStrategy(annotationMetadata, decoderContext, entityPropertyNamingStrategy);
final String propertyName = resolveName(serdeArgumentConf, constructorArgument, annotationMetadata, propertyNamingStrategy);

if (isIgnored(annotationMetadata) || (allowPropertyPredicate != null && !allowPropertyPredicate.test(propertyName))) {
boolean isIgnored = isIgnored(annotationMetadata) || (allowPropertyPredicate != null && !allowPropertyPredicate.test(propertyName));
if (isIgnored) {
ignoredProperties.add(propertyName);
}

Expand All @@ -210,10 +211,13 @@ public DeserBean(DeserializationConfiguration defaultDeserializationConfiguratio
i,
propertyName,
constructorWithPropertyArgument,
introspection.getProperty(propertyName).orElse(null),
isIgnored ? null : introspection.getProperty(propertyName)
.or(() -> introspection.getProperty(constructorArgument.getName()))
.orElse(null),
null,
unwrapped,
null
null,
isIgnored
);
if (isUnwrapped) {
if (creatorUnwrapped == null) {
Expand Down Expand Up @@ -252,7 +256,8 @@ public DeserBean(DeserializationConfiguration defaultDeserializationConfiguratio
null,
null,
null,
null
null,
false
);
readPropertiesBuilder.register(jsonProperty, derProperty, true);
}
Expand Down Expand Up @@ -326,7 +331,8 @@ public DeserBean(DeserializationConfiguration defaultDeserializationConfiguratio
beanProperty,
null,
unwrapped,
null
null,
false
);
if (isUnwrapped) {
if (unwrappedProperties == null) {
Expand Down Expand Up @@ -366,7 +372,8 @@ public AnnotationMetadata getAnnotationMetadata() {
null,
jsonSetter,
null,
null
null,
false
);
readPropertiesBuilder.register(property, derProperty, true);
}
Expand Down Expand Up @@ -481,7 +488,9 @@ private boolean isRecordLikeBean() {
}

private void initProperty(DerProperty<T, Object> property, Deserializer.DecoderContext decoderContext) throws SerdeException {
property.deserializer = findDeserializer(decoderContext, property.argument);
if (!property.ignored) {
property.deserializer = findDeserializer(decoderContext, property.argument);
}
}

private PropertyNamingStrategy getPropertyNamingStrategy(AnnotationMetadata annotationMetadata,
Expand Down Expand Up @@ -703,6 +712,7 @@ public static final class DerProperty<B, P> {
public final DerProperty<?, ?> unwrappedProperty;
public final String managedRef;
public final String backRef;
public final boolean ignored;

// Null when DeserBean not initialized
public Deserializer<P> deserializer;
Expand All @@ -715,7 +725,8 @@ public static final class DerProperty<B, P> {
@Nullable BeanWriteProperty<B, P> beanProperty,
@Nullable BeanMethod<B, P> beanMethod,
@Nullable DeserBean<P> unwrapped,
@Nullable DerProperty<?, ?> unwrappedProperty) throws SerdeException {
@Nullable DerProperty<?, ?> unwrappedProperty,
boolean ignored) throws SerdeException {
this(conversionService,
introspection,
index,
Expand All @@ -725,7 +736,8 @@ public static final class DerProperty<B, P> {
beanProperty,
beanMethod,
unwrapped,
unwrappedProperty
unwrappedProperty,
ignored
);
}

Expand All @@ -738,10 +750,12 @@ public static final class DerProperty<B, P> {
@Nullable BeanWriteProperty<B, P> beanProperty,
@Nullable BeanMethod<B, P> beanMethod,
@Nullable DeserBean<P> unwrapped,
@Nullable DerProperty<?, ?> unwrappedProperty) throws SerdeException {
@Nullable DerProperty<?, ?> unwrappedProperty,
boolean ignored) throws SerdeException {
this.introspection = introspection;
this.index = index;
this.argument = argument;
this.ignored = ignored;
Class<?> type = argument.getType();
this.mustSetField = argument.isNonNull() || type.equals(Optional.class)
|| type.equals(OptionalLong.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.micronaut.serde.tck.jackson.databind

import io.micronaut.context.ApplicationContextBuilder
import io.micronaut.serde.jackson.JsonIgnoreSpec
import spock.lang.PendingFeature

class DatabindJsonIgnoreSpec extends JsonIgnoreSpec {

Expand All @@ -17,4 +18,78 @@ class DatabindJsonIgnoreSpec extends JsonIgnoreSpec {
return """Unrecognized field "$propertyName" (class $className), not marked as ignorable"""
}

@PendingFeature(reason = "Jackson doesn't support ignored record values")
void "json ignore on a record parameter"() {
given:
def context = buildContext('example.Test', '''
package example;
import com.fasterxml.jackson.annotation.*;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.serde.annotation.Serdeable;
@JsonIgnoreProperties(ignoreUnknown = true)
record Test(@JsonIgnore @JsonProperty("foo") Ignored foo, @JsonProperty("bar") String bar) {
}
class Ignored {
}
''')

def des = jsonMapper.readValue('{"foo": {}, "bar": "2"}', typeUnderTest)

expect:
des.foo == null
des.bar == "2"

cleanup:
context.close()
}

@PendingFeature(reason = "Jackson doesn't support ignored constructor values")
void "json ignore on a constructor parameter"() {
given:
def context = buildContext('example.Test', '''
package example;
import com.fasterxml.jackson.annotation.*;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.serde.annotation.Serdeable;
@JsonIgnoreProperties(ignoreUnknown = true)
class Test{
@JsonIgnore
private final Ignored foo;
private final String bar;
@JsonCreator
public Test(@JsonProperty("foo") Ignored foo, @JsonProperty("bar") String bar) {
this.foo = foo;
this.bar = bar;
}
public example.Ignored getFoo() {
return foo;
}
public String getBar() {
return bar;
}
}
class Ignored {
}
''')

def des = jsonMapper.readValue('{"foo": {}, "bar": "2"}', typeUnderTest)

expect:
des.foo == null
des.bar == "2"

cleanup:
context.close()
}

}

0 comments on commit ba9e483

Please sign in to comment.