Skip to content

Commit

Permalink
Correct ignored properties for a constructor injection (#773)
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov authored Feb 23, 2024
1 parent d1bf594 commit 8dd9e6d
Show file tree
Hide file tree
Showing 4 changed files with 264 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,91 @@ import spock.lang.Unroll

class JsonPropertySpec extends JsonCompileSpec {

void "test @JsonProperty.Access.WRITE_ONLY (set only) - records"() {
given:
def context = buildContext("""
package test;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.micronaut.serde.annotation.Serdeable;
@Serdeable
record Test(
@JsonProperty
String value,
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
String ignored
) {}
""")
when:
def bean = newInstance(context, 'test.Test', "test", "xyz")
def result = writeJson(jsonMapper, bean)

then:
result == '{"value":"test"}'

when:
bean = jsonMapper.readValue('{"value":"test","ignored":"xyz"}', argumentOf(context, 'test.Test'))

then:
bean.value == 'test'
bean.ignored == 'xyz'

cleanup:
context.close()
}

void "test @JsonProperty.Access.WRITE_ONLY (set only) - constructor"() {
given:
def context = buildContext("""
package test;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.micronaut.serde.annotation.Serdeable;
@Serdeable
class Test {
private String value;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String ignored;
@JsonCreator
public Test(@JsonProperty("value") String value, @JsonProperty("ignored") String ignored) {
this.value = value;
this.ignored = ignored;
}
public String getValue() {
return this.value;
}
public String getIgnored() {
return this.ignored;
}
}
""")
when:
def bean = newInstance(context, 'test.Test', "test", "xyz")
def result = writeJson(jsonMapper, bean)

then:
result == '{"value":"test"}'

when:
bean = jsonMapper.readValue('{"value":"test","ignored":"xyz"}', argumentOf(context, 'test.Test'))

then:
bean.value == 'test'
bean.ignored == 'xyz'

cleanup:
context.close()
}


@Unroll
void "serde Number"(Number number) {
given:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,92 @@ import spock.lang.PendingFeature

class SerdeJsonPropertySpec extends JsonPropertySpec {

void "test @JsonProperty.Access.READ_ONLY (get only) - constructor"() {
// Jackson cannot deserialize READ_ONLY as null
given:
def context = buildContext("""
package test;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.micronaut.serde.annotation.Serdeable;
@Serdeable
class Test {
private String value;
private String ignored;
@JsonCreator
public Test(@JsonProperty("value") String value, @JsonProperty(value = "ignored", access = JsonProperty.Access.READ_ONLY) String ignored) {
this.value = value;
this.ignored = ignored;
}
public String getValue() {
return this.value;
}
public String getIgnored() {
return this.ignored;
}
}
""")
when:
def bean = newInstance(context, 'test.Test', "test", "xyz")
def result = writeJson(jsonMapper, bean)

then:
result == '{"value":"test","ignored":"xyz"}'

when:
bean = jsonMapper.readValue('{"value":"test","ignored":"xyz"}', argumentOf(context, 'test.Test'))

then:
bean.value == 'test'
bean.ignored == null

cleanup:
context.close()
}

void "test @JsonProperty.Access.READ_ONLY (get only) - record"() {
// Jackson cannot deserialize READ_ONLY as null
given:
def context = buildContext("""
package test;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.micronaut.serde.annotation.Serdeable;
@Serdeable
record Test(
@JsonProperty
String value,
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
String ignored
) {}
""")
when:
def bean = newInstance(context, 'test.Test', "test", "xyz")
def result = writeJson(jsonMapper, bean)

then:
result == '{"value":"test","ignored":"xyz"}'

when:
bean = jsonMapper.readValue('{"value":"test","ignored":"xyz"}', argumentOf(context, 'test.Test'))

then:
bean.value == 'test'
bean.ignored == null

cleanup:
context.close()
}

void "test optional by default primitive field in constructor XXX"() {

given:
Expand Down Expand Up @@ -238,7 +324,7 @@ import io.micronaut.serde.annotation.Serdeable;
record Test(
@JsonProperty(value = "other", defaultValue = "default")
String value,
@JsonProperty(access = JsonProperty.Access.READ_ONLY, defaultValue = "false")
@JsonProperty(access = JsonProperty.Access.READ_ONLY, defaultValue = "false") // Get only
boolean ignored
) {}
""")
Expand All @@ -250,7 +336,7 @@ record Test(
result == '{"other":"test","ignored":false}'

when:
bean = jsonMapper.readValue(result, argumentOf(context, 'test.Test'))
bean = jsonMapper.readValue('{"other":"test","ignored":true}', argumentOf(context, 'test.Test'))

then:
bean.ignored == false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,6 @@ public DeserBean(DeserializationConfiguration defaultDeserializationConfiguratio
for (int i = 0; i < constructorArguments.length; i++) {
Argument<Object> constructorArgument = resolveArgument((Argument<Object>) constructorArguments[i]);
final AnnotationMetadata annotationMetadata = resolveArgumentMetadata(introspection, constructorArgument, constructorArgument.getAnnotationMetadata());
if (annotationMetadata.isTrue(SerdeConfig.class, SerdeConfig.IGNORED)
|| annotationMetadata.isTrue(SerdeConfig.class, SerdeConfig.IGNORED_DESERIALIZATION)) {
continue;
}
if (annotationMetadata.isAnnotationPresent(SerdeConfig.SerAnySetter.class)) {
anySetterValue = new AnySetter<>(constructorArgument, i);
continue;
Expand All @@ -189,8 +185,8 @@ public DeserBean(DeserializationConfiguration defaultDeserializationConfiguratio
PropertyNamingStrategy propertyNamingStrategy = getPropertyNamingStrategy(annotationMetadata, decoderContext, entityPropertyNamingStrategy);
final String propertyName = resolveName(serdeArgumentConf, constructorArgument, annotationMetadata, propertyNamingStrategy);

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

Argument<Object> constructorWithPropertyArgument = Argument.of(
Expand Down Expand Up @@ -608,8 +604,7 @@ private static <T> Deserializer<T> findDeserializer(Deserializer.DecoderContext
return (Deserializer<T>) decoderContext.findDeserializer(argument).createSpecific(decoderContext, argument);
}

private boolean isIgnored(BeanProperty<T, Object> bp) {
final AnnotationMetadata annotationMetadata = bp.getAnnotationMetadata();
private boolean isIgnored(AnnotationMetadata annotationMetadata) {
return annotationMetadata.booleanValue(SerdeConfig.class, SerdeConfig.READ_ONLY).orElse(false)
|| annotationMetadata.booleanValue(SerdeConfig.class, SerdeConfig.IGNORED).orElse(false)
|| annotationMetadata.booleanValue(SerdeConfig.class, SerdeConfig.IGNORED_DESERIALIZATION).orElse(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,94 @@ class DatabindJsonPropertySpec extends JsonPropertySpec {
))
}

@PendingFeature
void "test @JsonProperty.Access.READ_ONLY (get only) - constructor"() {
// Jackson cannot deserialize READ_ONLY as null
given:
def context = buildContext("""
package test;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.micronaut.serde.annotation.Serdeable;
@Serdeable
class Test {
private String value;
private String ignored;
@JsonCreator
public Test(@JsonProperty("value") String value, @JsonProperty(value = "ignored", access = JsonProperty.Access.READ_ONLY) String ignored) {
this.value = value;
this.ignored = ignored;
}
public String getValue() {
return this.value;
}
public String getIgnored() {
return this.ignored;
}
}
""")
when:
def bean = newInstance(context, 'test.Test', "test", "xyz")
def result = writeJson(jsonMapper, bean)

then:
result == '{"value":"test","ignored":"xyz"}'

when:
bean = jsonMapper.readValue('{"value":"test","ignored":"xyz"}', argumentOf(context, 'test.Test'))

then:
bean.value == 'test'
bean.ignored == null

cleanup:
context.close()
}

@PendingFeature
void "test @JsonProperty.Access.READ_ONLY (get only) - record"() {
// Jackson cannot deserialize READ_ONLY as null
given:
def context = buildContext("""
package test;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.micronaut.serde.annotation.Serdeable;
@Serdeable
record Test(
@JsonProperty
String value,
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
String ignored
) {}
""")
when:
def bean = newInstance(context, 'test.Test', "test", "xyz")
def result = writeJson(jsonMapper, bean)

then:
result == '{"value":"test","ignored":"xyz"}'

when:
bean = jsonMapper.readValue('{"value":"test","ignored":"xyz"}', argumentOf(context, 'test.Test'))

then:
bean.value == 'test'
bean.ignored == null

cleanup:
context.close()
}

void "test required primitive field"() {

given:
Expand Down

0 comments on commit 8dd9e6d

Please sign in to comment.