سؤال

I have xml in the format of

<?xml version="1.0" ?>
<manifest attr="TEXT" attr="TEXT" attr="TEXT">
<list name="TRIM">
    <feature id="TEXT"/>
    <feature id="TEXT"/>
    <feature id="TEXT"/>
    <feature id="TEXT"/>
    <feature id="TEXT"/>
</list>
<list attr="TEXT">
    <feature id="TEXT"/>
    <feature id="TEXT"/>
</list>
<list attr="TEXT"/>
<list attr="TEXT">
    <feature id="TEXT" attr="TEXT"/>
    <feature id="TEXT" attr="TEXT"/>
</list>
</manifest>

I am trying to serialise this using C# and the IXmlSerializable interface. I have three classes, all of which inherit the interface IXmlSerializable, my intention being that the XML will be read in by the topmost class, and it will loop through passing xml of type "list" into the child object serialiser. The "list" serialise then in turn loops all "feature" entries. Below is a cut down version of my code.

I have tried several approaches to the looping, but always end up in either an infinite loop, an error due to trying to serialise the wrong bit of xml in the wrong type, or reach the end after skipping entire lists.

I am new to Xml serialisation, and this approach is naïve, I am willing to take any suggestions.

This XML is likely to change in the future (more attributes, element types etc.) and so must be maintainable, I cannot guarantee that empty elements will not be present either.

using UnityEngine;
using System.Collections;
using System.Xml.Serialization;

[XmlRoot("partManifest")]
public class ModelManifest : IEnumerator, IEnumerable, IXmlSerializable {

    [XmlRoot("feature")]
    public class Feature : IXmlSerializable
    {
        string m_id;
        string m_description;

        #region IXmlSerializable implementation
        System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema ()    
        {
            throw new System.NotImplementedException ();
        }

        void System.Xml.Serialization.IXmlSerializable.ReadXml (System.Xml.XmlReader reader)
        {       
        m_id = reader.GetAttribute("id");
        }

        void System.Xml.Serialization.IXmlSerializable.WriteXml (System.Xml.XmlWriter writer)
        {
        throw new System.NotImplementedException ();
        }
        #endregion
    }

    [XmlRoot("feature-list")]
    public class FeatureList : IXmlSerializable
    {
        string m_name;

        System.Collections.Generic.List<Feature> m_features = new System.Collections.Generic.List<Feature>();

        #region IXmlSerializable implementation
        public System.Xml.Schema.XmlSchema GetSchema ()
        {
            throw new System.NotImplementedException ();
        }

        public void ReadXml (System.Xml.XmlReader reader)
        {               
            XmlSerializer valueSerializer = new XmlSerializer(typeof(Feature));

            // Will return if no features present
            if(reader.IsEmptyElement)
                return;

            reader.ReadStartElement("feature-list");
            while(true)
            {
                m_features.Add ( (Feature)valueSerializer.Deserialize(reader) );
                i++;
                bool l_isAnotherSibling = reader.ReadToNextSibling("feature");

                if(!l_isAnotherSibling)
                    break;
            }
            Debug.Log (i.ToString() + " Features");
        }

        public void WriteXml (System.Xml.XmlWriter writer)
        {
            throw new System.NotImplementedException ();
        }
        #endregion
    }

    System.Collections.Generic.List<FeatureList> m_featureLists = new System.Collections.Generic.List<FeatureList>();

    #region IXmlSerializable implementation
    public System.Xml.Schema.XmlSchema GetSchema ()
    {
        throw new System.NotImplementedException ();
    }

    public void ReadXml (System.Xml.XmlReader reader)
    {       
        XmlSerializer valueSerializer = new XmlSerializer(typeof(FeatureList));

        if(reader.IsEmptyElement)
            return;

        reader.ReadStartElement("partManifest");

        while (true)
        {               
            m_featureLists.Add ( (FeatureList)valueSerializer.Deserialize(reader) );

            //bool l_isAnotherSibling = reader.ReadToNextSibling("feature-list");

            //if(!l_isAnotherSibling)
            //  break;
            if(reader.NodeType == System.Xml.XmlNodeType.EndElement)
                break;

            if(Input.GetKeyUp(KeyCode.A))
                break;
        }

        reader.ReadEndElement();
    }

    public void WriteXml (System.Xml.XmlWriter writer)
    {
        throw new System.NotImplementedException ();
    }
    #endregion
}
هل كانت مفيدة؟

المحلول

Unless you have a really good reason for needing to implement IXmlSerializable, I would just use XmlSerializer and appropriate attributes on classes.

Based on the given XML example, this should do it. Note that I had to rename two of the attr attributes on manifest, because having multiple attributes with the same name is invalid.

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

[Serializable]
[XmlRoot("manifest")]
public class Manifest
{
    [XmlElement("list")]
    public List<FeatureList> FeatureLists { get; set; }

    [XmlAttribute("attr")]
    public string Attr { get; set; }

    [XmlAttribute("attr2")]
    public string Attr2 { get; set; }

    [XmlAttribute("attr3")]
    public string Attr3 { get; set; }
}

[Serializable]
public class FeatureList
{
    [XmlElement("feature")]
    public List<Feature> Features { get; set; }

    [XmlAttribute("name")]
    public string Name { get; set; }

    [XmlAttribute("attr")]
    public string Attr { get; set; }
}

[Serializable]
public class Feature
{
    [XmlAttribute("id")]
    public string Id { get; set; }

    [XmlAttribute("attr")]
    public string Attr { get; set; }
}

Use the code like this:

var stream = ... // open the XML
var serializer = new XmlSerializer(typeof (Manifest));
var manifest = (Manifest) serializer.Deserialize(stream);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top