Ich habe eine Json-Zeichenfolge, die ich für die folgende Klasse deklarieren sollte
class Data <T> {
int found;
Class<T> hits
}
Wie mache ich das? Dies ist der übliche Weg
mapper.readValue(jsonString, Data.class);
Aber wie erwähne ich, wofür T steht?
Sie müssen für jeden generischen Typ, den Sie verwenden, ein TypeReference
-Objekt erstellen und dieses zur Deserialisierung verwenden. Zum Beispiel,
mapper.readValue(jsonString, new TypeReference<Data<String>>() {});
Das ist nicht möglich: Sie müssen einen vollständig aufgelösten Typ angeben, wie Data<MyType>
. T
ist nur eine Variable und hat keine Bedeutung.
Wenn Sie jedoch meinen, dass T
nur statisch nicht bekannt ist, müssen Sie das Äquivalent von TypeReference
dynamisch erstellen. Andere Fragen, auf die verwiesen wird, können dies bereits erwähnen, aber es sollte ungefähr so aussehen:
public Data<T> read(InputStream json, Class<T> contentClass) {
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, contentClass.class);
return mapper.readValue(json, type);
}
Zuerst serialisieren Sie, dann können Sie deserialisieren.
Wenn Sie also serialisieren, sollten Sie @JsonTypeInfo
verwenden, damit jackson Klasseninformationen in Ihre Json-Daten schreiben kann. Was Sie tun können, ist wie folgt:
Class Data <T> {
int found;
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
Class<T> hits
}
Wenn Sie dann deserialisieren, werden Sie feststellen, dass Jackson Ihre Daten in eine Klasse deserialisiert, die Ihre Variablen zur Laufzeit tatsächlich treffen.
Schreiben Sie einfach eine statische Methode in die Klasse Util. Ich lese einen Json aus einer Datei. Sie können String auch readValue geben
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())));
}
Verwendungszweck:
List<TaskBean> list = Util.<List<TaskBean>>convertJsonToPOJO("E:/J2eeWorkspaces/az_workspace_svn/az-client-service/dir1/dir2/filename.json", TaskBean.class);
Für die Klasse Data <>
ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametrizedType(Data.class, Data.class, Parameter.class);
Data<Parameter> dataParam = mapper.readValue(jsonString,type)
Sie können es in eine andere Klasse einschließen, die den Typ Ihres generischen Typs kennt.
Z.B,
class Wrapper {
private Data<Something> data;
}
mapper.readValue(jsonString, Wrapper.class);
Hier ist etwas ein konkreter Typ. Sie benötigen einen Wrapper für jeden angegebenen Typ. Ansonsten weiß Jackson nicht, welche Objekte erstellt werden sollen.
Die JSON-Zeichenfolge, die deserialisiert werden muss, muss die Typinformationen zum Parameter T
enthalten.
Sie müssen Jackson-Anmerkungen zu jeder Klasse setzen, die als Parameter T
an die Klasse Data
übergeben werden kann, damit die Typinformationen über den Parametertyp T
von Jackson aus dem JSON-String gelesen bzw. in diesen geschrieben werden können.
Nehmen wir an, dass T
jede Klasse sein kann, die die abstrakte Klasse Result
erweitert.
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 {
}
Sobald alle Klassen (oder deren allgemeiner Supertyp), die als Parameter T
übergeben werden können, mit Anmerkungen versehen sind, fügt Jackson Informationen über den Parameter T
in der JSON ein. Ein solches JSON kann dann deserialisiert werden, ohne den Parameter T
zur Kompilierzeit zu kennen.
Dieser Jackson-Dokumentationslink spricht über polymorphe Deserialisierung, kann aber auch für diese Frage verwendet werden.
Ab Jackson 2.5 eine elegante Art zu lösen, bei der die Methode TypeFactory.constructParametricType (Class parametrized, Class ... parameterClasses) verwendet wird, mit der ein Jackson JavaType
durch Angabe des Parameters genau definiert werden kann Klasse und ihre parametrisierten Typen.
Angenommen, Sie möchten zu Data<String>
deserialisieren, dann können Sie Folgendes tun:
// 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);
Beachten Sie, dass wenn die Klasse mehrere parametrisierte Typen deklarieren würde, es nicht wirklich schwieriger wäre:
class Data <T, U> {
int found;
Class<T> hits;
List<U> list;
}
Wir könnten :
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class, Integer);
Data<String, Integer> data = mapper.readValue(json, type);
wenn Sie scala verwenden und den generischen Typ zur Kompilierzeit kennen, aber TypeReference nicht überall in allen Ihren Primaten manuell übergeben möchten, können Sie folgenden Code verwenden (mit 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)
}
was kann so verwendet werden:
read[List[Map[Int, SomethingToSerialize]]](inputStream)