Vra

Na aanleiding van my vorige vraag Ek het gewerk om my objekmodel te kry om na XML te serialiseer.Maar ek het nou 'n probleem gekry (quelle surprise!).

Die probleem wat ek het, is dat ek 'n versameling het, wat van 'n abstrakte basisklastipe is, wat deur die konkrete afgeleide tipes bevolk word.

Ek het gedink dit sal goed wees om net die XML-kenmerke by al die betrokke klasse by te voeg en alles sal peachy wees.Ongelukkig is dit nie die geval nie!

So ek het 'n paar grawe op Google gedoen en ek verstaan ​​nou hoekom dit werk nie.Daarin die XmlSerializer doen in werklikheid 'n paar slim refleksie om voorwerpe na/van XML te serialiseer, en aangesien dit op die abstrakte tipe gebaseer is, kan dit nie uitvind met wie de hel dit praat nie.Goed.

Ek het afgekom hierdie bladsy op CodeProject, wat lyk of dit baie kan help (nog om ten volle te lees/verbruik), maar ek het gedink ek wil hierdie probleem ook na die StackOverflow-tabel bring, om te sien of jy enige netjiese hacks/tricks het om kry dit op die vinnigste/ligste manier moontlik aan die gang.

Een ding wat ek ook moet byvoeg, is dat ek MOENIE wil afgaan die XmlInclude roete.Daar is eenvoudig te veel koppeling daarmee, en hierdie area van die stelsel is onder swaar ontwikkeling, so dit sal 'n ware instandhoudingshoofpyn wees!

Was dit nuttig?

Oplossing

Probleem opgelos!

OK, so ek het uiteindelik daar gekom (weliswaar met 'n baie van hulp van hier!).

So som op:

Doelwitte:

  • Ek wou nie die afgaan nie Xml Sluit in roete as gevolg van die instandhouding hoofpyn.
  • Sodra 'n oplossing gevind is, wou ek hê dit moet vinnig in ander toepassings geïmplementeer word.
  • Versamelings van abstrakte tipes kan gebruik word, sowel as individuele abstrakte eienskappe.
  • Ek wou nie regtig moeite doen om “spesiale” goed in die betonklasse te doen nie.

Geïdentifiseerde kwessies/punte om op te let:

  • XmlSerializer doen 'n bietjie koel refleksie, maar dit is baie beperk wanneer dit kom by abstrakte tipes (d.w.s.dit sal slegs werk met gevalle van die abstrakte tipe self, nie subklasse nie).
  • Die Xml-kenmerkversierders definieer hoe die XmlSerializer die eienskappe wat sy vonds hanteer, hanteer.Die fisiese tipe kan ook gespesifiseer word, maar dit skep 'n stywe koppeling tussen die klas en die serializer (nie goed nie).
  • Ons kan ons eie XmlSerializer implementeer deur 'n klas te skep wat implementeer IXmlSerialiseerbaar .

Die oplossing

Ek het 'n generiese klas geskep, waarin jy die generiese tipe spesifiseer as die abstrakte tipe waarmee jy sal werk.Dit gee die klas die vermoë om tussen die abstrakte tipe en die konkrete tipe te "vertaal" aangesien ons die gietstuk hard kan kodeer (d.w.s.ons kan meer inligting kry as wat die XmlSerializer kan kry).

Ek het toe die IXmlSerialiseerbaar koppelvlak, dit is redelik reguit vorentoe, maar wanneer ons serialiseer, moet ons verseker dat ons die tipe betonklas na die XML skryf, sodat ons dit kan terugstuur wanneer ons de-serialiseer.Dit is ook belangrik om daarop te let dat dit moet wees ten volle gekwalifiseer aangesien die gemeentes waarin die twee klasse is waarskynlik sal verskil.Daar is natuurlik 'n bietjie tipe kontrolering en goed wat hier moet gebeur.

Aangesien die XmlSerializer nie kan cast nie, moet ons die kode verskaf om dit te doen, sodat die implisiete operateur dan oorlaai word (ek het nooit eers geweet jy kan dit doen nie!).

Die kode vir die AbstractXmlSerializer is hierdie:

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
    }
}

So, van daar af, hoe vertel ons die XmlSerializer om met ons serializer te werk eerder as die verstek?Ons moet ons tipe binne die Xml-kenmerke tipe eiendom deurgee, byvoorbeeld:

[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>();
    }
}

Hier kan jy sien, ons het 'n versameling en 'n enkele eiendom wat blootgestel word, en al wat ons hoef te doen is om die by te voeg tipe benoemde parameter na die XML-verklaring, maklik!:D

LET WEL:As jy hierdie kode gebruik, sal ek 'n shout-out baie waardeer.Dit sal ook help om meer mense na die gemeenskap te dryf :)

