2

シリアル化されたときに、そのメンバーの 1 つを代わりにシリアル化する必要があるクラスがあります。私のクラスは:

@JsonSerialize(using = MyClassSerializer.class)
public class MyClass implements Serializable {

    /**
     * A default ID for this class for serialization.
     */
    private static final long serialVersionUID = 1L;

    /**
     * A member of this object.
     */
    private final OtherClass otherClass;

    ...

    /**
     * Returns the instance of the member object.
     * 
     * @return The instance of the member object.
     */
    public OtherClass getOtherClass() {
        return otherClass;
    }
}

これを実現するために、非常に単純なカスタム シリアライザーを作成しました。

public class MyClassSerializer extends JsonSerializer<MyClass> {
    /**
     * Serializes only the OtherClass field.
     */
    @Override
    public void serialize(
        final MyClass myClass,
        final JsonGenerator generator,
        final SerializerProvider provider)
        throws IOException, JsonProcessingException {

        // Write the schema.
        generator.writeObject(myClass.getOtherClass());
    }
}

これは、このようなことを行うための最も簡単な (そして、最も正しいと私が信じている) 方法です。そうしたかったとしても、OtherClass は抽象ルート クラスであるため、カスタム シリアライザーを作成するのは非常に複雑です。ただし、これは、いくつかの注釈を付けることで、Jackson を介して実現できます。

@JsonTypeInfo(
    use = Id.NAME,
    include = As.PROPERTY,
    property = OtherClass.JSON_KEY_TYPE,
    defaultImpl = OtherClassDefault.class)
@JsonSubTypes({
    @JsonSubTypes.Type(
        value = SubOtherClass1.class,
        name = SubOtherClass1.TYPE_ID),
    @JsonSubTypes.Type(
        value = SubOtherClass2.class,
        name = SubOtherClass2.TYPE_ID),
    @JsonSubTypes.Type(
        value = SubOtherClass3.class,
        name = SubOtherClass3.TYPE_ID),
    @JsonSubTypes.Type(
        value = SubOtherClass4.class,
        name = SubOtherClass4.TYPE_ID),
    @JsonSubTypes.Type(
        value = SubOtherClass5.class,
        name = SubOtherClass5.TYPE_ID) })
@JsonAutoDetect(
    fieldVisibility = Visibility.DEFAULT,
    getterVisibility = Visibility.NONE,
    setterVisibility = Visibility.NONE,
    creatorVisibility = Visibility.DEFAULT)
public abstract class OtherClass implements Serializable {
    ...
}

Jackson を使用してこれをテストし、MyClass のインスタンスをシリアル化および逆シリアル化しましたが、意図したとおりに動作します。

私のアプリケーション コードはもう少し複雑です。ContainerClasstype のメンバーを持つ がありますMyClassContainerClassMongoJack を使用してのインスタンスをシリアル化しようとしています。

// Get the authentication token collection.
JacksonDBCollection<ContainerClass, Object> collection =
    JacksonDBCollection
        .wrap(
            MongoBinController
                .getInstance()
                .getDb()
                .getCollection(COLLECTION_NAME),
            ContainerClass.class);

// Save it.
collection.insert(container);

ただし、次のエラーが表示されます。

...
Caused by: java.lang.IllegalArgumentException: can't serialize class my.package.MyClass
    at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:270)
    ...

@JsonSerializefromを削除すると機能するようになりますが、これによりインスタンスMyClass全体がシリアル化され、これは私が望むものではありません。MyClassこれにより、問題がカスタム シリアライザーにあることがほぼ確実になりますが、他にどのように記述すればよいかわかりません。

前もって感謝します。

4

1 に答える 1

1

役立つかもしれない簡単なメモ: 多相型を使用する場合、呼び出されるメソッドは次のようになります。

serializeWithType(...)

ではありませんserialize(...)。したがって、そのメソッドを実装する必要があります。通常、次のような単純なものになります。

typeSer.writeTypePrefixForObject(value, jgen);
// implement actual content serialization, or delegate here:
this.serialize(...);
typeSer.writeTypeSuffixForObject(value, jgen);

ただし、標準の Jackson シリアライザーを確認することをお勧めします。唯一の実際の違いは、serializeSTART_OBJECT、END_OBJECT を直接出力する必要があるのに対し、ここではTypeSerializerそれらを追加するように要求する必要があることです。含めると実際にこれらが変更される可能性があるため、これが必要ですtype id(これについて詳しく説明することはできますが、今のところはこれで十分です)。

ただし、ここにはもっと簡単な解決策があるかもしれません。オブジェクト全体の代わりに使用したいメンバーに注釈を追加できる場合@JsonValue(直接または mix-in を介して)、それでうまくいくはずです:

@JsonValue
public OtherClass getOtherClass()...

逆シリアル化する必要がある場合は、次@JsonCreatorのように使用できます。

@JsonCreator
public MyClass(OtherClass surrogate) { ... }
于 2013-09-27T23:00:04.840 に答える