سؤال

وبعد يوم من السؤال السابق لقد تم العمل على الحصول على بلدي طراز كائن إلى تسلسل إلى XML.ولكن لدي الآن واجهت مشكلة (quelle مفاجأة!).

المشكلة لدي هو أن لدي مجموعة ، وهي مجردة قاعدة الطبقة التي يتم ملؤها من قبل ملموسة مستمدة أنواع.

اعتقدت انه سيكون على ما يرام فقط إضافة سمات XML لجميع الفئات المشاركة و كل شيء سيكون بخير.للأسف هذا ليس هو الحال!

لذلك يجب القيام ببعض البحث على جوجل و أنا الآن أفهم لماذا انها لا تعمل.في هذا على XmlSerializer هو في الواقع القيام ببعض ذكي التفكير في تسلسل الكائنات من XML ، ومنذ تأسيسها على أساس نوع مجردة ، فإنه لا يمكن معرفة ماذا يتحدث.بخير

أنا لم تأتي عبر هذه الصفحة على CodeProject ، والذي يبدو أنه قد يساعد الكثير (بعد قراءة/تستهلك بالكامل), ولكن أعتقد أود أن تجلب هذه المشكلة إلى ستاكوفيرفلوو الجدول أيضا أن نرى إذا كان لديك أي أنيق الخارقة/الحيل من أجل الحصول على هذا وتشغيلها في أسرع/أخف طريقة ممكنة.

شيء واحد أود أن أضيف أيضا أنني لا أريد أن أذهب إلى أسفل XmlInclude الطريق.هناك ببساطة الكثير من اقتران مع هذه المنطقة من النظام تحت التطوير الثقيلة ، لذلك سيكون الحقيقي صيانة الصداع!

هل كانت مفيدة؟

المحلول

حل المشكلة!

حسنا, لقد حصلت أخيرا هناك (المسلم مع الكثير من المساعدة من هنا!).

لذا تلخيص:

الأهداف:

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

تحديد القضايا/نقاط ملاحظة:

  • XmlSerializer هل بعض باردة جدا التفكير ، بل هو جدا محدودة عندما يتعلق الأمر المجرد أنواع (أيوسوف تعمل فقط مع الحالات مجردة من النوع نفسه ، وليس فرعية).
  • Xml السمة الديكور تحديد كيفية XmlSerializer يعامل خصائص يجد لها.المادية نوع كما يمكن تحديد, ولكن هذا يخلق ضيق اقتران بين فئة مسلسل (غير جيد).
  • يمكننا تنفيذ منطقتنا XmlSerializer طريق إنشاء الفئة التي تطبق IXmlSerializable .

الحل

أنا خلقت فئة عامة ، والتي يمكنك تحديد نوع عام كما المجرد نوع سوف تعمل مع.وهذا يعطي الدرجة القدرة على "ترجمة" بين مجردة نوع الخرسانة نوع بما يمكننا من الصعب رمز الصب (أييمكننا الحصول على المزيد من المعلومات من XmlSerializer يمكن).

ثم نفذت IXmlSerializable واجهة هذا هو مستقيم جدا إلى الأمام ، ولكن عندما التسلسلية نحن بحاجة للتأكد من أننا الكتابة نوع من الخرسانة فئة إلى XML ، حتى أننا يمكن أن يلقي عليه مرة أخرى عندما de-التسلسلية.ومن المهم أيضا أن نلاحظ أنه يجب أن يكون المؤهل بالكامل كما التجميعات التي فئتين في من المرجح أن تختلف.هناك بالطبع قليلا التحقق من نوع الأشياء يجب أن يحدث هنا.

منذ XmlSerializer لا يلقي نحن بحاجة إلى توفير رمز ذلك حتى ضمني المشغل ثم طاقتها (أنا لم أعرف حتى أنك يمكن أن تفعل هذا!).

رمز AbstractXmlSerializer هو هذا:

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

namespace Utility.Xml
{
    public class AbstractXmlSerializer<AbstractType> : IXmlSerializable
    {
        // Override the Implicit Conversions Since the XmlSerializer
        // Casts to/from the required types implicitly.
        public static implicit operator AbstractType(AbstractXmlSerializer<AbstractType> o)
        {
            return o.Data;
        }

