wake-up-neo.com

Jackson - Deserialize mit generischer Klasse

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?

90
gnjago

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>>() {});
150
Eser Aygün

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);
}
50
StaxMan

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.

22
CharlieQ

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);
9
AZ_

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)
7
Devesh

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.

6
monkjack

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.

1
Sanjeev Sachdev

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);
1
davidxxx

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)
0
nathan g