34

動物園の例でスピンを使用します。

public class ZooPen {
    public String type;
    public List<Animal> animals;
}

public class Animal {
    public String name;
    public int age;
}

public class Bird extends Animal {
    public double wingspan;
}

Animalウィングスパンが指定されていない場合はインスタンスを構築し、指定されている場合はインスタンスを構築するためにポリモーフィックデシリアライゼーションを使用したいと考えてBirdいます。Jackson では、通常、型指定されていないデシリアライゼーションは次のようになります。

@JsonTypeInfo( 
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "wingspan",
    visible = true,
    defaultImpl = Animal.class
)
@JsonSubTypes({
    @Type(value = Bird.class, name = "bird")
})  
public class Animal {
    ...
}

wingspan の値は何でもかまいませんが、具体的に一致するものがない場合、Jackson は defaultImpl クラスにフォールバックします。

私はおそらく使用できます@JsonCreator

@JsonCreator
public static Animal create(Map<String,Object> jsonMap) 
        throws JsonProcessingException {

    ObjectMapper mapper = new ObjectMapper();
    if (jsonMap.get("wingspan") == null) {
        // Construct and return animal
    } else {
        // Construct and return bird
    }
}

ただし、余分な値を手動で処理し、一貫した例外をスローする必要があり、Animal後で適切にシリアル化されるかどうかは明確ではありません。

独自のTypeResolverorを使用できるようですTypeIdResolverが、生の json を自分で逆シリアル化するよりも手間がかかるようです。さらに、本質的に型情報がシリアル化されていると想定しているようTypeResolverTypeIdResolver見えるため、それらは使用するのに適していません。

JsonDeserializerタイプを指定するためにライフサイクルにフックする独自のものを実装することは可能でしょうか?ただし、基本的な Jackson アノテーション処理機能を引き続き使用しますか? を見てきましたが、JsonDeserializer.deserializeWithType(...)逆シリアル化を完全にに委任しているようTypeDeserializerです。使用するタイプを知る前に、オブジェクトの一部を逆シリアル化する必要があるという問題もあります。

または、親オブジェクトにある場合でも、動物園のペンのタイプをターゲットにする方法があるかもしれません。

ポリモーフィック型の処理で私がやりたいことをする方法はありますか?

4

6 に答える 6

8

Jackson 2.12.2 の時点で、以下は「演繹ベースのポリモーフィズム」機能を使用して目標を達成します。Birdサブタイプ (つまり)とは異なるプロパティwingspanが存在する場合、逆シリアル化された型はBird;になります。それ以外の場合は次のようになりますAnimal

@JsonTypeInfo(use=Id.DEDUCTION, defaultImpl = Animal.class)
@JsonSubTypes({@Type(Bird.class)})
public class Animal {
    public String name;
    public int age;
}

演繹ベースのポリモーフィズム

演繹ベースのポリモーフィズム機能は、特定のサブタイプに固有のプロパティの存在に基づいてサブタイプを演繹します。サブタイプ固有のプロパティによって一意に識別可能なサブタイプがない場合は、defaultImpl値で指定されたタイプが使用されます。

演繹ベースのポリモーフィズム機能は、Jackson 2.12 のjackson-databind#43に従って実装され、 2.12 リリース ノートにまとめられています。

フィールドの存在からサブタイプを推定 ( ) できる限り、基本的には実際の Type Id フィールドまたは値を省略でき@JsonTypeInfo(use=DEDUCTION)ます。つまり、すべてのサブタイプには含まれている一連のフィールドが異なるため、逆シリアル化中にタイプを一意かつ確実に検出できます。

一意に識別可能なサブタイプがない場合に、例外をスローするのではなく、デフォルトのタイプを指定するこの機能は、Jackson 2.12.2の jackson-databind#3055によって追加されました。

候補が 1 つもない場合は、defaultImpl適合性に関係なくターゲット タイプにする必要があります。

演繹ベースのポリモーフィズムのもう少し長い説明は、Jackson の作成者によって書かれたJackson 2.12 Most Wanted (1/5): Deduction-Based Polymorphismの記事に記載されています。

于 2021-02-12T07:16:34.067 に答える
1

編集:最新の Jackson リリース候補を使用できる場合、問題は解決されます。ここで簡単なデモを組み立てましたhttps://github.com/MariusSchmidt/de.denktmit.stackoverflow/tree/main/de.denktmit.jackson