        public static implicit operator AbstractXmlSerializer<AbstractType>(AbstractType o)
        {
            return o == null ? null : new AbstractXmlSerializer<AbstractType>(o);
        }

        private AbstractType _data;
        /// <summary>
        /// [Concrete] Data to be stored/is stored as XML.
        /// </summary>
        public AbstractType Data
        {
            get { return _data; }
            set { _data = value; }
        }

        /// <summary>
        /// **DO NOT USE** This is only added to enable XML Serialization.
        /// </summary>
        /// <remarks>DO NOT USE THIS CONSTRUCTOR</remarks>
        public AbstractXmlSerializer()
        {
            // Default Ctor (Required for Xml Serialization - DO NOT USE)
        }

        /// <summary>
        /// Initialises the Serializer to work with the given data.
        /// </summary>
        /// <param name="data">Concrete Object of the AbstractType Specified.</param>
        public AbstractXmlSerializer(AbstractType data)
        {
            _data = data;
        }

        #region IXmlSerializable Members

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null; // this is fine as schema is unknown.
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            // Cast the Data back from the Abstract Type.
            string typeAttrib = reader.GetAttribute("type");

            // Ensure the Type was Specified
            if (typeAttrib == null)
                throw new ArgumentNullException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
                    "' because no 'type' attribute was specified in the XML.");

            Type type = Type.GetType(typeAttrib);

            // Check the Type is Found.
            if (type == null)
                throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
                    "' because the type specified in the XML was not found.");

            // Check the Type is a Subclass of the AbstractType.
            if (!type.IsSubclassOf(typeof(AbstractType)))
                throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
                    "' because the Type specified in the XML differs ('" + type.Name + "').");

            // Read the Data, Deserializing based on the (now known) concrete type.
            reader.ReadStartElement();
            this.Data = (AbstractType)new
                XmlSerializer(type).Deserialize(reader);
            reader.ReadEndElement();
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            // Write the Type Name to the XML Element as an Attrib and Serialize
            Type type = _data.GetType();

            // BugFix: Assembly must be FQN since Types can/are external to current.
            writer.WriteAttributeString("type", type.AssemblyQualifiedName);
            new XmlSerializer(type).Serialize(writer, _data);
        }

        #endregion
    }
}

لذا ، من هناك ، كيف نقول XmlSerializer إلى العمل مع شركائنا في مسلسل بدلا من الافتراضي ؟ يجب أن تمر لدينا نوع داخل Xml سمات نوع المنشأة, على سبيل المثال:

[XmlRoot("ClassWithAbstractCollection")]
public class ClassWithAbstractCollection
{
    private List<AbstractType> _list;
    [XmlArray("ListItems")]
    [XmlArrayItem("ListItem", Type = typeof(AbstractXmlSerializer<AbstractType>))]
    public List<AbstractType> List
    {
        get { return _list; }
        set { _list = value; }
    }

    private AbstractType _prop;
    [XmlElement("MyProperty", Type=typeof(AbstractXmlSerializer<AbstractType>))]
    public AbstractType MyProperty
    {
        get { return _prop; }
        set { _prop = value; }
    }

    public ClassWithAbstractCollection()
    {
        _list = new List<AbstractType>();
    }
}

هنا يمكنك ان ترى لدينا مجموعة واحدة المنشأة التعرض ، و كل ما علينا فعله هو إضافة نوع اسمه المعلمة إلى تعريف Xml, سهلة!:D

ملاحظة:إذا كنت تستخدم هذا الرمز ، أنا حقا نقدر يصرخ.وسوف يساعد أيضا في دفع المزيد من الناس إلى المجتمع :)

الآن, ولكن غير متأكد ما يجب القيام به مع الإجابات هنا منذ أنهم جميعا قد برو ويخدع.سوف upmod تلك التي أشعر مفيدة (لا جريمة تلك التي لم تكن) وإغلاق هذا مرة واحدة لدي rep :)

مشكلة مثيرة للاهتمام ومتعة جيدة إلى حل!:)

نصائح أخرى

