writevalueasstring serialize deserialize java json serialization jackson

java - serialize - Deserialización dinámica Json con jackson



objectmapper java (2)

Ya he echado un vistazo a la pregunta " Nombres de propiedades dinámicos de Jackson " pero realmente no responde a mi pregunta.

Quiero deserializar algo como esto:

public class Response<T> { private String status; private Error error; private T data; }

pero los datos pueden tener diferentes nombres dado que existen diferentes servicios y devuelven la misma estructura con algunos datos diferentes. Por ejemplo, ''usuario'' y ''contrato'':

{ response: { status: "success", user: { ... } } }

o

{ response: { status: "failure", error : { code : 212, message : "Unable to retrieve contract" } contract: { ... } } }

Me gustaría genéricos mis respuestas objetos como este:

public class UserResponse extends Response<User> {}

He intentado lo siguiente, pero no estoy seguro de que sea mi caso de uso o si no lo utilizo en el buen sentido:

@JsonTypeInfo(include = As.WRAPPER_OBJECT, use = Id.CLASS) @JsonSubTypes({@Type(value = User.class, name = "user"), @Type(value = Contract.class, name = "contract")})

Finalmente, he creado un deserializador personalizado. Funciona pero no estoy satisfecho:

public class ResponseDeserializer extends JsonDeserializer<Response> { @Override public Response deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { ObjectMapper mapper = new ObjectMapper(); Response responseData = new Response(); Object data = null; for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) { String propName = jp.getCurrentName(); // Skip field name: jp.nextToken(); if ("contract".equals(propName)) { data = mapper.readValue(jp, Contract.class); } else if ("user".equals(propName)) { data = mapper.readValue(jp, User.class); } else if ("status".equals(propName)) { responseData.setStatus(jp.getText()); } else if ("error".equals(propName)) { responseData.setError(mapper.readValue(jp, com.ingdirect.dg.business.object.community.api.common.Error.class)); } } if (data instanceof Contract) { Response<Contract> response = new Response<Ranking>(responseData); return response; } if (data instanceof User) { Response<User> response = new Response<User>(responseData); return response; } // in all other cases, the type is not yet managed, add it when needed throw new JsonParseException("Cannot parse this Response", jp.getCurrentLocation()); }

}

¿Alguna idea de hacer esto limpio con anotaciones? Gracias por adelantado !


Has probado:

public class AnyResponse { private String status; private Error error; private Contract contract; private User user; // And all other possibilities. } // ... mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);

Esto debería completar cualquier objeto que aparezca en el JSON y dejar el resto nulo.

Luego puede completar una Respuesta con el objeto relevante.


El marco de Jackson proporciona soporte integrado para tipos dinámicos.

//Base type @JsonTypeInfo(property = "type", use = Id.NAME) @JsonSubTypes({ @Type(ValidResponse.class), @Type(InvalidResponse.class) }) public abstract class Response<T> { } //Concrete type 1 public class ValidResponse extends Response<T>{ } //Concrete type 2 public class InvalidResponse extends Response<T>{ } main { ObjectMapper mapper = new ObjectMapper(); //Now serialize ValidResponse response = (ValidResponse)(mapper.readValue(jsonString, Response.class)); //Deserialize String jsonString = mapper.writeValueAsString(response); }