javajsonjacksonjackson-databindjackson2

how to make java class behave like a normal object and list-of-object at runtime and as per need?


I have a simple class in java like below:

class Simple {
   private String name;
   private String email;
}

I want to have behaviour of java.util.List<Simple> and Simple both according to input data that my program receives.

i.e.

Case 1:: if my program receives below kind of json-array input

{"simple" : [ {"name":"a", "email" : "a@z.com"}, {"name":"b", "email" : "b@z.com"} ]}

I need to parse it using List<Simple>

Case 2:: if my program receives below kind of json-object input

{"simple" : {"name":"c", "email" : "c@z.com"} }

I need to parse it using Simple

Note: I have tried using JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, but the problem is it is basically converting single value also into json-array at the time of writing json. I need to persist json as it is, is there any other way to achieve this?


Solution

  • To avoid any Jackson customisation I would create wrapper class with an Object simple property. We can add two extra checking methods and two extra casting methods. It will allow Jackson to do it's logic and in runtime we can check what actually we have:

    import com.fasterxml.jackson.annotation.JsonIgnore;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.databind.json.JsonMapper;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.util.Collections;
    import java.util.List;
    
    public class DateApp {
        private final static JsonMapper JSON_MAPPER = JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build();
    
        public static void main(String[] args) throws Exception {
            Simple object = new Simple("John", "john@doe.com");
            SimpleWrapper wrapper = new SimpleWrapper();
            wrapper.setSimple(object);
    
            serializeAndDeserialize(wrapper);
    
            wrapper.setSimple(Collections.singletonList(object));
            serializeAndDeserialize(wrapper);
        }
    
        private static void serializeAndDeserialize(SimpleWrapper wrapper) throws JsonProcessingException {
            String json = JSON_MAPPER.writeValueAsString(wrapper);
            System.out.println("JSON:");
            System.out.println(json);
    
            wrapper = JSON_MAPPER.readValue(json, SimpleWrapper.class);
            System.out.println("Wrapper:");
            System.out.println(wrapper);
        }
    }
    
    @Data
    class SimpleWrapper {
        private Object simple;
    
        @JsonIgnore
        public boolean isSimpleObject() {
            return simple instanceof Simple;
        }
    
        @JsonIgnore
        public boolean isSimpleList() {
            return simple instanceof List;
        }
    
        @JsonIgnore
        public Simple getSimpleAsObject() {
            return (Simple) simple;
        }
    
        @JsonIgnore
        public List<Simple> getSimpleAsList() {
            return (List<Simple>) simple;
        }
    }
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    class Simple {
        private String name;
        private String email;
    }
    

    Above code prints:

    JSON:
    {
      "simple" : {
        "name" : "John",
        "email" : "john@doe.com"
      }
    }
    Wrapper:
    SimpleWrapper(simple={name=John, email=john@doe.com})
    
    JSON:
    {
      "simple" : [ {
        "name" : "John",
        "email" : "john@doe.com"
      } ]
    }
    Wrapper:
    SimpleWrapper(simple=[{name=John, email=john@doe.com}])