Custom mapper for conditional map serialization

32 views
Skip to first unread message

Viktor Remennik

unread,
Apr 26, 2024, 10:08:47 PMApr 26
to jackson-user
Hi there!

I hope it's a simple question but I cannot find an answer myself. So, asking for help.
I need to implement custom map serializer when mam mets some condition. the problem is, that I need to replace the map with a primitive type, e.g. string. As a simple example - when map conforms to some rule I want to replace it with, let's say, string value. I understand there could be at least one case when primitive, like string, cannot be used witout a key. It's probably if the map is the root node.Maybe I could generate a key for such a case? 
Anyways, I cannot achieve it in any way I tried. Here's the MVE for my question. I want to make it producing this:
{
  "Second" : "Two",
  "Third" : "magic string",
  "First" : 1
}

instead of default:

{
  "Second" : "Two",
  "Third" : {
    "Eleventh" : 11,
    "Twelfth" : "12"
  },
  "First" : 1
}

Here's the mve itself. Thank you!


public class MapParseTest

{

    private final ObjectMapper mapper;


    public MapParseTest()

    {

        mapper = new ObjectMapper();

        SimpleModule module = new SimpleModule();

        module.setSerializerModifier(new MyMapSerializerModifier());

        mapper.registerModule(module);

    }


    public void doTest() throws JsonProcessingException

    {

        MyMap map1 = new MyMap();

        MyMap map2 = new MyMap();


        map1.setName("shtame");

        map2.setName("magic name");


        map1.put("First", 1);

        map1.put("Second", "Two");

        map1.put("Third", map2);


        map2.put("Eleventh", 11);

        map2.put("Twelfth", "12");


        String result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(map1);

        System.out.println(result);

    }


    public static void main(String[] args) throws JsonProcessingException

    {

        MapParseTest test = new MapParseTest();

        test.doTest();

    }


    @Setter

    @Getter

    public static class MyMap extends HashMap<String, Object>

    {

        private String name;

    }


    public static class MyMapSerializer extends JsonSerializer<MyMap>

    {

        private final JsonSerializer<Object> defaultSerializer;


        public MyMapSerializer(JsonSerializer<Object> defaultSerializer)

        {

            this.defaultSerializer = defaultSerializer;

        }


        @Override

        public void serialize(MyMap value,

                              JsonGenerator gen,

                              SerializerProvider provider) throws IOException

        {

            String className = value.getName();

            if (className.equalsIgnoreCase("magic name"))

            {

                gen.writeString("magic string");

            } else

            {

                defaultSerializer.serialize(value, gen, provider);

            }

        }

    }


    public static class MyMapSerializerModifier extends BeanSerializerModifier

    {

        @Override

        public JsonSerializer<?> modifyMapSerializer(SerializationConfig config,

                                                     MapType mapType,

                                                     BeanDescription beanDesc,

                                                     JsonSerializer<?> serializer)

        {

            Class<?> type = beanDesc.getBeanClass();

            if (type.equals(MyMap.class))

            {

                return new MyMapSerializer((JsonSerializer<Object>) serializer);

            }

            return serializer;

        }

    }

}




Mantas Gridinas

unread,
Apr 27, 2024, 11:27:30 AMApr 27
to jackson-user
Is @JsonSerialize still around? You could annotate fields with that annotation and jackson would delegate serializing those fields to that particular serializer. But your code would remain largely the same - you'd still need to check what to write depending on the field. I suppose you could cheat by having that field be some value interface type Foo, and implementations assigned to that field be FooMap or FooString, where FooString would have dedicated to string serializer. Deserializing on the other hand of such structures is painful, but nothing you couldn't handle with @JsonDeserialize.

Mantas Gridinas

unread,
Apr 27, 2024, 11:30:33 AMApr 27
to jackson-user

Viktor Remennik

unread,
Apr 27, 2024, 11:41:44 AMApr 27
to jackson-user
Thank you for your reply. Yep, my fault, it's just a mve, I actually receive an 'Object o' which I need to serialize, that's why i doubt i could annotate it as it is received from external source. I mean, i cannot annotate fields of this object and would like to avoid modifying it as it should be used further in the program. I don't need deserialization, just serialization.
Reply all
Reply to author
Forward
0 new messages