Tengo una cadena json, que debo deserializar a la siguiente clase
class Data <T> {
int found;
Class<T> hits
}
¿Cómo lo hago? Esta es la forma habitual
mapper.readValue(jsonString, Data.class);
Pero, ¿cómo menciono lo que significa T?
Tengo una cadena json, que debo deserializar a la siguiente clase
class Data <T> {
int found;
Class<T> hits
}
¿Cómo lo hago? Esta es la forma habitual
mapper.readValue(jsonString, Data.class);
Pero, ¿cómo menciono lo que significa T?
Respuestas:
Debe crear un TypeReference
objeto para cada tipo genérico que use y usarlo para la deserialización. Por ejemplo -
mapper.readValue(jsonString, new TypeReference<Data<String>>() {});
Data<T>
, eso NO es un tipo. Debe especificar la clase real; de lo contrario es lo mismo que Data<Object>
.
TypeReference
? ¿es com.fasterxml.jackson.core.type
?
No puede hacer eso: debe especificar el tipo totalmente resuelto, como Data<MyType>
. T
es solo una variable, y como no tiene sentido.
Pero si quiere decir que T
se sabrá, pero no estáticamente, debe crear un equivalente de TypeReference
forma dinámica. Otras preguntas a las que se hace referencia ya pueden mencionar esto, pero debería verse así:
public Data<T> read(InputStream json, Class<T> contentClass) {
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, contentClass);
return mapper.readValue(json, type);
}
TypeReference
: return mapper.readValue(json, clazz);
¿Cuál es exactamente el problema aquí?
TypeFactory
... Editaré mi respuesta.
Lo primero que debe hacer es serializar, luego puede deserializar.
así que cuando serialices, deberías usar @JsonTypeInfo
para dejar que Jackson escriba información de clase en tus datos json Lo que puedes hacer es así:
Class Data <T> {
int found;
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
Class<T> hits
}
Luego, cuando deserialice, encontrará que Jackson ha deserializado sus datos en una clase en la que sus aciertos variables están en tiempo de ejecución.
Para la clase Datos <>
ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametrizedType(Data.class, Data.class, Parameter.class);
Data<Parameter> dataParam = mapper.readValue(jsonString,type)
Simplemente escriba un método estático en la clase Util. Estoy leyendo un Json de un archivo. puedes darle String también a readValue
public static <T> T convertJsonToPOJO(String filePath, Class<?> target) throws JsonParseException, JsonMappingException, IOException, ClassNotFoundException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(new File(filePath), objectMapper .getTypeFactory().constructCollectionType(List.class, Class.forName(target.getName())));
}
Uso:
List<TaskBean> list = Util.<List<TaskBean>>convertJsonToPOJO("E:/J2eeWorkspaces/az_workspace_svn/az-client-service/dir1/dir2/filename.json", TaskBean.class);
Puede envolverlo en otra clase que conozca el tipo de su tipo genérico.
P.ej,
class Wrapper {
private Data<Something> data;
}
mapper.readValue(jsonString, Wrapper.class);
Aquí algo es un tipo concreto. Necesita un contenedor por tipo reificado. De lo contrario, Jackson no sabe qué objetos crear.
La cadena JSON que debe ser deserializada tendrá que contener la información de tipo sobre el parámetro T
.
Tendrá que poner anotaciones de Jackson en cada clase que se pueda pasar como parámetro T
a clase Data
para que T
Jackson pueda leer / escribir la información del tipo sobre el tipo de parámetro .
Supongamos que T
puede ser cualquier clase que extienda la clase abstracta Result
.
class Data <T extends Result> {
int found;
Class<T> hits
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({
@JsonSubTypes.Type(value = ImageResult.class, name = "ImageResult"),
@JsonSubTypes.Type(value = NewsResult.class, name = "NewsResult")})
public abstract class Result {
}
public class ImageResult extends Result {
}
public class NewsResult extends Result {
}
Una vez que T
se anota cada clase (o su supertipo común) que se puede pasar como parámetro , Jackson incluirá información sobre el parámetro T
en el JSON. Tal JSON se puede deserializar sin conocer el parámetro T
en tiempo de compilación.
Este enlace de documentación de Jackson habla sobre la deserialización polimórfica, pero también es útil hacer referencia a esta pregunta.
Desde Jackson 2.5, una forma elegante de resolver usando el
método TypeFactory.constructParametricType (Class parametrized, Class ... parameterClasses) que permite definir de forma recta un Jackson JavaType
al especificar la clase parametrizada y sus tipos parametrizados.
Suponiendo que desea deserializarse Data<String>
, puede hacer:
// the json variable may be a String, an InputStream and so for...
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class);
Data<String> data = mapper.readValue(json, type);
Tenga en cuenta que si la clase declara múltiples tipos parametrizados, no sería realmente más difícil:
class Data <T, U> {
int found;
Class<T> hits;
List<U> list;
}
Podríamos hacer :
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class, Integer);
Data<String, Integer> data = mapper.readValue(json, type);
public class Data<T> extends JsonDeserializer implements ContextualDeserializer {
private Class<T> cls;
public JsonDeserializer createContextual(DeserializationContext ctx, BeanProperty prop) throws JsonMappingException {
cls = (Class<T>) ctx.getContextualType().getRawClass();
return this;
}
...
}
si está usando scala y conoce el tipo genérico en tiempo de compilación, pero no desea pasar manualmente TypeReference a todas partes en todas sus aplicaciones, puede usar el siguiente código (con jackson 2.9.5):
def read[T](entityStream: InputStream)(implicit typeTag: WeakTypeTag[T]): T = {
//nathang: all of this *crazy* scala reflection allows us to handle List[Seq[Map[Int,Value]]]] without passing
// new TypeReference[List[Seq[Map[Int,Value]]]]](){} to the function
def recursiveFindGenericClasses(t: Type): JavaType = {
val current = typeTag.mirror.runtimeClass(t)
if (t.typeArgs.isEmpty) {
val noSubtypes = Seq.empty[Class[_]]
factory.constructParametricType(current, noSubtypes:_*)
}
else {
val genericSubtypes: Seq[JavaType] = t.typeArgs.map(recursiveFindGenericClasses)
factory.constructParametricType(current, genericSubtypes:_*)
}
}
val javaType = recursiveFindGenericClasses(typeTag.tpe)
json.readValue[T](entityStream, javaType)
}
que se puede usar así:
read[List[Map[Int, SomethingToSerialize]]](inputStream)
Para deserializar una cadena JSON genérica a un objeto Java con Jackson, necesita:
Para definir una clase JSON.
Realizar un mapeo de atributos.
Código final, probado y listo para usar:
static class MyJSON {
private Map<String, Object> content = new HashMap<>();
@JsonAnySetter
public void setContent(String key, Object value) {
content.put(key, value);
}
}
String json = "{\"City\":\"Prague\"}";
try {
MyPOJO myPOJO = objectMapper.readValue(json, MyPOJO.class);
String jsonAttVal = myPOJO.content.get("City").toString();
System.out.println(jsonAttVal);
} catch (IOException e) {
e.printStackTrace();
}
Importante: la
@JsonAnySetter
anotación es obligatoria, garantiza un análisis genérico de JSON y una población.
Para casos más complicados con matrices anidadas, consulte la referencia de Baeldung: https://www.baeldung.com/jackson-mapping-dynamic-object