У мене є рядок json, яку мені слід дезариалізувати в наступний клас
class Data <T> {
int found;
Class<T> hits
}
Як це зробити? Це звичайний спосіб
mapper.readValue(jsonString, Data.class);
Але як я можу зазначити, що означає Т?
У мене є рядок json, яку мені слід дезариалізувати в наступний клас
class Data <T> {
int found;
Class<T> hits
}
Як це зробити? Це звичайний спосіб
mapper.readValue(jsonString, Data.class);
Але як я можу зазначити, що означає Т?
Відповіді:
Вам потрібно створити TypeReference
об'єкт для кожного загального типу, який ви використовуєте, і використовувати його для десеріалізації. Наприклад -
mapper.readValue(jsonString, new TypeReference<Data<String>>() {});
Data<T>
, це НЕ тип. Ви повинні вказати фактичний клас; інакше це те саме, що Data<Object>
.
TypeReference
? це com.fasterxml.jackson.core.type
?
Ви не можете цього зробити: потрібно вказати повністю розв'язаний тип, наприклад 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);
}
TypeReference
: У return mapper.readValue(json, clazz);
чому саме тут проблема?
TypeFactory
.. Я відредагую свою відповідь.
Перше, що ви робите - це серіалізація, потім ви можете зробити десяріалізацію.
тому, коли ви серіалізуєте, вам слід використовувати, @JsonTypeInfo
щоб Джексон записував інформацію про клас у свої дані 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
під час компіляції.
Це посилання на документацію Джексона говорить про поліморфну десеріалізацію, але корисно посилатися і на це питання.
У Джексона 2.5 - це елегантний спосіб вирішити те, що використовується
метод TypeFactory.constructParametricType (клас параметризований, клас ... 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);
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;
}
...
}
якщо ви використовуєте scala і знаєте загальний тип під час компіляції, але не хочете вручну передавати TypeReference скрізь у всіх ваших програмах api l, ви можете використовувати наступний код (з 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)
Щоб десаріалізувати загальний рядок JSON для Java-об’єкта з Джексоном, потрібно:
Для визначення класу JSON.
Виконайте відображення атрибутів.
Кінцевий код, перевірений та готовий до використання:
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();
}
Важливо:
@JsonAnySetter
анотація є обов'язковою, вона забезпечує загальний JSON-аналіз та сукупність.
Більш складні випадки з вкладеними масивами див. У посиланні Baeldung: https://www.baeldung.com/jackson-mapping-dynamic-object