Json文字列があり、次のクラスにシリアル化解除する必要があります
class Data <T> {
int found;
Class<T> hits
}
どうすればいいのですか?これは通常の方法です
mapper.readValue(jsonString, Data.class);
しかし、Tが何を表すのかをどのように言及すればよいのでしょうか?
使用するジェネリック型ごとに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
を使用して、jacksonがjsonデータにクラス情報を書き込む必要があります。あなたができることはこのようなものです:
Class Data <T> {
int found;
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
Class<T> hits
}
その後、デシリアライズすると、jacksonがデータをデシリアライズして、実行時に変数が実際にヒットするクラスにデシリアライズすることがわかります。
クラスData <>の場合
ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametrizedType(Data.class, Data.class, Parameter.class);
Data<Parameter> dataParam = mapper.readValue(jsonString,type)
Utilクラスに静的メソッドを記述するだけです。ファイルからJsonを読んでいます。 readValueにもStringを与えることができます
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
に渡すことができるすべてのクラスにJacksonアノテーションを付ける必要があります。そうすると、パラメータータイプT
に関するタイプ情報を読み取ることができます/ Jacksonによって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
として渡すことができる各クラス(または共通のスーパータイプ)に注釈が付けられると、JacksonはパラメーターT
に関する情報をJSONに含めます。このようなJSONは、コンパイル時にパラメーターT
を知らなくても逆シリアル化できます。
この Jackson documentation link は、多相デシリアライゼーションについて説明していますが、この質問を参照するのにも役立ちます。
scalaを使用していて、コンパイル時にジェネリック型を知っているが、すべてのAPIレイヤーのすべての場所で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)