13

シリアル化しようとしていますが、abstactクラスで問題が発生しています。

私は答えをグーグルで検索しました、そして私はこのブログアイテムを見つけまし。私はそれとその仕事を試しました。

わかりました、とてもいいです。しかし、アイテムのコメントをチェックしてください:

この方法論は本当の問題を隠しているようであり、それはオブジェクト指向設計パターン、つまりファクトリパターンの不正確な実装です。

新しいファクトリクラスを参照するために基本クラスを変更する必要があるのは、自滅的です。

少し後から考えることで、コードを変更して、任意の派生型を抽象クラスに関連付けることができ(インターフェイスの奇跡を通じて)、XmlIncludeは必要ありません。

ここで実装しようとしていると思われるファクトリパターンをさらに調査することをお勧めします。

コメンターは何について話しているのですか?彼はちょっと漠然としている。誰かがそれをより詳細に(例を挙げて)説明できますか?それとも彼はナンセンスを話しているだけですか?

更新(最初の回答を読んだ後)

なぜコメンターは

ファクトリパターン

コードは、任意の派生型を抽象クラスに関連付けることができる場所に変更できます(インターフェイスの奇跡を通じて)

彼はこのようなインターフェースを作りたいですか?

public interface IWorkaround
{
    void Method();
}

public class SomeBase : IWorkaround
{
    public void Method()
    {
        // some logic here
    }
}

public class SomeConcrete : SomeBase, IWorkaround
{
    public new void Method()
    {
        base.Method();
    }
}
4

2 に答える 2

46

彼は同時に正しいことも間違っていることもあります。

のようなものBinaryFormatterでは、これは問題ではありません。シリアル化されたストリームにはフルタイプのメタデータが含まれているため、次の場合は次のようになります。

[Serializable] abstract class SomeBase {}
[Serializable] class SomeConcrete : SomeBase {}
...
SomeBase obj = new SomeConcrete();

シリアル化すると、ストリームに「objI'ma」が含まれます。SomeConcreteこれは人生をシンプルにしますが、特に繰り返されるときは冗長です。また、逆シリアル化するときに同じ実装が必要になるため、脆弱です。異なるクライアント/サーバーの実装、または長期保存のいずれにも適していません。

XmlSerializerブログで話していると思いますが)では、メタデータはありませんが、要素名(またはxsi:type属性)は、どちらが使用されているかを識別するために使用されます。これが機能するためには、シリアライザーは、どの名前がどのタイプにマップされているかを事前に知る必要があります。

これを行う最も簡単な方法は、私たちが知っているサブクラスで基本クラスを装飾することです。次に、シリアライザーはこれらのそれぞれ(および追加のxml固有の属性)を検査して、<someConcreteType>要素を検出すると、それがインスタンスにマップされることをSomeConcrete確認できます(名前は一致する必要がないため、一致する必要はありません。名前で探してください)。

[XmlInclude(typeof(SomeConcrete))]
public abstract class SomeBase {}
public class SomeConcrete : SomeBase {}
...
SomeBase obj = new SomeConcrete();
XmlSerializer ser = new XmlSerializer(typeof(SomeBase));
ser.Serialize(Console.Out, obj);

ただし、彼が純粋主義者である場合(またはデータが利用できない場合)、別の方法があります。オーバーロードされたコンストラクターを介して、このすべてのデータを個別に指定できますXmlSerializer。たとえば、構成(またはIoCコンテナー)から既知のサブタイプのセットを検索し、コンストラクターを手動でセットアップすることができます。これはそれほどトリッキーではありませんが、実際に必要でない限り、それだけの価値がないほどトリッキーです。

public abstract class SomeBase { } // no [XmlInclude]
public class SomeConcrete : SomeBase { }
...
SomeBase obj = new SomeConcrete();
Type[] extras = {typeof(SomeConcrete)}; // from config
XmlSerializer ser = new XmlSerializer(typeof(SomeBase), extras);
ser.Serialize(Console.Out, obj);

さらに、XmlSerializerカスタムctorルートを使用する場合は、XmlSerializerインスタンスをキャッシュして再利用することが重要です。それ以外の場合は、使用ごとに新しい動的アセンブリがロードされます-非常に高価です(アンロードできません)。単純なコンストラクターを使用すると、モデルがキャッシュされて再利用されるため、単一のモデルのみが使用されます。

YAGNIは、最も単純なオプションを選択する必要があると指示しています。を使用[XmlInclude]すると、複雑なコンストラクターが不要になり、シリアライザーのキャッシュについて心配する必要がなくなります。ただし、他のオプションがあり、完全にサポートされています。


フォローアップの質問を再確認してください。

「ファクトリパターン」とは、おそらくIoC / DIまたは同様のフレームワークが原因で、コードが認識しない 場合について話していることです。SomeConcreteだからあなたは持っているかもしれません:

SomeBase obj = MyFactory.Create(typeof(SomeBase), someArgsMaybe);

