2
votes

I have a class like this:

public class DeserializedHeader
    int typeToClassId;
    Object obj

I know what type of object obj is based on the typeToClassId, which is unfortunately only known at runtime.

I want to parse obj out based on typeToClassId - what's the best approach here? Annotations seem like they're out, and something based on ObjectMapper seems right, but I'm having trouble figuring out what the best approach is likely to be.

Something along the lines of Class clazz = lookUpClassBasedOnId(typeToClassId) objectMapper.readValue(obj, clazz)

Obviously, this doesn't work since obj is already deserialized... but could I do this in 2 steps somehow, perhaps with convertValue?

1
This is pretty smelly design. Why do you have the type as Object? Why is it only known at runtime? - Vivin Paliath
Can you create a Map<Integer, Class<?>> in runtime? I'm not sure what you have and what do you need. Could you create a simple example with JSON and POJO classes? - Michał Ziober
Definitely agree about the code smell - it's a third party service, however, so I don't really get to control this. – Basically, after parsing the typeToClassId field, I'll know what type of object will be in the obj field. - secureboot

1 Answers

3
votes

This is really complex and painful problem. I do not know any sophisticated and elegant solution, but I can share with you my idea which I developed. I have created example program which help me to show you how you can solve your problem. At the beginning I have created two simple POJO classes:

class Product {

    private String name;

        // getters/setters/toString
}

and

class Entity {

    private long id;

    // getters/setters/toString
}

Example input JSON for those classes could look like this. For Product class:

{
  "typeToClassId" : 33,
  "obj" : {
    "name" : "Computer"
  }
}

and for Entity class:

{
  "typeToClassId" : 45,
  "obj" : {
    "id" : 10
  }
}

The main functionality which we want to use is "partial serializing/deserializing". To do this we will enable FAIL_ON_UNKNOWN_PROPERTIES feature on ObjectMapper. Now we have to create two classes which define typeToClassId and obj properties.

class HeaderType {

    private int typeToClassId;

    public int getTypeToClassId() {
        return typeToClassId;
    }

    public void setTypeToClassId(int typeToClassId) {
        this.typeToClassId = typeToClassId;
    }

    @Override
    public String toString() {
        return "HeaderType [typeToClassId=" + typeToClassId + "]";
    }
}

class HeaderObject<T> {

    private T obj;

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }

    @Override
    public String toString() {
        return "HeaderObject [obj=" + obj + "]";
    }
}

And, finally source code which can parse JSON:

// Simple binding
Map<Integer, Class<?>> classResolverMap = new HashMap<Integer, Class<?>>();
classResolverMap.put(33, Product.class);
classResolverMap.put(45, Entity.class);

ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

String json = "{...}";

// Parse type
HeaderType headerType = mapper.readValue(json, HeaderType.class);
// Retrieve class by integer value
Class<?> clazz = classResolverMap.get(headerType.getTypeToClassId());
// Create dynamic type
JavaType type = mapper.getTypeFactory().constructParametricType(HeaderObject.class, clazz);
// Parse object
HeaderObject<?> headerObject = (HeaderObject<?>) mapper.readValue(json, type);
// Get the object
Object result = headerObject.getObj();

System.out.println(result);

Helpful links:

  1. How To Convert Java Map To / From JSON (Jackson).
  2. java jackson parse object containing a generic type object.