質問

  • .Net / C#アプリケーションには、相互参照のあるデータ構造があります。
  • それらをシリアル化すると、.Netはすべての参照を個別のオブジェクトコピーでシリアル化します。
  • 次の例では、「Person」の配列にシリアル化しようとしています
  • 「個人」には、別の個人への参照が含まれる場合があります。

    public class Person
    {
        public string Name;
        public Person Friend;
    }
    
    Person p1 = new Person();
    p1.Name = "John";
    
    Person p2 = new Person();
    p2.Name = "Mike";
    
    p1.Friend = p2;
    
    Person[] group = new Person[] { p1, p2 };
    XmlSerializer ser = new XmlSerializer(typeof(Person[]));
    using (TextWriter tw = new StreamWriter("test.xml"))
        ser.Serialize(tw,group );
    
    //above code generates following xml
    
    <ArrayOfPerson>
      <Person>
        <Name>John</Name>
        <Friend>
          <Name>Mike</Name>
        </Friend>
      </Person>
      <Person>
        <Name>Mike</Name>
      </Person>
    </ArrayOfPerson>
    
  • 上記のコードでは、同じオブジェクトに2つの参照があるため、同じ「マイク」オブジェクトが2つの場所にあります。

  • デシリアライズ中は、2つの異なるオブジェクトになりますが、シリアライズされたときの正確な状態ではありません。
  • これを避けたいのは、シリアル化されたxmlにオブジェクトのコピーのみを持ち、すべての参照はこのコピーを参照する必要があることです。逆シリアル化した後、元の同じデータ構造に戻りたい。
  • それは可能ですか?
役に立ちましたか?

解決

XmlSerializer では不可能です。 。 DataContractSerializer で< href = "http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.preserveobjectreferences.aspx" rel = "noreferrer"> PreserveObjectReferences プロパティ。この投稿をご覧ください。詳細。

サンプルコードは次のとおりです。

public class Person
{
    public string Name;
    public Person Friend;
}

class Program
{
    static void Main(string[] args)
    {
        Person p1 = new Person();
        p1.Name = "John";

        Person p2 = new Person();
        p2.Name = "Mike";
        p1.Friend = p2;
        Person[] group = new Person[] { p1, p2 };

        var serializer = new DataContractSerializer(group.GetType(), null, 
            0x7FFF /*maxItemsInObjectGraph*/, 
            false /*ignoreExtensionDataObject*/, 
            true /*preserveObjectReferences : this is where the magic happens */, 
            null /*dataContractSurrogate*/);
        serializer.WriteObject(Console.OpenStandardOutput(), group);
    }
}

これにより、次のXMLが生成されます。

<ArrayOfPerson z:Id="1" z:Size="2" xmlns="http://schemas.datacontract.org/2004/07/ToDelete" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
    <Person z:Id="2">
        <Friend z:Id="3">
            <Friend i:nil="true"/>
            <Name z:Id="4">Mike</Name>
        </Friend>
        <Name z:Id="5">John</Name>
    </Person>
    <Person z:Ref="3" i:nil="true"/>
</ArrayOfPerson>

コンストラクタで PreserveObjectReferences false に設定すると、次のようになります:

<ArrayOfPerson xmlns="http://schemas.datacontract.org/2004/07/ToDelete" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <Person>
        <Friend>
            <Friend i:nil="true"/>
            <Name>Mike</Name>
        </Friend>
        <Name>John</Name>
    </Person>
    <Person>
        <Friend i:nil="true"/>
        <Name>Mike</Name>
    </Person>
</ArrayOfPerson>

この方法で生成されたXMLは相互運用性がなく、DataContractSerializerでのみ逆シリアル化できることに注意する価値があります( BinaryFormatter )。

他のヒント

ExtendedXmlSerializerを使用できます。シリアル化の例は、オブジェクト参照と循環参照

クラスがある場合:

public class Person
{
    public int Id { get; set; }

    public string Name { get; set; }

    public Person Boss { get; set; }
}

public class Company
{
    public List<Person> Employees { get; set; }
}

次に、次のように循環参照を使用してオブジェクトを作成します。

var boss = new Person {Id = 1, Name = "John"};
boss.Boss = boss; //himself boss
var worker = new Person {Id = 2, Name = "Oliver"};
worker.Boss = boss;
var obj = new Company
{
    Employees = new List<Person>
    {
        worker,
        boss
    }
};

参照オブジェクトとしてPersonクラスを構成する必要があります:

var serializer = new ConfigurationContainer().ConfigureType<Person>()
                                             .EnableReferences(p => p.Id)
                                             .Create();

最後に、オブジェクトをシリアル化できます:

var xml = serializer.Serialize(obj);

出力XMLは次のようになります。

<?xml version="1.0" encoding="utf-8"?>
<Company xmlns="clr-namespace:ExtendedXmlSerializer.Samples.ObjectReference;assembly=ExtendedXmlSerializer.Samples">
  <Employees>
    <Capacity>4</Capacity>
    <Person Id="2">
      <Name>Oliver</Name>
      <Boss Id="1">
        <Name>John</Name>
        <Boss xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:entity="1" />
      </Boss>
    </Person>
    <Person xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:entity="1" />
  </Employees>
</Company>

ExtendedXmlSerializerは.net 4.5および.net Coreをサポートしています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top