que - manejo de datos genericos con java
Jackson-Deserializar usando una clase genérica (8)
La cadena JSON que necesita ser deserializada deberá contener la información de tipo sobre el parámetro T
Deberá colocar anotaciones de Jackson en cada clase que se pueda pasar como parámetro T
a los Data
clase, Data
modo que Jackson pueda leer / escribir la información de tipo sobre el parámetro tipo T
en cadena JSON.
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 cada clase (o su supertipo común) que se puede pasar como parámetro T
se anota, Jackson incluirá información sobre el parámetro T
en el JSON. Tal JSON puede deserializarse sin conocer el parámetro T
en el momento de la compilación.
Este enlace de documentación de Jackson habla de deserialización polimórfica, pero también es útil referirse a esta pregunta.
Tengo una cadena json, que debería deSerialize para 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 T significa?
Lo primero que haces es serializar, luego puedes deserializar.
así que cuando lo haga en serie, debe usar @JsonTypeInfo
para permitir que jackson escriba la información de la clase en sus 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 te deserialices, descubrirás que jackson ha deserializado tus datos en una clase en la que tus hits variables en realidad están en tiempo de ejecución.
Necesita crear un objeto TypeReference
para cada tipo genérico que use y usarlo para la deserialización. Por ejemplo,
mapper.readValue(jsonString, new TypeReference<Data<String>>() {});
No puede hacer eso: debe especificar el tipo completamente resuelto, como Data<MyType>
. T
es solo una variable, y no tiene sentido.
Pero si quiere decir que se conocerá a T
, simplemente no de forma estática, debe crear un equivalente de TypeReference
forma dinámica. Es posible que otras preguntas a las que se hace referencia ya lo mencionen, pero debería ser algo así como:
public Data<T> read(InputStream json, Class<T> contentClass) {
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, contentClass.class);
return mapper.readValue(json, type);
}
Para datos de clase <>
ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametrizedType(Data.class, Data.class, Parameter.class);
Data<Parameter> dataParam = mapper.readValue(jsonString,type)
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. Necesitas un contenedor por tipo reificado. De lo contrario, Jackson no sabe qué objetos crear.
Si está utilizando 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 leer [T] (entityStream: InputStream) (typeTag implícita: 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)
Solo escribe un método estático en la clase Util. Estoy leyendo un Json de un archivo. usted puede darle a String también para leerValor
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);