سؤال

  • في تطبيق ‎.Net/C#، لدي هياكل بيانات تشير إلى بعضها البعض.
  • عندما أقوم بإجراء تسلسل لها، يقوم .Net بتسلسل كافة المراجع بنسخ كائنات منفصلة.
  • في المثال التالي، أحاول إجراء تسلسل إلى صفيف "الشخص"
  • قد يشير "الشخص" إلى شخص آخر.

    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>
    
  • في الكود أعلاه، يوجد نفس كائن "Mike" في مكانين، نظرًا لوجود مرجعين لنفس الكائن.

  • أثناء إلغاء التسلسل، يصبحان كائنين مختلفين، وهي ليست الحالة الدقيقة عند إجراء التسلسل.
  • أريد تجنب ذلك والحصول على نسخة فقط من الكائن في ملف XML المتسلسل، ويجب أن تشير جميع المراجع إلى هذه النسخة.بعد إلغاء التسلسل، أريد العودة، نفس بنية البيانات القديمة.
  • هل هو ممكن ؟
هل كانت مفيدة؟

المحلول

ليس من الممكن مع XmlSerializer.يمكنك تحقيق ذلك مع DataContractSerializer باستخدام 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
    }
};

يجب عليك تكوين فئة الشخص ككائن مرجعي:

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