Nou, maar onseker oor wat om hier met antwoorde te doen, aangesien hulle almal hul voor- en nadele gehad het.Ek sal diegene wat ek voel nuttig was opgradeer (geen aanstoot vir diegene wat nie was nie) en dit afsluit sodra ek die verteenwoordiger het :)

Interessante probleem en lekker om op te los!:)

Ander wenke

Een ding om na te kyk, is die feit dat jy in die XmlSerialiser-konstruktor 'n verskeidenheid tipes kan slaag wat die serialiseerder dalk sukkel om op te los.Ek moes dit al 'n hele paar keer gebruik waar 'n versameling of komplekse stel datastrukture gerangskik moes word en daardie tipes in verskillende samestellings gewoon het, ens.

XmlSerialiser Constructor met extraTypes-param

EDIT:Ek wil byvoeg dat hierdie benadering die voordeel het bo XmlInclude-kenmerke, ens. dat jy 'n manier kan uitwerk om 'n lys van jou moontlike konkrete tipes tydens looptyd te ontdek en saam te stel en dit in te vul.

Ernstig, 'n uitbreidbare raamwerk van POCO's sal nooit betroubaar na XML serialiseer nie.Ek sê dit omdat ek kan waarborg dat iemand saam sal kom, jou klas sal uitbrei en dit sal mis.

Jy moet kyk na die gebruik van XAML vir die serialisering van jou objekgrafieke.Dit is ontwerp om dit te doen, terwyl XML-serialisering nie is nie.

Die Xaml serializer en deserializer hanteer generiese produkte sonder 'n probleem, versamelings van basisklasse en koppelvlakke ook (solank die versamelings self implementeer IList of IDictionary).Daar is 'n paar waarskuwings, soos om jou leesalleen-versameling-eienskappe te merk met die DesignerSerializationAttribute, maar dit is nie so moeilik om jou kode te herwerk om hierdie hoekgevalle te hanteer nie.

Net 'n vinnige opdatering hieroor, ek het nie vergeet nie!

Doen net nog navorsing, dit lyk of ek 'n wenner is, moet net die kode gesorteer kry.

Tot dusver het ek die volgende:

  • Die XmlSeralizer is basies 'n klas wat 'n paar handige refleksie doen oor die klasse wat dit serialiseer.Dit bepaal die eienskappe wat gerangskik word op grond van die Tik.
  • Die rede waarom die probleem voorkom, is omdat 'n tipe wanverhouding voorkom, dit verwag die Basistipe maar in werklikheid ontvang die Afgeleide Tipe ..Alhoewel jy dalk dink dat dit dit polimorfies sal behandel, is dit nie so nie, aangesien dit 'n hele ekstra vrag van refleksie en tipe-kontrolering sal behels, wat dit nie ontwerp is om te doen nie.

Dit lyk asof hierdie gedrag oorheers kan word (kode hangende) deur 'n instaanklas te skep om op te tree as die tussenganger vir die serializer.Dit sal basies die tipe van die afgeleide klas bepaal en dit dan as normaal seriealiseer.Hierdie instaanbedienerklas sal dan daardie XML-rugsteun na die hoofreeksvoerder voer.

Hou hierdie spasie dop!^_^

Dit is beslis 'n oplossing vir jou probleem, maar daar is 'n ander probleem, wat jou voorneme om "draagbare" XML-formaat te gebruik ietwat ondermyn.Slegte ding gebeur wanneer jy besluit om klasse in die volgende weergawe van jou program te verander en jy moet beide formate van serialisering ondersteun -- die nuwe een en die ou een (omdat jou kliënte steeds hul ou lêers/databasisse gebruik, of hulle koppel aan jou bediener met ou weergawe van jou produk).Maar jy kan nie meer hierdie serialisator gebruik nie, want jy het dit gebruik

type.AssemblyQualifiedName

wat lyk

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

dit bevat jou samestelling-kenmerke en weergawe ...

As jy nou probeer om jou samestelling weergawe te verander, of jy besluit om dit te onderteken, gaan hierdie deserialisering nie werk nie...

Ek het dinge soortgelyk aan hierdie gedoen.Wat ek normaalweg doen, is om seker te maak dat al die XML-serialiseringseienskappe op die betonklas is, en dat die eienskappe van daardie klas net na die basisklasse (waar nodig) deurgeroep word om inligting te haal wat gede/serialiseer sal word wanneer die serialiseerder aanroep daardie eiendomme.Dit is 'n bietjie meer koderingswerk, maar dit werk baie beter as om die serializer te probeer dwing om net die regte ding te doen.

Nog beter, met behulp van notasie:

[XmlRoot]
public class MyClass {
    public abstract class MyAbstract {} 
    public class MyInherited : MyAbstract {} 
    [XmlArray(), XmlArrayItem(typeof(MyInherited))] 
    public MyAbstract[] Items {get; set; } 
}
Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top