Ответ 1
Вам нужно создать объект TypeReference
для каждого используемого вами типового типа и использовать его для десериализации. Например,
mapper.readValue(jsonString, new TypeReference<Data<String>>() {});
У меня есть строка JSON, который я должен deSerialize для следующего класса
class Data <T> {
int found;
Class<T> hits
}
Как мне это сделать? Это обычный способ
mapper.readValue(jsonString, Data.class);
Но как мне упомянуть, что означает Т?
Вам нужно создать объект TypeReference
для каждого используемого вами типового типа и использовать его для десериализации. Например,
mapper.readValue(jsonString, new TypeReference<Data<String>>() {});
Вы не можете сделать это: вы должны указать полностью разрешенный тип, например, Data<MyType>
. T
- это просто переменная, которая бессмысленна.
Но если вы имеете в виду, что T
будет известен, но не статически, вам нужно динамически создать эквивалент TypeReference
. Другие упомянутые вопросы могут уже упомянуть об этом, но это должно выглядеть примерно так:
public Data<T> read(InputStream json, Class<T> contentClass) {
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, contentClass);
return mapper.readValue(json, type);
}
Первое, что вы делаете, - сериализовать, тогда вы можете выполнить десериализацию.
поэтому, когда вы выполняете сериализацию, вы должны использовать @JsonTypeInfo
, чтобы позволить jsonson записывать информацию о классе в ваши json-данные. Что вы можете сделать, так это:
Class Data <T> {
int found;
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
Class<T> hits
}
Затем, когда вы выполните десериализацию, вы обнаружите, что джексон десериализует ваши данные в класс, который ваша переменная на самом деле находится во время выполнения.
Для класса данных <>
ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametrizedType(Data.class, Data.class, Parameter.class);
Data<Parameter> dataParam = mapper.readValue(jsonString,type)
Просто напишите статический метод в классе Util. Я читаю Json из файла. вы также можете передать String 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())));
}
Использование:
List<TaskBean> list = Util.<List<TaskBean>>convertJsonToPOJO("E:/J2eeWorkspaces/az_workspace_svn/az-client-service/dir1/dir2/filename.json", TaskBean.class);
Вы можете перенести его в другой класс, который знает тип вашего общего типа.
Например,
class Wrapper {
private Data<Something> data;
}
mapper.readValue(jsonString, Wrapper.class);
Здесь что-то конкретный тип. Вам нужна обертка для каждого типа. В противном случае Джексон не знает, какие объекты создавать.
Строка JSON, которую необходимо десериализовать, должна содержать информацию о типе параметра T
Вам нужно будет поместить аннотации Джексона в каждый класс, который может быть передан в качестве параметра T
в класс Data
чтобы информация о типе типа T
могла быть прочитана/записана в строку JSON Джексоном.
Предположим, что T
может быть любым классом, расширяющим абстрактный класс 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 {
}
Как только каждый из классов (или их общий супертип), который может быть передан в качестве параметра T
, аннотирован, Джексон включит информацию о параметре T
в JSON. Такой JSON может затем быть десериализован без знания параметра T
во время компиляции.
Эта ссылка на документацию Джексона говорит о полиморфной десериализации, но полезно также обратиться к этому вопросу.
С Jackson 2.5, элегантный способ решить, используя
TypeFactory.constructParametricType(Class параметризованный, Class... parameterClasses) метод, который позволяет строго определить Джексона JavaType
, указав параметризованный класс и его параметризованные типы.
Предположим, что вы хотите десериализовать в Data<String>
, вы можете сделать:
// 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);
Обратите внимание, что если бы класс объявил несколько параметризованных типов, это было бы не очень сложно:
class Data <T, U> {
int found;
Class<T> hits;
List<U> list;
}
Мы могли бы сделать:
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class, Integer);
Data<String, Integer> data = mapper.readValue(json, type);
если вы используете scala и знаете универсальный тип во время компиляции, но не хотите вручную передавать TypeReference везде во всех ваших приложениях, вы можете использовать следующий код (с 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)
}
который можно использовать так:
read[List[Map[Int, SomethingToSerialize]]](inputStream)
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;
}
...
}