شيء واحد أن ننظر إلى حقيقة أنه في XmlSerialiser منشئ يمكنك تمرير مجموعة من الأنواع التي serialiser يمكن أن تواجه صعوبة في حل.لقد كان استخدام مرات قليلة جدا حيث مجموعة أو مجموعة معقدة من datastructures يلزم تسلسل و هذه الأنواع تعيش في مختلف المجالس.... الخ

XmlSerialiser منشئ مع extraTypes المعلمة

تحرير:وأود أن أضيف أن هذا النهج قد يعود بالنفع على XmlInclude الصفات وغيرها التي يمكنك العمل بها وسيلة لاكتشاف و تجميع قائمة من الممكن ملموسة أنواع في وقت التشغيل و الاشياء لهم في.

على محمل الجد ، الموسعة إطار POCOs ' لن تسلسل إلى XML بشكل موثوق.أقول هذا لأنني يمكن أن يضمن شخص ما سوف يأتي على طول ، تمتد الطبقة ، أفسدتها.

يجب أن ننظر إلى استخدام XAML عن التسلسلية الخاصة بك وجوه الرسوم البيانية.وهي مصممة للقيام بذلك ، بينما XML التسلسل لا.

Xaml مسلسل و deserializer مقابض الأدوية دون مشكلة, مجموعات من الفئات الأساسية وكذلك واجهات (طالما مجموعات أنفسهم تنفيذ IList أو IDictionary).هناك بعض المحاذير ، مثل وضع العلامات الخاصة بك للقراءة فقط جمع خصائص مع DesignerSerializationAttribute, لكن إعادة صياغة التعليمات البرمجية الخاصة بك للتعامل مع هذه الحالات الزاوية ليس من الصعب أن.

مجرد تحديث سريع على هذا, أنا لم ننس!

فقط تفعل المزيد من البحوث يبدو أنا على الفائز ، فقط تحتاج إلى الحصول على رمز فرز.

حتى الآن لدي التالية:

  • على XmlSeralizer هو في الأساس هذا بعض أنيق التفكير في الطبقات هو التسلسلية.فإنه يحدد الخصائص التي هي تسلسل بناء على نوع.
  • السبب تحدث هذه المشكلة بسبب عدم تطابق النوع يحدث ، فمن تتوقع BaseType ولكن في الواقع يتلقى DerivedType ..بينما كنت قد تعتقد أنه سيكون التعامل معها polymorphically, لا لأنه ينطوي على كل حمولة اضافية تفكير و تدقيق النوع التي لا تهدف إلى القيام به.

هذا السلوك يبدو أن تكون قادرة على تجاوزها (رمز انتظار) من خلال خلق فئة وكيل بمثابة الوسيط بين مسلسل.وهذا في الأساس تحديد نوع فئة مشتقة ثم تسلسل ذلك كالمعتاد.هذه فئة وكيل ثم سيتم تغذية XML العودة إلى الخط الرئيسي مسلسل..

مشاهدة هذا الفضاء!^_^

انها بالتأكيد حل المشكلة الخاصة بك, ولكن هناك مشكلة أخرى ، وهو ما يقوض نية لاستخدام "المحمولة" تنسيق XML.شيء سيء يحدث عندما تقرر تغيير الفصول في النسخة القادمة من البرنامج و تحتاج إلى دعم كل صيغ التسلسل -- الجديد و القديم (لأن العملاء لا تزال تستخدم ثير الملفات القديمة/قواعد البيانات أو الاتصال على الخادم الخاص بك باستخدام النسخة القديمة من المنتج الخاص بك).ولكن لا يمكنك استخدام هذا serializator بعد الآن لأنك كنت

type.AssemblyQualifiedName

الذي يبدو وكأنه

TopNamespace.SubNameSpace.ContainingClass+NestedClass, MyAssembly, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089

الذي يحتوي على التجميع الخاص بك سمات الإصدار...

الآن إذا كنت تحاول تغيير إصدار التجميع ، أو أن تقرر أن توقع هذا إلغاء التسلسل هو عدم الذهاب إلى العمل...

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

حتى أفضل ، باستخدام التدوين:

[XmlRoot]
public class MyClass {
    public abstract class MyAbstract {} 
    public class MyInherited : MyAbstract {} 
    [XmlArray(), XmlArrayItem(typeof(MyInherited))] 
    public MyAbstract[] Items {get; set; } 
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top