1

I have a JAVA POJO which has many fields. One of the fields is Map<String, Object> for which I am using the Custom JsonSerializer as it can have many type of Objects. All I want to know is how can I avoid the Serialization of the fieldname only for this Map<String,Object> field. For all other fields in POJO, I would like to have the field name but only for this, I want to remove it.

As of now when use Jackson searlizer then I get the following output:

{
  "isA" : "Human",
  "name" : "Batman",
  "age" : "2008",
  "others" : {
    "key1" : "value1",
    "key2" : {
      "key3" : "value3"
    },
    "key5" : {
      "key4" : "One",
      "key4" : "Two"
    }
  }
}

I want to get the following output: (All I want to do is remove the Map<String,Object> field name but keep its children.)

{
  "isA" : "Human",
  "name" : "Batman",
  "age" : "2008",
  "key1" : "value1",
  "key2" : {
    "key3" : "value3"
  },
  "key5" : {
    "key4" : "One",
    "key4" : "Two"
  }
}

Following is my Human.class POJO which is used by ObjectMapper:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter
@Setter
@NoArgsConstructor
@ToString
class Human {
    private String isA;
    private String name;
    private String age;

    @JsonSerialize(using = MyCustomSearlize.class)
    private Map<String, Object> others = new HashMap<>();
}

Following is my Custom searlizer which is used by MAP during searlization:

class MyCustomSearlize extends JsonSerializer<Map<String, Object>> {

    private static final ObjectMapper mapper = new ObjectMapper();

    @Override
    public void serialize(Map<String, Object> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeStartObject();
        recusiveSerializer(value, gen, serializers);
        gen.writeEndObject();
    }

    public void recusiveSerializer(Map<String, Object> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        for (Map.Entry<String, Object> extension : value.entrySet()) {
            if (extension.getValue() instanceof Map) {
                //If instance is MAP then call the recursive method
                gen.writeFieldName(extension.getKey());
                gen.writeStartObject();
                recusiveSerializer((Map) extension.getValue(), gen, serializers);
                gen.writeEndObject();
            } else if (extension.getValue() instanceof String) {
                //If instance is String directly add it to the JSON
                gen.writeStringField(extension.getKey(), (String) extension.getValue());
            } else if (extension.getValue() instanceof ArrayList) {
                //If instance if ArrayList then loop over it and add it to the JSON after calling recursive method
                for (Object dupItems : (ArrayList<Object>) extension.getValue()) {
                    if (dupItems instanceof Map) {
                        gen.writeFieldName(extension.getKey());
                        gen.writeStartObject();
                        recusiveSerializer((Map) dupItems, gen, serializers);
                        gen.writeEndObject();
                    } else {
                        gen.writeStringField(extension.getKey(), (String) dupItems);
                    }
                }
            }
        }
    }
}


Following is my Main class:

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

        Human person = new Human();

        person.setName("Batman");
        person.setAge("2008");
        Map<String, Object> others = new HashMap<>();
        others.put("key1", "value1");
        Map<String, Object> complex = new HashMap<>();
        complex.put("key3", "value3");
        others.put("key2", complex);
        Map<String, Object> complex2 = new HashMap<>();
        List<String> dup = new ArrayList<>();
        dup.add("One");
        dup.add("Two");
        complex2.put("key4", dup);
        others.put("key5", complex2);
        person.setOthers(others);

        final ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        objectMapper.registerModule(simpleModule);
        final String jsonEvent = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(person);
        System.out.println(jsonEvent);
    }
}

Following things I tried:

  1. I tried to add @JsonValue on the Map but this will remove all my other values (name,age, isA, etc.)
  2. I tried the @JsonAnyGetter this works for Map and String but does not work for ArrayList as I want. I am handling the ArrayList bit differently in my application as a part of my requirement.

Is there a way to may it work with @JsonSerialize and @JsonAnyGetter because I am unable to use both together.

Can someone please help in solving this issue? Please guide me to appropriate documentation or workaround thanks a lot.

BATMAN_2008
  • 2,788
  • 3
  • 31
  • 98

2 Answers2

2

From the wiki page it sounds like the @JsonUnwrapped annotation should do what you want.

@JsonUnwrapped: property annotation used to define that value should be "unwrapped" when serialized (and wrapped again when deserializing), resulting in flattening of data structure, compared to POJO structure.

The Javadoc for the class also has an example that looks appropriate.

Hitobat
  • 2,847
  • 1
  • 16
  • 12
  • Thanks a lot for your quick response and example. I tried few more things and got it working using `@XmlAnyGetter` and `@JsonSearlize`. – BATMAN_2008 Jun 09 '21 at 19:55
  • I will accept this answer as you were quick and provided some useful docs and examples. However, I will also post my answer so it can be helpful to someone in the future. Thanks again for the response. Have a great day :) – BATMAN_2008 Jun 09 '21 at 19:57
0

As mentioned in another answer @JsonUnwrapped might work but I used the following approach to get it working. Posting here as it can be helpful to someone in the future:

I added the @JsonAnyGetter and @JsonSearlize on the Getter method of Map and got it to work.

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter
@Setter
@NoArgsConstructor
@ToString
class Human {
    private String isA;
    private String name;
    private String age;

    @JsonIgnore
    private Map<String, Object> others = new HashMap<>();

    @JsonAnyGetter
    @JsonSerialize(using = MyCustomSearlize.class)
    public Map<String,Object> getOther(){
        return others;
    }
}

In the MyCustomSearlize class code I removed the start and end object

    @Override
    public void serialize(Map<String, Object> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        recusiveSerializer(value, gen, serializers);
    }
BATMAN_2008
  • 2,788
  • 3
  • 31
  • 98