3

Jakson を使用して、ネストされたポリモーフィック型を逆シリアル化しようとしています。つまり、私の最上位の型は、抽象的ではないクラスによって最終的に拡張される別のポリモーフィック型を参照しています。これは機能せず、例外がスローされます。

これは、私がやろうとしていることの縮小された例です。

package com.adfin;

import junit.framework.TestCase;
import org.codehaus.jackson.annotate.JsonSubTypes;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.codehaus.jackson.map.ObjectMapper;

import java.io.IOException;

public class JaksonDouble extends TestCase {

  @JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "name"
  )
  @JsonSubTypes({
    @JsonSubTypes.Type(value = SecondLevel.class, name = "SECOND")
  })
  public static abstract class FirstLevel {
    public abstract String getTestValue();
  }

  @JsonTypeInfo(
    use = JsonTypeInfo.Id.CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class"
  )
  public static abstract class SecondLevel extends FirstLevel {

  }

  public static class FinalLevel extends SecondLevel {
    String test;
    @Override public String getTestValue() { return test; }
  }

  public void testDoubleAbstract() throws IOException {
    String testStr = "{ \"name\": \"SECOND\", \"@class\": \"com.adfin.JasksonDouble.FinalLevel\", \"test\": \"foo\"}";

    ObjectMapper mapper = new ObjectMapper();
    FirstLevel result = mapper.readValue(testStr, FirstLevel.class);
  }
}

抽象型に関する標準例外が発生します。

org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.adfin.JaksonDouble$SecondLevel, problem: abstract types can only be instantiated with additional type information at [Source: java.io.StringReader@f2a55aa; line: 1, column: 19]

私のユースケースを説明しましょう。データのワークフローを記述した Json ドキュメントがあります。単一の値に対する操作を記述する「レベル 1」の抽象型があります。一般的な操作を実装する抽象的ではない一連のクラスを派生させます (それらすべてに @JsonSubTypes で注釈を付けます)。

「CUSTOM」と呼ばれる特別な @JsonSubTypes が 1 つあります。これは、他の誰かが (通常の jar の外部で) 記述したカスタム操作を表す別の抽象クラスであり、"@class" プロパティを使用して完全修飾クラス名を指定できます。Jakson パーサーは、2 番目のラベル クラスの @JsonTypeInfo アノテーションを読み取らないようです。

どうすればこれを機能させることができますか。または、少なくともこのユースケースを機能させるにはどうすればよいですか。

4

2 に答える 2

2

定義がおかしくなっています。型名とクラスの 2 つの型識別子を使用しようとしています。これは意味がありません。両方ではなく、どちらか一方の方法を選択する必要があります。

タイプ情報として Java クラス名を選択した場合は、名前を省略します。@JsonTypeInfoまた、 forのみを含める必要がありますFirstLevel。サブクラスはこの定義を継承します。

論理型名を使用する場合は、クラス プロパティを削除します。また、アノテーションを使用するか、ObjectMapper.

于 2013-01-09T04:15:54.580 に答える
0

まず、json のクラス名が間違っています。com.adfin.JasksonDouble$FinalLevel、ドットではなくドルにする必要があります。

これは機能するコードですが、すべてのサブタイプに正しく応答するかどうかはわかりません。

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public static abstract class FirstLevel {
    public abstract String getTestValue();
}

他のクラスから注釈を削除すると、機能するはずです(テストしたばかりです)。

ただし、これらはすべて非常に複雑に見えます。別のライブラリを自由に試すことができる場合は、Gensonを確認してください。ポリモーフィック タイプのサポートを有効にするには、Genson インスタンスを構成する必要があります。あなたがjsonを生成する人でもある場合は、他に何も必要ありません(多相型を処理するために必要に応じてGensonを使用してjsonストリームを生成できるため)。

以下に例を示します。

// enable polymorphic types support
Genson genson = new Genson.Builder().setWithClassMetadata(true).create();
// the @class must be first property in the json object
String testStr = "{ \"@class\": \"com.adfin.JasksonDouble$FinalLevel\", \"test\": \"foo\"}";
FirstLevel result = genson.deserialize(testStr, FirstLevel.class);
System.out.println(result.getTestValue());

Genson のもう 1 つの優れた点は、クラスのエイリアスを登録できるため、すべてのパッケージ情報をストリームで利用できるようにする必要がないことです。もう 1 つの利点は、json ストリームをデータベースに保存し、クラスを別のパッケージに移動する場合、アプリケーションでエイリアスを変更するだけで済み、DB からすべての json ストリームを移行する必要がないことです。

Genson genson = new Genson.Builder().setWithClassMetadata(true)
                                    .addAlias("FinalLevel", FinalLevel.class)
                                    .create();
// the @class must be first property in the json object
String testStr = "{ \"@class\": \"FinalLevel\", \"test\": \"foo\"}";
FirstLevel result = genson.deserialize(testStr, FirstLevel.class);
System.out.println(result.getTestValue());
于 2013-01-08T23:48:11.493 に答える