Skip to content

Commit

Permalink
Backport enum support
Browse files Browse the repository at this point in the history
Improve Enum handling parity with Jackson (#818)
  • Loading branch information
oujesky authored and andriy-dmytruk committed Jan 15, 2025
1 parent ab3573f commit f10a773
Show file tree
Hide file tree
Showing 3 changed files with 397 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.micronaut.serde.jackson.object

import io.micronaut.serde.AbstractJsonCompileSpec
import spock.lang.Ignore
import spock.lang.Issue

class EnumSerdeSpec extends AbstractJsonCompileSpec {
Expand Down Expand Up @@ -118,6 +119,7 @@ enum Foo {
compiled.close()
}

@Ignore
def "test null value handling"() {
given:
def compiled = buildContext('''
Expand Down Expand Up @@ -166,4 +168,283 @@ enum EnumWithDefaultValue {
cleanup:
compiled.close()
}

def 'test deserialize EnumSet for Enum with @JsonValue on property'() {
given:
def context = buildContext('''
package test;
import com.fasterxml.jackson.annotation.JsonValue;
import io.micronaut.serde.annotation.Serdeable;
import java.util.EnumSet;
@Serdeable
class Test {
private EnumSet<MyEnum> enumSet;
public EnumSet<MyEnum> getEnumSet() {
return enumSet;
}
public void setEnumSet(EnumSet<MyEnum> enumSet) {
this.enumSet = enumSet;
}
}
@Serdeable
enum MyEnum {
VALUE1("value_1"),
VALUE2("value_2"),
VALUE3("value_3");
@JsonValue
private final String value;
MyEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
''')
when:
def json = '{"enumSet":["value_1","value_3"]}'
def result = jsonMapper.readValue(json, argumentOf(context, 'test.Test'))

then:
result.enumSet instanceof EnumSet
result.enumSet == EnumSet.of(getEnum(context, 'test.MyEnum.VALUE1'), getEnum(context, 'test.MyEnum.VALUE3'))

cleanup:
context.close()
}

def 'test deserialize EnumSet for Enum with @JsonValue on getter'() {
given:
def context = buildContext('''
package test;
import com.fasterxml.jackson.annotation.JsonValue;
import io.micronaut.serde.annotation.Serdeable;
import java.util.EnumSet;
@Serdeable
class Test {
private EnumSet<MyEnum> enumSet;
public EnumSet<MyEnum> getEnumSet() {
return enumSet;
}
public void setEnumSet(EnumSet<MyEnum> enumSet) {
this.enumSet = enumSet;
}
}
@Serdeable
enum MyEnum {
VALUE1("value_1"),
VALUE2("value_2"),
VALUE3("value_3");
private final String value;
MyEnum(String value) {
this.value = value;
}
@JsonValue
public String getValue() {
return value;
}
}
''')
when:
def json = '{"enumSet":["value_1","value_3"]}'
def result = jsonMapper.readValue(json, argumentOf(context, 'test.Test'))

then:
result.enumSet instanceof EnumSet
result.enumSet == EnumSet.of(getEnum(context, 'test.MyEnum.VALUE1'), getEnum(context, 'test.MyEnum.VALUE3'))

cleanup:
context.close()
}

def 'test deserialize EnumSet for Enum with @JsonCreator'() {
given:
def context = buildContext('''
package test;
import com.fasterxml.jackson.annotation.JsonCreator;
import io.micronaut.serde.annotation.Serdeable;
import java.util.Objects;
import java.util.Arrays;
import java.util.EnumSet;
@Serdeable
class Test {
private EnumSet<MyEnum> enumSet;
public EnumSet<MyEnum> getEnumSet() {
return enumSet;
}
public void setEnumSet(EnumSet<MyEnum> enumSet) {
this.enumSet = enumSet;
}
}
@Serdeable
enum MyEnum {
VALUE1("value_1"),
VALUE2("value_2"),
VALUE3("value_3");
private final String value;
MyEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@JsonCreator
public static MyEnum create(String value) {
return Arrays.stream(values())
.filter(val -> Objects.equals(val.value, value))
.findFirst()
.orElse(null);
}
}
''')
when:
def json = '{"enumSet":["value_1","value_3"]}'
def result = jsonMapper.readValue(json, argumentOf(context, 'test.Test'))

then:
result.enumSet instanceof EnumSet
result.enumSet == EnumSet.of(getEnum(context, 'test.MyEnum.VALUE1'), getEnum(context, 'test.MyEnum.VALUE3'))

cleanup:
context.close()
}

void "enum @JsonValue property"() throws IOException {
given:
def context = buildContext('''
package example;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;import io.micronaut.serde.annotation.Serdeable;
@Serdeable
class Foo {
private final MyEnum myEnum;
@JsonCreator
Foo(@JsonProperty("myEnum") MyEnum myEnum) {
this.myEnum = myEnum;
}
public MyEnum getMyEnum() {
return myEnum;
}
}
@Serdeable
enum MyEnum {
VALUE1("value_1"),
VALUE2("value_2"),
VALUE3("value_3");
private final String value;
MyEnum(String value) {
this.value = value;
}
@JsonValue
public String getValue() {
return value;
}
}
''')
def enumValue2 = context.classLoader.loadClass('example.MyEnum').VALUE2
def testBean = newInstance(context, 'example.Foo', enumValue2)
when:
String json = jsonMapper.writeValueAsString(testBean)
then:
'{"myEnum":"value_2"}' == json

when:
def foo = jsonMapper.readValue(json, testBean.class)

then:
foo.myEnum == enumValue2

when:
jsonMapper.readValue('{"myEnum":"invalid"}', testBean.class)
then:
thrown IOException
}

void "enum @JsonValue on field"() throws IOException {
given:
def context = buildContext('''
package example;
import io.micronaut.serde.annotation.Serdeable;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
@Serdeable
class Foo {
private final MyEnum myEnum;
@JsonCreator
Foo(@JsonProperty("myEnum") MyEnum myEnum) {
this.myEnum = myEnum;
}
public MyEnum getMyEnum() {
return myEnum;
}
}
@Serdeable
enum MyEnum {
VALUE1("value_1"),
VALUE2("value_2"),
VALUE3("value_3");
@JsonValue
private final String value;
MyEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
''')
def enumValue2 = context.classLoader.loadClass('example.MyEnum').VALUE2
def testBean = newInstance(context, 'example.Foo', enumValue2)
when:
String json = jsonMapper.writeValueAsString(testBean)
then:
'{"myEnum":"value_2"}' == json

when:
def foo = jsonMapper.readValue(json, testBean.class)

then:
foo.myEnum == enumValue2

when:
jsonMapper.readValue('{"myEnum":"invalid"}', testBean.class)
then:
thrown IOException
}
}
Loading

0 comments on commit f10a773

Please sign in to comment.