適切なSomeBase具体的な実装を見つけ出し、それをインスタンス化して返します。明らかに、コードが具体的な型を認識していない場合(構成ファイルでのみ指定されているため)、;を使用することはできませんXmlInclude。ただし、構成データを解析して、ctorアプローチを使用することはできます(上記のとおり)実際には、ほとんどの場合XmlSerializerPOCO / DTOエンティティで使用されるため、これは人為的な懸念事項です。

そして、インターフェースを再作成します。同じことですが、より柔軟性があります(インターフェイスは型階層を必要としません)。ただし、XmlSerializerこのモデルはサポートされていません。率直に言って、タフです。それはその仕事ではありません。その仕事は、データの保存と転送を可能にすることです。実装ではありません。xmlスキーマで生成されたエンティティにメソッドがありません。データは具体的であり、抽象的なものではありません。「DTO」と考える限り、インターフェースの議論は問題ではありません。境界でインターフェースを使用できないことに悩まされている人々は、関心の分離を受け入れていません。つまり、次のことを行おうとしています。

Client runtime entities <---transport---> Server runtime entities

制限が少ないのではなく

Client runtime entities <---> Client DTO <--- transport--->
           Server DTO <---> Server runtime entities

現在、多くの(ほとんど?)場合、DTOとエンティティは同じである可能性があります。ただし、トランスポートが気に入らないことをしようとしている場合は、DTOを導入してください。シリアライザーと戦わないでください。人々がオブジェクトを書くのに苦労しているときも同じ論理が当てはまります。

class Person {
    public string AddressLine1 {get;set;}
    public string AddressLine2 {get;set;}
}

フォームのxmlとして:

<person>
    <address line1="..." line2="..."/>
</person>

これが必要な場合は、トランスポートに対応するDTOを誘導し、エンティティとDTOの間でマッピングします。

// (in a different namespace for the DTO stuff)
[XmlType("person"), XmlRoot("person")]
public class Person {
    [XmlElement("address")]
    public Address Address {get;set;}
}
public class Address {
    [XmlAttribute("line1")] public string Line1 {get;set;}
    [XmlAttribute("line2")] public string Line2 {get;set;}
}

これは、次のような他のすべてのニグルにも当てはまります。

  • なぜパラメーターなしのコンストラクターが必要なのですか?
  • コレクションプロパティにセッターが必要なのはなぜですか?
  • 不変型を使用できないのはなぜですか?
  • なぜ私のタイプはパブリックでなければならないのですか?
  • 複雑なバージョン管理を処理するにはどうすればよいですか?
  • さまざまなデータレイアウトを持つさまざまなクライアントを処理するにはどうすればよいですか?
  • なぜインターフェースを使用できないのですか?
  • などなど

これらの問題が常に発生するとは限りません。ただし、そうする場合は、DTO(またはいくつか)を導入すれば、問題は解決します。これをインターフェースに関する質問に戻します。DTOタイプはインターフェースベースではない場合がありますが、ランタイム/ビジネスタイプはインターフェースベースにすることができます。

于 2009-08-26T10:53:37.120 に答える
-1
**Example of Enum Abstract Serializer

Simple example of an abstract enum ...(Java)(Spring-Boot) 
----------------------------------------------------------------------------------**


@JsonSerialize(using = CatAbstractSerializer.class)
public enum CatTest implements Tes{

    TYPE1(1, "Type 1"), TYPE2(2, "Type 2");

    private int id;
    private String nome;

    private CatTest(int id, String nome) {
        // TODO Auto-generated constructor stub

        this.id = id;
        this.nome = nome;


    }
    @JsonValue
    public int getId() {
        return id;
    }
    @JsonSetter
    public void setId(int id) {
        this.id = id;
    }
    @JsonValue
    public String getNome() {
        return nome;
    }
    @JsonSetter
    public void setNome(String nome) {
        this.nome = nome;
    }
    @Override
     @JsonValue
     public String toString() {
            return nome;
     }
     @JsonCreator
        public static CatTest fromValueString(String nome) {
            if(nome == null) {
                throw new IllegalArgumentException();
            }
            for(CatTest nomeSalvo : values()) {
                if(nome.equals(nomeSalvo.getNome())) {
                    return nomeSalvo;
                }
            }
            throw new IllegalArgumentException();
        }


}


public interface Tes {

    @JsonValue
    int getId();

    @JsonValue
    String getNome();

    @JsonSetter
    void setId(int id);

    @JsonSetter
    void setNome(String nome);


}

public class CatAbstractSerializer<T extends Tes> extends JsonSerializer<T> {

    @Override
    public void serialize(T value, JsonGenerator gen, SerializerProvider serializers)
            throws IOException, JsonProcessingException {
        // TODO Auto-generated method stub

        gen.writeStartObject();
        gen.writeFieldName("id");
        gen.writeNumber(value.getId());
        gen.writeFieldName("name");
        gen.writeString(value.getNome());
        gen.writeEndObject();

    }

}
于 2018-11-04T04:22:44.683 に答える