Elección dinámica de qué clase de objeto de crear a partir de JSON

b15:

Tengo un problema interesante que estoy teniendo problemas para llegar a una solución limpia para. Mi aplicación lee colecciones de objetos JSON que necesita para deserializar a tal o cual tipo de clase basado en un campo en el propio JSON. No tengo ningún control sobre la estructura JSON o cómo llega a mi solicitud.

He creado modelos para cada tipo de objeto que podría estar llegando a la aplicación y he llegado a un punto en el que estoy tratando de construir un servicio que se saca el campo 'tipo' y luego utiliza ObjectMapper deserializar el JSON para el apropiado modelo.

Json ejemplo:

{
    "message_type" : "model1"
    "other data" : "other value"
    ...
}

modelos:

public class Model1 {
    ...
}

public class Model2 {
    ...
}

¿Servicio?:

public class DynamicMappingService {

    public ???? mapJsonToObject(String json) {
        String type = pullTypeFromJson();

        ???
    }

    private String pullTypeFromJson() {...}
}

No quiero una sentencia switch masiva que dice "Si el valor tipo es esto, entonces deserializar a que" pero estoy luchando para llegar a algo limpio que hace eso. Pensé que tal vez una clase de modelo genérico en el que el parámetro genérico es el tipo de modelo y el único campo es la instancia de ese tipo de modelo, pero que no parece correcto tampoco y no estoy seguro de lo que me compra. También podría tener algún tipo de clase abstracta vacía que todos los modelos se extienden pero que parece terrible también. ¿Cómo puedo lidiar con esto? Puntos extra para un ejemplo.

Conffusion:

Yo uso el concepto de un vehículo interfaz de padre con 2 clases de coches y camiones. En caso de que esto se Model1 medios y model2 debe implementar una interfaz común.

Mi clase de prueba:

import com.fasterxml.jackson.databind.ObjectMapper;

public class Tester {
    static ObjectMapper mapper=new ObjectMapper();

    public static void main(String[] args) throws IOException {
        Car car = new Car();
        car.setModel("sedan");
        String jsonCar=mapper.writeValueAsString(car);
        System.out.println(jsonCar);
        Vehicle c=mapper.readValue(jsonCar, Vehicle.class);
        System.out.println("Vehicle of type: "+c.getClass().getName());

        Truck truck=new Truck();
        truck.setPower(100);
        String jsonTruck=mapper.writeValueAsString(truck);
        System.out.println(jsonTruck);
        Vehicle t=mapper.readValue(jsonTruck, Vehicle.class);
        System.out.println("Vehicle of type: "+t.getClass().getName());
    }
}

En algún lugar tendrá que guardar una correspondencia entre el valor del campo de tipo y la clase correspondiente. Dependiendo de la ubicación en la que desea que esta aplicación es diferente.

1) El tipo de matriz posee la lista de subtipos:

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonSubTypes({
    @JsonSubTypes.Type(value = Car.class, name = "car"),
    @JsonSubTypes.Type(value = Truck.class, name = "truck") }
)
@JsonTypeInfo(
          use = JsonTypeInfo.Id.NAME, 
          include = JsonTypeInfo.As.PROPERTY, 
          property = "type")
public interface Vehicle {
}

Los modelos de coches y camiones son simples POJO sin anotaciones:

public class Car implements Vehicle {
    private String model;

    public String getModel() {
        return model;
    }

    public void setModel(String model) {
        this.model = model;
    }
}

2) Una resolución separada sostiene el mapeo:

Vehículo contiene la anotación adicional @JsonTypeIdResolver

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;

@JsonTypeInfo(
          use = JsonTypeInfo.Id.NAME, 
          include = JsonTypeInfo.As.PROPERTY, 
          property = "type")
@JsonTypeIdResolver(JsonResolver.class)
public interface Vehicle {
}

La clase JsonResolver mantiene la correspondencia entre el valor del campo tipo y la clase:

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;

public class JsonResolver extends TypeIdResolverBase {

    private static Map<String,Class<?>> ID_TO_TYPE=new HashMap<>();
    static {
        ID_TO_TYPE.put("car",Car.class);
        ID_TO_TYPE.put("truck",Truck.class);
    }
    public JsonResolver() {
        super();
    }

    @Override
    public Id getMechanism() {
        return null;
    }

    @Override
    public String idFromValue(Object value) {
        return value.getClass().getSimpleName();
    }

    @Override
    public String idFromValueAndType(Object value, Class<?> arg1) {
        return idFromValue(value);
    }

    @Override
    public JavaType typeFromId(DatabindContext context, String id) {
        return context.getTypeFactory().constructType(ID_TO_TYPE.get(id));
    }
}

3) El JSON contiene el nombre completo de la clase:

Si acepta el JSON serializado tiene el nombre completo de la clase Java, que no es necesario un dispositivo de resolución, sino especificar use = JsonTypeInfo.Id.CLASS:

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;

@JsonTypeInfo(
          use = JsonTypeInfo.Id.CLASS, 
          include = JsonTypeInfo.As.PROPERTY, 
          property = "type")
public interface Vehicle {
}

Solución 3 es el más fácil de aplicar, pero personalmente no me gusta la idea de tener nombres de clases Java de pleno derecho en mis datos. Puede ser un riesgo potencial si se empieza a refactorizar sus paquetes java.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=230829&siteId=1
Recomendado
Clasificación