web-dev-qa-db-ja.com

ジャクソンは、シリアル化したジェネリックリストを逆シリアル化していません

(サーバーとクライアントの両方で)JSONシリアル化にJacksonでApache Jerseyを使用すると、ジェネリックリストを逆シリアル化するときに問題が発生します。

私が作成しているJSONは次のとおりです。「data」の3つのクラスすべてが「CheckStatusDetail」を実装しています。

{
  "errorCode" : 0,
  "errorMessage" : null,
  "type" : "array",
  "data" : [ {
    "@class" : "com.rrr.base.status.module.dto.DiscoveryAgentCheckStatusDetail",
    "serverInfo" : {
      "@class" : "com.rrr.base.util.discovery.config.xml.XMLServerInfo",
      "name" : "Java",
      "location" : "THEO",
      "description" : "sddgs",
      "group" : "Java",
      "aliases" : [ "mercury" ]
    }
  }, {
    "@class" : "com.rrr.base.status.module.dto.MongoDBCheckStatusDetail",
    "addresses" : [ "localhost:27017" ],
    "version" : "2.5",
    "connected" : true
  }, {
    "@class" : "com.rrr.base.status.module.dto.NetworkCheckStatusDetail",
    "splitBrain" : false
  } ],
  "count" : 3,
  "status" : 0
}

このJSONを生成するオブジェクトは次のようになります。クライアント側で同じクラスを使用しています。

public class NSResponse<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    public static final int STATUS_OK       = 0;
    public static final int STATUS_ERROR    = -1;

    public static final String TYPE_OBJECT  = "object";
    public static final String TYPE_ARRAY   = "array";

    private int status;
    private int errorCode;
    private String errorMessage;
    private String type;

    private List<T> data;

    private int count;

    public NSResponse() {   }

    public NSResponse(int errorCode, String errorMessage) {
        this.status = STATUS_ERROR;
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }

    public NSResponse(T data) {
        this.status = STATUS_OK;
        this.type = TYPE_OBJECT;
        this.data = new ArrayList<T>();
        this.data.add(data);
        this.count = this.data.size();
    }

    public NSResponse(List<T> data) {
        this.status = STATUS_OK;
        this.type = TYPE_ARRAY;
        this.data = data;
        this.count = (data == null) ? 0 : data.size();
    }

    /* Getters and setters omitted */
}

このアノテーションをCheckStatusDetailインターフェイスに追加してから、@ class情報が適用されています。

@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
public interface CheckStatusDetail extends Serializable {}

クライアント側でJSONを使用しようとすると、次のエラーが発生します。

Java.lang.ClassCastException: Java.util.LinkedHashMap cannot be cast to com.rrr.base.status.module.dto.CheckStatusDetail

このエラーは、デシリアライズ後に「データ」フィールドに初めてアクセスしようとしたときに発生します。クライアント側をデバッグすると、JacksonはList <LinkedHashMap>を返しているようです。これは、List <CheckStatusDetail>を期待しているため、エラーを説明しています。

私は何が間違っているのですか?

16
seanhodges

特に逆シリアル化を呼び出す方法について、もう少しコードを表示する必要がありますが、エラーから、Tのパラメーター化を渡していないと思います。欠落している場合、TはObject型であり、公称型はオブジェクトは「ネイティブ」Javaタイプにバインドされます。これは、JSONオブジェクトの場合はMap(具体的には、順序を保持するためのLinkedHashMap)です。

したがって、おそらく、逆シリアル化時にオブジェクトのジェネリック型を指定する必要があります(シリアル化の場合、実行時型が使用可能であるため、オブジェクトは必要ありません)。 TypeReferenceを使用するか(ジェネリック型情報がないためプレーンクラスではありません)、ジェネリック対応のJavaTypeを構築します。例えば:

NSResponse<CheckStatusDetail> resp = mapper.readValue(json, new TypeReference<NSResponse<CheckStatusDetail>>() { });

または

NSResponse<CheckStatusDetail> resp = mapper.readValue(json, TypeFactory.genericType(NSResponse.class, CheckStatusDetails.class));

両方とも機能します。タイプが動的にのみ使用可能な場合は、後者が必要です。

29
StaxMan