Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@JsonDeserialize prioritized over addDeserializer #592

Closed
JanStureNielsen opened this issue Apr 28, 2023 · 1 comment
Closed

@JsonDeserialize prioritized over addDeserializer #592

JanStureNielsen opened this issue Apr 28, 2023 · 1 comment

Comments

@JanStureNielsen
Copy link

Using Jackson Databind 2.13.5, annotating a POJO with @JsonDeserialize:

@Getter
@Builder
@ToString
@JsonDeserialize(builder=Person.PersonBuilder.class)
static class Person {
    private final String name;
    private final List<Pet> pets;

}

@Getter
@Builder
@ToString
static class Pet {
    private final String name;
    private final String type;

}

takes priority over an explicitly configured deserializer in a mapper:

String serializedPersons = """
name,pets
Ali,"[{'Cutesy','cat'}, {'Tootsie','cat'}]"
Jan,"[{'Mr. Bubbles','dog'}, {'Lilly','cat'}]"
Tom,"[{'Fester','dog'}, {'Wednesday','cat'}]"
Zoe,"[{'Jaeger','dog'}, {'Deira','cat'}]"
""";

CsvMapper mapper = new CsvMapper();
SimpleModule module = new SimpleModule();

module.addSerializer(Person.class, new PersonSerializer());
module.addDeserializer(Person.class, new PersonDeserializer());

mapper.registerModule(module);

CsvSchema schema = mapper.schemaFor(Person.class).withHeader();
ObjectReader reader = mapper.readerFor(Person.class).with(schema);

MappingIterator<Person> i = reader.readValues(serializedPersons);

while (i.hasNext()) {
    System.out.println("Person: " + i.next());
}

It seems like explicitly configured serializers & deserializers on the mappers should take precedence over others that are discovered.

In my case, I have network marshalled objects that should be using annotated builder, and CLI applications that should be using their own explicitly configured deserializers. I wrote a question on SO before I found the problem.


public class JacksonTest {
    @Test
    public void writeJson() throws JsonProcessingException {
        var mapper = new ObjectMapper();
        var writer = mapper.writer();

        System.out.println(writer.writeValueAsString(_single()));
        System.out.println(writer.writeValueAsString(_multiple()));
    }

    @Test
    public void writeJsonObjectsIndividually() throws IOException {
        var mapper = new ObjectMapper();
        var writer = new StringWriter();

        try (var seqWriter = mapper.writerFor(Person.class).writeValues(writer)) {
            seqWriter.write(_single());
            seqWriter.writeAll(_multiple());
        }
        System.out.println(writer.toString());
    }

    @Test
    public void writePersonToCsv() throws IOException {
        CsvMapper mapper = new CsvMapper();
        SimpleModule module = new SimpleModule();

        module.addSerializer(Person.class, new PersonSerializer());
        module.addDeserializer(Person.class, new PersonDeserializer());

        mapper.registerModule(module);
        var writer = new StringWriter();

        CsvSchema schema = mapper.schemaFor(Person.class)
                .withHeader();

        try (var seqWriter = mapper.writerWithSchemaFor(Person.class).with(schema).writeValues(writer)) {
            seqWriter.write(_single());
            seqWriter.writeAll(_multiple());
        }
        System.out.println(writer.toString());
    }

    @Test
    public void deserializePets() {
        String serializedPets = "[{'Mr. Bubbles','dog'}, {'Lilly','cat'}]";

        List<Pet> pets = PersonDeserializer.deserialize(serializedPets);

        assertThat(pets).hasSize(2);
    }

    @Test
    public void readPersonFromCsv() throws IOException {
        String serializedPersons = """
        name,pets
        Ali,"[{'Cutesy','cat'}, {'Tootsie','cat'}]"
        Jan,"[{'Mr. Bubbles','dog'}, {'Lilly','cat'}]"
        Tom,"[{'Fester','dog'}, {'Wednesday','cat'}]"
        Zoe,"[{'Jaeger','dog'}, {'Deira','cat'}]"
        """;

        CsvMapper mapper = new CsvMapper();
        SimpleModule module = new SimpleModule();

        module.addSerializer(Person.class, new PersonSerializer());
        module.addDeserializer(Person.class, new PersonDeserializer());

        mapper.registerModule(module);

        CsvSchema schema = mapper.schemaFor(Person.class).withHeader();
        ObjectReader reader = mapper.readerFor(Person.class).with(schema);

        MappingIterator<Person> i = reader.readValues(serializedPersons);

        while (i.hasNext()) {
            System.out.println("Person: " + i.next());
        }
    }

    @Getter
    @Builder
    @ToString
    //This will take precedence over mapper registered deserializers
    @JsonDeserialize(builder=Person.PersonBuilder.class)
    static class Person {
        private final String name;
        private final List<Pet> pets;

    }

    @Getter
    @Builder
    @ToString
    static class Pet {
        private final String name;
        private final String type;

    }

    static class PersonSerializer extends StdSerializer<Person> {
        private static final long serialVersionUID = 1L;

        public PersonSerializer() {
            this(Person.class);
        }

        public PersonSerializer(Class<Person> type) {
            super(type);
        }

        @Override
        public void serialize(Person value, JsonGenerator jgen, SerializerProvider provider)
          throws IOException, JsonProcessingException {
            jgen.writeStartObject();
            jgen.writeStringField("name", value.getName());
            jgen.writeStringField("pets", _toString(value.getPets()));
            jgen.writeEndObject();
        }

        private String _toString(List<Pet> pets) {
            return Arrays.toString(pets.stream()
                    .map(PersonSerializer::serialize)
                    .toArray(String[]::new));
        }

        private static String serialize(Pet pet) {
            return String.format("{'%s','%s'}", pet.name, pet.type);
        }

    }

    static class PersonDeserializer extends StdDeserializer<Person> {
        private static final long serialVersionUID = 1L;

        public PersonDeserializer() {
            this(Person.class);
        }

        public PersonDeserializer(Class<Person> type) {
            super(type);
        }

        @Override
        public Person deserialize(JsonParser p, DeserializationContext ctxt)
          throws IOException, JsonProcessingException {
            JsonNode node = p.getCodec().readTree(p);

            String name = node.get("name").asText();
            String petsSerialized = node.get("pets").asText();

            List<Pet> pets = deserialize(petsSerialized);

            return Person.builder().name(name).pets(pets).build();
        }

        private static List<Pet> deserialize(String serializedPets) {
            return Arrays.stream(serializedPets.replaceAll("^.\\{?|}?.$", "").split("},\s*\\{"))
                    .map(inner -> Arrays.stream(inner.replaceAll("^.'?|'?.$", "").split("','")).toList())
                    .map(l -> deserialize(l))
                    .collect(Collectors.toList());
        }

        private static Pet deserialize(List<String> l) {
            return Pet.builder().name(l.get(0)).type(l.get(1)).build();
        }
    }

    private static Person _single() {
        return Person.builder().name("Jan").pets(List.of(Pet.builder().name("Mr. Bubbles").type("dog").build(), Pet.builder().name("Lilly").type("cat").build())).build();
    }

    private static Person[] _multiple() {
        return List.of(_single(), _single(), _single()).toArray(Person[]::new);
    }

}
@JanStureNielsen JanStureNielsen changed the title @JsonDeserialize(builder=<class>) prioritized over addDeserializer(<instance>) @JsonDeserialize prioritized over addDeserializer Apr 29, 2023
@JanStureNielsen
Copy link
Author

Whoops -- wrong repo

@JanStureNielsen JanStureNielsen closed this as not planned Won't fix, can't repro, duplicate, stale Apr 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant