如何在XML

时间:2018-03-08 17:18:53

标签: c# .net xml recursion ixmlserializable

我想知道当我的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>();
    }
}

与继承的接口IWidget相比,Section类还有一个属性,即 subsection属性

然后我准备从这个示例XmlSerializer serialize generic List of interface

开始移动我的步骤

但我真的不明白如何管理代码中的<section>元素,目前它只不过是该类的签名:

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)
    {

    }

}

非常感谢您的建议!

2 个答案:

答案 0 :(得分:1)

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

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')。

然后,您可以创建三个子类型:

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是相同的,从而证明了这一点。

答案 1 :(得分:0)

下面我使用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;
                }
            }

        }

    }
}