XML 如何为递归标签实现 IXmlSerializable

How to implement IXmlSerializable for recursive tags in XML

我想知道当我的 XML 包含递归标签时如何实现 IXmlSerializable 接口的 ReadXml 方法,如下例所示:

    <?xml version='1.0' encoding='utf-8'?>
<dform>
    <label name='label-a' text='A' dbpath='module/label-a'/>
    <textmemo name='textmemo-a' text='' dbpath='module/textmemo-a'/>
    <section name='section-a' text='' dbpath='module/section-a'>
        <textmemo name='textmemo-b' text='' dbpath='module/textmemo-b'/>
    </section>
    <section name='section-b' text='' dbpath='module/section-b'>
        <textmemo name='textmemo-c' text='' dbpath='module/textmemo-c'/>
        <label name='label-c' text='A' dbpath='module/label-c'/>
        <section name='section-c' text='' dbpath='module/section-c'>
            <label name='label-d' text='A' dbpath='module/label-d'/>
        </section>
    </section>
</dform>

元素 <section> 就像一个容器,可以容纳所有类型的元素,它本身也包括在内。 我需要将这个结构翻译成对象:当元素是 Section 时,包含其他 List 对象的元素(对象)列表。所以我创建了以下界面和 类:

public interface IWidget
{       
    String type { get;}
    String name { get; set; }
    String labelCation { get; set; }
    String text { get; set; }
    String dbpath { get; set; }

}

class Textmemo : IWidget
{
    public String type { get; }
    public String name { get; set; }
    public String labelCation { get; set; }
    public String text { get; set; }
    public String dbpath { get; set; }
    public List<IWidget> subsection { get; set; }

    public Textmemo()
    {
        type = "CONTROL";
    }

}



class Label : IWidget
{
    public String type { get; }
    public String name { get; set; }
    public String labelCation { get; set; }
    public String text { get; set; }
    public String dbpath { get; set; }
    public List<IWidget> subsection { get; set; }

    public Label()
    {
        type = "LABEL";
    }
}

class Section : IWidget
{
    public String type { get; }
    public String name { get; set; }
    public String labelCation { get; set; }
    public String text { get; set; }
    public String dbpath { get; set; }
    public List<IWidget> subsection { get; set; }

    public Section()
    {
        type = "SECTION";
        subsection = new List<IWidget>();
    }
}

class段比其继承的接口IWidget多属性,即小段属性

然后我准备从这个例子开始移动我的步骤XmlSerializer serialize generic List of interface

但我真的不明白如何在我的代码中管理 <section> 元素,目前,它只不过是 class:[=16 的签名=]

public class WidgetsList: List<IWidget>, IXmlSerializable
{
    public WidgetsList() : base() { }

    public System.Xml.Schema.XmlSchema GetSchema() { return null; }

    public void ReadXml(XmlReader reader)
    {

    }

    public void WriteXml(XmlWriter writer)
    {

    }

}

非常感谢您的建议!

下面我使用 Xml Linq 编写了解析器

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

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";

        static void Main(string[] args)
        {

            new Widget(FILENAME);
        }
    }
    public class Widget
    {
        public static Widget root = new Widget();

        public String type { get; set; }
        public String name { get; set; }
        public String labelCation { get; set; }
        public String text { get; set; }
        public String dbpath { get; set; }
        public List<Widget> subsection { get; set; }

        public Widget() { }
        public Widget(string filename)
        {
            XDocument doc = XDocument.Load(filename);
            XElement root = doc.Root;
            RecursiveParse(root, Widget.root);

        }
        public static void RecursiveParse(XElement xParent, Widget textParent)
        {
            foreach (XElement child in xParent.Elements())
            {
                string elementName = child.Name.LocalName;

                switch (elementName)
                {
                    case "label" :
                        textParent.name = (string)child.Attribute("name");
                        textParent.labelCation = (string)child.Attribute("text");
                        textParent.dbpath = (string)child.Attribute("dbpath");

                        break;
                    case "section" :
                        if (textParent.subsection == null) textParent.subsection = new List<Widget>();

                        Widget childSection = new Widget();
                        textParent.subsection.Add(childSection);
                        RecursiveParse(child, childSection);

                        break;
                    default :
                        textParent.text = (string)child.Attribute("text");
                        textParent.labelCation = elementName;
                        break;
                }
            }

        }

    }
}

为什么要自己实现 IXmlSerializable?您可以使用各种属性让框架为您执行此操作。例如,定义一个基数 class(假定您的所有实现都相同):

public abstract class Widget
{
    [XmlIgnore]
    public abstract string type { get;}
    [XmlAttribute]
    public string name { get; set; }
    [XmlIgnore]
    public string labelCation { get; set; }
    [XmlAttribute]
    public string text { get; set; }
    [XmlAttribute]
    public string dbpath { get; set; }
}

这里的属性指定 typelabelCation 被忽略(因为它们不存在于 XML 中)。其余映射到 XML 属性(例如 name='abc')。

然后您可以创建您的三个 sub-types:

public class Textmemo : Widget
{
    public override string type { get; } = "CONTROL";
}

public class Label : Widget
{
    public override string type { get; } = "LABEL";
}

public class Section : Widget
{
    public override string type { get; } = "SECTION";

    [XmlElement("textmemo", Type=typeof(Textmemo))]
    [XmlElement("label", Type=typeof(Label))]
    [XmlElement("section", Type=typeof(Section))]
    public List<Widget> subsection { get; } = new List<Widget>();
}

Section 很有趣,因为它定义了允许作为具有相关名称映射的子元素的类型。与此类似,然后您将定义根元素:

[XmlRoot("dform")]
public class DForm
{
    [XmlElement("textmemo", Type=typeof(Textmemo))]
    [XmlElement("label", Type=typeof(Label))]
    [XmlElement("section", Type=typeof(Section))]
    public List<Widget> Widgets { get; } = new List<Widget>();
}

您可以在 this demo 中看到您的 XML round-tripped 通过序列化程序是相同的,从而证明这是有效的。