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

Time:10-05

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" : "[email protected]"}, {"name":"b", "email" : "[email protected]"} ]}

I need to parse it using List<Simple>

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

{"simple" : {"name":"c", "email" : "[email protected]"} }

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?

CodePudding user response:

Jackson is able to parse any json into a map where value is any object. you can then inquire on the type of the map value

Map<String, Object> map = new ObjectMapper().readValue(jsonInput, Map.class);
Object value = map.get("simple");
if (value instanceof Collection) { // will return false for null
    Collection<Simple> simples = (Collection<Simple>)value;
}
else if (value instanceof Simple) {
    Simple simple = (Simple)value;
}
else {
    System.err.println("unrecognized");
}

CodePudding user response:

You can use JsonNode.isArray() (or JsonNode.isObject()) to perform this check.

Then you can parse the node into a list with ObjectReader.readValue() or into a POJO using ObjectMapper.treeToValue().

String myJson = """
    {"simple" : {"name":"c", "email" : "[email protected]"} }
""";
    
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(myJson);

if (node.isArray()) {
    ObjectReader reader = mapper.readerFor(new TypeReference<List<Simple>>() {});
    List<Simple> list = reader.readValue(node);

    // do something with a list
} else {
    Simple pojo = mapper.treeToValue(node, Simple.class);

    // do something else with a single object
}

CodePudding user response:

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", "[email protected]");
        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" : "[email protected]"
  }
}
Wrapper:
SimpleWrapper(simple={name=John, [email protected]})

JSON:
{
  "simple" : [ {
    "name" : "John",
    "email" : "[email protected]"
  } ]
}
Wrapper:
SimpleWrapper(simple=[{name=John, [email protected]}])

CodePudding user response:

You only need to read the first node, simple and check if it is an array - using isArray() method.

public class Main{
    public static void main(String args[]) {
        
        //String inputString = [your input];
        
        ObjectMapper mapper = new ObjectMapper();
    
        JsonNode root = mapper.readTree(inputString);
    
        JsonNode simpleNode = root.findPath("simple");
    
        if(simpleNode.isArray()) {
    
           //you have an array

        } else {
    
        // you have an element
    
        }

    }
}
  • Related