このスレッドhttps://github.com/FasterXML/jackson-databind/issues/1627を参照してください。問題について議論し、解決策を提案しています。私には有望に見えるマージがありますhttps://github.com/FasterXML/jackson-databind/pull/2813。したがって、@JsonTypeInfo(use = DEDUCTION) のパスをたどろうとするかもしれません。

ただし、ジャクソンの最新バージョンを使用できない場合は、次のようにします。

マージ リクエストをバックポートする、または

  1. Jackson を使用して、入力を一般的な JsonNode に逆シリアル化します
  2. https://github.com/json-path/JsonPathを使用して、1 つ以上のプロパティの存在を確認します。一部のコンテナ クラスは、クラス タイプを一意に識別するために必要なすべてのパスをラップできます。
  3. ここで説明されているように、JsonNode を決定されたクラスにマップしますJsonNode を POJO に変換します

このようにして、低レベルのマッピング ロジックを処理することなく、Jackson の機能を最大限に活用できます。

よろしくお願いします、

マリウス

動物

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.denktmit.stackoverflow.jackson.polymorphic.deductive.Bird;
import de.denktmit.stackoverflow.jackson.polymorphic.deductive.Fish;
import org.junit.jupiter.api.Test;

import java.util.List;

import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.DEDUCTION;
import static org.assertj.core.api.Assertions.assertThat;

@JsonTypeInfo(use = DEDUCTION)
@JsonSubTypes( {@JsonSubTypes.Type(Bird.class), @JsonSubTypes.Type(Fish.class)})
public class Animal {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class Bird extends de.denktmit.stackoverflow.jackson.polymorphic.deductive.Animal {
    private double wingspan;

    public double getWingspan() {
        return wingspan;
    }

    public void setWingspan(double wingspan) {
        this.wingspan = wingspan;
    }
}

public class Fish extends de.denktmit.stackoverflow.jackson.polymorphic.deductive.Animal {

    private boolean freshwater;

    public boolean isFreshwater() {
        return freshwater;
    }

    public void setFreshwater(boolean freshwater) {
        this.freshwater = freshwater;
    }
}

ズーペン

public class ZooPen {

    private String type;
    private List<de.denktmit.stackoverflow.jackson.polymorphic.deductive.Animal> animals;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public List<de.denktmit.stackoverflow.jackson.polymorphic.deductive.Animal> getAnimals() {
        return animals;
    }

    public void setAnimals(List<de.denktmit.stackoverflow.jackson.polymorphic.deductive.Animal> animals) {
        this.animals = animals;
    }
}

テスト

import com.fasterxml.jackson.databind.ObjectMapper;
        import de.denktmit.stackoverflow.jackson.polymorphic.deductive.Animal;
        import de.denktmit.stackoverflow.jackson.polymorphic.deductive.Bird;
        import de.denktmit.stackoverflow.jackson.polymorphic.deductive.Fish;
        import de.denktmit.stackoverflow.jackson.polymorphic.deductive.ZooPen;
        import org.junit.jupiter.api.Test;

        import static org.assertj.core.api.Assertions.assertThat;

public class DeductivePolymorphicDeserializationTest {

    private static final String birdString = "{\n" +
            "      \"name\": \"Tweety\",\n" +
            "      \"age\": 79,\n" +
            "      \"wingspan\": 2.9\n" +
            "    }";

    private static final String fishString = "{\n" +
            "      \"name\": \"Nemo\",\n" +
            "      \"age\": 16,\n" +
            "      \"freshwater\": false\n" +
            "    }";

    private static final String zooPenString = "{\n" +
            "  \"type\": \"aquaviary\",\n" +
            "  \"animals\": [\n" +
            "    {\n" +
            "      \"name\": \"Tweety\",\n" +
            "      \"age\": 79,\n" +
            "      \"wingspan\": 2.9\n" +
            "    },\n" +
            "    {\n" +
            "      \"name\": \"Nemo\",\n" +
            "      \"age\": 16,\n" +
            "      \"freshwater\": false\n" +
            "    }\n" +
            "  ]\n" +
            "}";
    private final ObjectMapper mapper = new ObjectMapper();

