XML-сериализация списков

У меня есть xml в формате

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

Я пытаюсь сериализовать это с помощью С# и интерфейса IXmlSerializable. У меня есть три класса, все из которых наследуют интерфейс IXmlSerializable, мое намерение состоит в том, чтобы XML был прочитан самым верхним классом, и он будет циклически передавать xml типа «список» в сериализатор дочерних объектов. Сериализация «списка», затем, в свою очередь, зацикливает все записи «функций». Ниже приведена урезанная версия моего кода.

Я пробовал несколько подходов к зацикливанию, но всегда попадал либо в бесконечный цикл, либо в ошибку из-за попытки сериализовать неправильный бит xml в неправильном типе, либо достигал конца после пропуска целых списков.

Я новичок в сериализации Xml, и этот подход наивен, я готов принять любые предложения.

Этот XML, вероятно, изменится в будущем (больше атрибутов, типов элементов и т. д.), и поэтому он должен поддерживаться, я не могу гарантировать, что пустые элементы также не будут присутствовать.

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
}

person Luke Turner    schedule 18.07.2013    source источник
comment
Я слышал об использовании свойств С# для прямой сериализации объектов, подойдет ли это в случае класса Feature?   -  person Luke Turner    schedule 18.07.2013
comment
Вы пытаетесь сериализовать это в уже определенный объект С#?   -  person AntLaC    schedule 18.07.2013
comment
В Visual Studio вы можете щелкнуть правой кнопкой мыши файл XML и сказать «создать схему». Он создаст XSD для вас. Затем вы можете использовать такой инструмент, как XSD2Code, для создания класса C#, который будет сериализоваться таким образом.   -  person K0D4    schedule 18.07.2013
comment
Я пытаюсь сериализовать это в объект ac#, который я определяю, приведенный выше код урезан, и в трех классах на самом деле гораздо больше кода, чем автоматическая генерация, я знаю о таких инструментах, но не имел большого успеха в прошлое с ними. Кроме того, как я уже упоминал, этот XML может быть изменен, и повторная интеграция всего класса каждый раз, когда происходит изменение, может привести к серьезным проблемам с обслуживанием.   -  person Luke Turner    schedule 18.07.2013


Ответы (1)


Если у вас нет действительно веских причин для реализации IXmlSerializable, я бы просто использовал XmlSerializer и соответствующие атрибуты в классах.

Основываясь на данном примере XML, это должно сделать это. Обратите внимание, что мне пришлось переименовать два атрибута attr в манифесте, потому что наличие нескольких атрибутов с одинаковыми именами недопустимо.

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

Используйте такой код:

var stream = ... // open the XML
var serializer = new XmlSerializer(typeof (Manifest));
var manifest = (Manifest) serializer.Deserialize(stream);
person Tim B    schedule 18.07.2013
comment
Шикарное спасибо, сейчас попробую. У меня нет веских причин для реализации IXmlSerializable, это была просто отправная точка из некоторого кода, который я объединил вместе, чтобы создать сериализуемый тип Dictionary. - person Luke Turner; 18.07.2013
comment
Я ищу сейчас, но если кто-то другой быстрее, и чтобы помочь другим в будущем, какая директива использования мне нужна для атрибута [Serializable]? - person Luke Turner; 18.07.2013
comment
Ах, хорошо, это использование System; - person Luke Turner; 18.07.2013
comment
В ответ добавлен импорт пространства имен. - person Tim B; 18.07.2013