Skip to content

Instantly share code, notes, and snippets.

@youribonnaffe
Last active December 21, 2020 13:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save youribonnaffe/03176be516c0ed06828ccc7d6c1724ce to your computer and use it in GitHub Desktop.
Save youribonnaffe/03176be516c0ed06828ccc7d6c1724ce to your computer and use it in GitHub Desktop.
Java 14 Record (JEP 359) deserialization with Jackson
package com.github.youribonnaffe.jackson;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyName;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.util.StdConverter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
class RecordTest {
private ObjectMapper mapper;
@BeforeEach
void setUp() {
JacksonAnnotationIntrospector implicitRecordAI = new JacksonAnnotationIntrospector() {
@Override
public PropertyName findNameForDeserialization(Annotated a) {
PropertyName nameForDeserialization = super.findNameForDeserialization(a);
// when @JsonDeserialize is used, USE_DEFAULT is default
// preventing the implicit constructor to be found
if (PropertyName.USE_DEFAULT.equals(nameForDeserialization)
&& a instanceof AnnotatedParameter
&& ((AnnotatedParameter) a).getDeclaringClass().isRecord()) {
String str = findImplicitPropertyName((AnnotatedParameter) a);
if (str != null && !str.isEmpty()) {
return PropertyName.construct(str);
}
}
return nameForDeserialization;
}
@Override
public String findImplicitPropertyName(AnnotatedMember m) {
if (m.getDeclaringClass().isRecord()
&& m instanceof AnnotatedParameter parameter) {
return m.getDeclaringClass().getRecordComponents()[parameter.getIndex()].getName();
}
return super.findImplicitPropertyName(m);
}
};
mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(implicitRecordAI);
}
@Test
void readValue_record() throws JsonProcessingException {
String json = """
{
"id" : 123,
"name" : "Bob"
}""";
MyRecord record = mapper.readValue(json, MyRecord.class);
assertEquals(123, record.id());
assertEquals("Bob", record.name());
}
record MyRecord(@JsonProperty("id")int id,
@JsonProperty("name")String name) {
}
@Test
void readValue_record_customDeserialize() throws JsonProcessingException {
String json = """
{
"id":1,
"name":"name1",
"alias": "alias1,alias2"
}
""";
Person record = mapper.readValue(json, Person.class);
assertEquals(1, record.id());
assertEquals("name1", record.name());
assertEquals(List.of("alias1", "alias2"), record.alias());
}
@JsonIgnoreProperties(ignoreUnknown = true)
record Person(
Integer id,
String name,
@JsonDeserialize(converter = AliasConverter.class)
List<String>alias
) {
@JsonProperty("alias")
public String jsonGetAlias() {
return String.join(",", alias);
}
}
static class AliasConverter extends StdConverter<String, List<String>> {
@Override
public List<String> convert(String value) {
return Arrays.stream(value.split(",")).collect(Collectors.toList());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment