.net XMLシリアル化-オブジェクトコピーの代わりに参照を保存する
-
06-07-2019 - |
質問
- .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をサポートしています。