    @Test
    void deserializeBird() throws Exception {
        de.denktmit.stackoverflow.jackson.polymorphic.deductive.Animal animal = mapper.readValue(birdString, de.denktmit.stackoverflow.jackson.polymorphic.deductive.Animal.class);
        assertThat(animal).isInstanceOf(de.denktmit.stackoverflow.jackson.polymorphic.deductive.Bird.class);
    }

    @Test
    void deserializeFish() throws Exception {
        de.denktmit.stackoverflow.jackson.polymorphic.deductive.Animal animal = mapper.readValue(fishString, de.denktmit.stackoverflow.jackson.polymorphic.deductive.Animal.class);
        assertThat(animal).isInstanceOf(de.denktmit.stackoverflow.jackson.polymorphic.deductive.Fish.class);
    }

    @Test
    void deserialize() throws Exception {
        de.denktmit.stackoverflow.jackson.polymorphic.deductive.ZooPen zooPen = mapper.readValue(zooPenString, de.denktmit.stackoverflow.jackson.polymorphic.deductive.ZooPen.class);
        assertThat(zooPen).isInstanceOf(de.denktmit.stackoverflow.jackson.polymorphic.deductive.ZooPen.class);
    }
}
于 2020-11-22T12:24:47.200 に答える
-1

Jackson と結婚していない場合は、これと同様のことが FlexJSON を使用して実現できると思います。

http://flexjson.sourceforge.net/javadoc/flexjson/JSONDeserializer.html

同様のことを行うための Jackson の方法には慣れていませんが、FlexJSON は非常にパフォーマンスが高く、一般的にシリアル化/逆シリアル化のステップで直感的に使用できると言えます。

于 2013-10-21T01:35:00.300 に答える
-1

こんにちはショーン、継承を使用してジャクソンで実際にこの動作を非常に簡単に実現できます。ここでは、動物と鳥のシナリオをモデル化しました。

Impl内のコンストラクターにより、 Animal の正しいインスタンスをインスタンス化できます (つまり、名前と年齢が存在する場合は An Animal 、名前と年齢と翼幅が存在する場合は Bird )。これは、Jersey などを使用して API 経由で値を取得する場合と同じように機能します。

@com.fasterxml.jackson.annotation.JsonSubTypes({
    @com.fasterxml.jackson.annotation.JsonSubTypes.Type(AnimalImpl.class)
})
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(as = AnimalImpl.class)
public interface Animal {

    public String getName();

    public int getAge();
}

public class AnimalImpl implements Animal {

    private final String name;
    private final int age;

    public AnimalImpl(
        @JsonProperty("name") final String name,
        @JsonProperty("age") final int age
    ) {
    this.name = Objects.requireNonNull(name);
    this.age = Objects.requireNonNull(age);
    }

    @Override
    public String getName() {
    return name;
    }

    @Override
    public int getAge() {
    return age;
    }
}

@com.fasterxml.jackson.annotation.JsonSubTypes({
    @com.fasterxml.jackson.annotation.JsonSubTypes.Type(BirdImpl.class)
})
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(as = BirdImpl.class)
public interface Bird extends Animal {

    public double getWingspan();
}

public class BirdImpl extends AnimalImpl implements Bird {

    private final double wingspan;

    public BirdImpl(
        @com.fasterxml.jackson.annotation.JsonProperty("name") final String name,
        @com.fasterxml.jackson.annotation.JsonProperty("age") final int age,
        @com.fasterxml.jackson.annotation.JsonProperty("wingspan") final double wingspan
    ) {
    super(name, age);
    this.wingspan = wingspan;
    }

    @Override
    public double getWingspan() {
    return wingspan;
    }
}

public class Test {

    public static void main(final String[] args) throws java.io.IOException {

    final com.fasterxml.jackson.databind.ObjectMapper objectMapper
        = new com.fasterxml.jackson.databind.ObjectMapper();

    final String animalJson = "{\"name\": \"the name\", \"age\": 42}";
    final Animal animal = objectMapper.readValue(animalJson, Animal.class);

    System.out.println(animal);

    final String birdJson = "{\"name\": \"the name\", \"age\": 42, \"wingspan\": 21}";
    final Bird bird = objectMapper.readValue(birdJson, Bird.class);

    System.out.println(bird);
    }
}
于 2020-02-21T07:38:34.187 に答える