如何获取包含嵌套元素的XML文件并从中获取一组C#类?

如何获取包含嵌套元素的XML文件并从中获取一组C#类?,c#,xml,xsd,C#,Xml,Xsd,首先,我对XML不是很有经验。我知道阅读和写作的基本知识,但在大多数情况下,像模式这样的东西开始让我的眼睛快速交叉。如果看起来我对XML的工作原理做出了错误的假设,那么很有可能是这样 撇开那个免责声明不谈,这个问题我已经遇到好几次了,但都没有找到一个令人满意的解决方案。我有一个XML,它定义了数据,包括嵌套的条目(举个例子,一个文件可能有一个“Power”元素,它有一个“AlternatePowers”的子节点,而这个子节点又包含“Power”元素)。理想情况下,我希望能够从这个XML文件生成一

首先,我对XML不是很有经验。我知道阅读和写作的基本知识,但在大多数情况下,像模式这样的东西开始让我的眼睛快速交叉。如果看起来我对XML的工作原理做出了错误的假设,那么很有可能是这样

撇开那个免责声明不谈,这个问题我已经遇到好几次了,但都没有找到一个令人满意的解决方案。我有一个XML,它定义了数据,包括嵌套的条目(举个例子,一个文件可能有一个“Power”元素,它有一个“AlternatePowers”的子节点,而这个子节点又包含“Power”元素)。理想情况下,我希望能够从这个XML文件生成一组快速的类来存储我正在读取的数据。我看到的一般解决方案是使用Microsoft的XSD.exe工具从XML文件生成XSD文件,然后使用相同的工具将模式转换为类。问题是,如果存在嵌套元素,工具将阻塞。例如:

- A column named 'Power' already belongs to this DataTable: cannot set 
a nested table name to the same name.
有没有一个简单的好方法?我在这里搜索了两个类似的问题,但是我发现的唯一一个关于使用相同名称的嵌套元素生成模式的问题没有得到回答

另外,我也可能完全误解了XML和XSD的工作原理,不可能有这样的嵌套

更新

例如,我想解析的内容之一是特定字符生成器程序的XML输出。公平的警告,这是有点罗嗦,尽管我删除了任何东西,但权力部分

<?xml version="1.0" encoding="ISO-8859-1"?>
<document>
  <product name="Hero Lab" url="http://www.wolflair.com" versionmajor="3" versionminor="7" versionpatch=" " versionbuild="256">Hero Lab® and the Hero Lab logo are Registered Trademarks of LWD Technology, Inc. Free download at http://www.wolflair.com
    Mutants &amp; Masterminds, Second Edition is ©2005-2011 Green Ronin Publishing, LLC. All rights reserved.</product>
  <hero active="yes" name="Pretty Deadly" playername="">
    <size name="Medium"/>
    <powers>
      <power name="Enhanced Trait 16" info="" ranks="16" cost="16" range="" displaylevel="0" summary="Traits: Constitution +6 (18, +4), Dexterity +8 (20, +5), Charisma +2 (12, +1)" active="yes">
        <powerdesc>You have an enhancement to a non-effect trait, such as an ability (including saving throws) or skill (including attack or defense bonus). Since Toughness save cannot be increased on its own,use the Protection effect instead of Enhanced Toughness (see Protection later in this chapter).</powerdesc>
        <descriptors/>
        <elements/>
        <options/>
        <traitmods>
          <traitmod name="Constitution" bonus="+6"/>
          <traitmod name="Dexterity" bonus="+8"/>
          <traitmod name="Charisma" bonus="+2"/>
        </traitmods>
        <flaws/>
        <powerfeats/>
        <powerdrawbacks/>
        <usernotes/>
        <alternatepowers/>
        <chainedpowers/>
        <otherpowers/>
      </power>
      <power name="Sailor Suit (Device 2)" info="" ranks="2" cost="8" range="" displaylevel="0" summary="Hard to lose" active="yes">
        <powerdesc>A device that has one or more powers and can be equipped and un-equipped.</powerdesc>
        <descriptors/>
        <elements/>
        <options/>
        <traitmods/>
        <flaws/>
        <powerfeats/>
        <powerdrawbacks/>
        <usernotes/>
        <alternatepowers/>
        <chainedpowers/>
        <otherpowers>
          <power name="Protection 6" info="+6 Toughness" ranks="6" cost="10" range="" displaylevel="1" summary="+6 Toughness; Impervious [4 ranks only]" active="yes">
            <powerdesc>You're particularly resistant to harm. You gain a bonus on your Toughness saving throws equal to your Protection rank.</powerdesc>
            <descriptors/>
            <elements/>
            <options/>
            <traitmods/>
            <extras>
              <extra name="Impervious" info="" partialranks="2">Your Protection stops some damage completely. If an attack has a damage bonus less than your Protection rank, it inflicts no damage (you automatically succeed on your Toughness saving throw). Penetrating damage (see page 112) ignores this modifier; you must save against it normally.</extra>
            </extras>
            <flaws/>
            <powerfeats/>
            <powerdrawbacks/>
            <usernotes/>
            <alternatepowers/>
            <chainedpowers/>
            <otherpowers/>
          </power>
        </otherpowers>
      </power>
    </powers>
  </hero>
</document>

我刚刚帮了一个人。请尝试在此处阅读此线程:

从你的例子和我的链接来看,你会有这样的类

public class Power
{
    XElement self;

    public Power(XElement power) { self = power; }

    public AlternatePowers AlternatePowers
    { get { return new AlternatePowers(self.Element("AlternatePowers")); } }
}

public class AlternatePowers
{
    XElement self;

    public AlternatePowers(XElement power) { self = power; }

    public Power2[] Powers
    { 
        get 
        { 
            return self.Elements("Power").Select(e => new Power2(e)).ToArray();
        }
    }
}

public class Power2
{
    XElement self;

    public Power2(XElement power) { self = power; }
}
在不了解xml其余部分的情况下,我无法生成构成每个类/节点级别的属性,但您应该从这里和链接中获得要点

然后您可以这样引用它:

Power power = new Power(XElement.Load("file"));
foreach(Power2 power2 in power.AlternatePowers.Powers)
{
    ...
}

我刚刚帮了一个人。请尝试在此处阅读此线程:

从你的例子和我的链接来看,你会有这样的类

public class Power
{
    XElement self;

    public Power(XElement power) { self = power; }

    public AlternatePowers AlternatePowers
    { get { return new AlternatePowers(self.Element("AlternatePowers")); } }
}

public class AlternatePowers
{
    XElement self;

    public AlternatePowers(XElement power) { self = power; }

    public Power2[] Powers
    { 
        get 
        { 
            return self.Elements("Power").Select(e => new Power2(e)).ToArray();
        }
    }
}

public class Power2
{
    XElement self;

    public Power2(XElement power) { self = power; }
}
在不了解xml其余部分的情况下,我无法生成构成每个类/节点级别的属性,但您应该从这里和链接中获得要点

然后您可以这样引用它:

Power power = new Power(XElement.Load("file"));
foreach(Power2 power2 in power.AlternatePowers.Powers)
{
    ...
}

错误消息意味着您正试图从架构(
/d
开关)生成一个
数据集(
/d
开关),而不是一组用XML序列化程序属性修饰的任意类(
/c
开关)

我自己也没有尝试过生成这样的
数据集
,但我知道它可能会失败。
DataSet
DataTable
s的集合,而DataTable又包含
DataRow
s的集合。这是一个固定的三级层次结构。如果您的XML模式深度大于或小于3级,那么它将不适合所需的结构。尝试在设计器中创建测试
数据集
,并检查生成的
.xsd
文件;这将向您展示适合哪种模式结构


根据个人经验,我可以向您保证,如果您将模式转换为一组任意类,那么它将处理您想要抛出的几乎任何模式结构。

您的错误消息意味着您正试图从模式(
/d
开关)生成一个
数据集,与用XML序列化程序属性(
/c
开关)修饰的一组任意类相反

我自己也没有尝试过生成这样的
数据集
,但我知道它可能会失败。
DataSet
DataTable
s的集合,而DataTable又包含
DataRow
s的集合。这是一个固定的三级层次结构。如果您的XML模式深度大于或小于3级,那么它将不适合所需的结构。尝试在设计器中创建测试
数据集
,并检查生成的
.xsd
文件;这将向您展示适合哪种模式结构


根据个人经验,我可以向您保证,如果您将模式转换为一组任意类,那么它将处理您想要抛出的几乎任何模式结构。

因此,它并不漂亮,但以下是我作为解决方案得出的结论。我在基本节点上运行processElement,然后检查现存元素并导出类代码

namespace XMLToClasses
{
    public class Element
    {
        public string Name;
        public HashSet<string> attributes;
        public HashSet<string> children;

        public bool hasText;

        public Element()
        {
            Name = "";

            attributes = new HashSet<string>();
            children = new HashSet<string>();

            hasText = false;
        }

    public string getSource()
        {
            StringBuilder sourceSB = new StringBuilder();

            sourceSB.AppendLine("[Serializable()]");
            sourceSB.AppendLine("public class cls_" + Name);
            sourceSB.AppendLine("{");

            sourceSB.AppendLine("\t// Attributes" );

            if (hasText)
            {
                sourceSB.AppendLine("\tstring InnerText;");
            }

            foreach(string attribute in attributes)
            {
                sourceSB.AppendLine("\tpublic string atr_" + attribute + ";");
            }
            sourceSB.AppendLine("");
            sourceSB.AppendLine("\t// Children");
            foreach (string child in children)
            {
                sourceSB.AppendLine("\tpublic List<cls_" + child + "> list" + child + ";");
            }

            sourceSB.AppendLine("");
            sourceSB.AppendLine("\t// Constructor");
            sourceSB.AppendLine("\tpublic cls_" + Name + "()");
            sourceSB.AppendLine("\t{");
            foreach (string child in children)
            {
                sourceSB.AppendLine("\t\tlist" + child + " = new List<cls_" + child + ">()" + ";");
            }
            sourceSB.AppendLine("\t}");

            sourceSB.AppendLine("");
            sourceSB.AppendLine("\tpublic cls_" + Name + "(XmlNode xmlNode) : this ()");
            sourceSB.AppendLine("\t{");

            if (hasText)
            {
                sourceSB.AppendLine("\t\t\tInnerText = xmlNode.InnerText;");
                sourceSB.AppendLine("");
            }            

            foreach (string attribute in attributes)
            {
                sourceSB.AppendLine("\t\tif (xmlNode.Attributes[\"" + attribute + "\"] != null)");
                sourceSB.AppendLine("\t\t{");
                sourceSB.AppendLine("\t\t\tatr_" + attribute + " = xmlNode.Attributes[\"" + attribute + "\"].Value;");
                sourceSB.AppendLine("\t\t}");
            }

            sourceSB.AppendLine("");

            foreach (string child in children)
            {
                sourceSB.AppendLine("\t\tforeach (XmlNode childNode in xmlNode.SelectNodes(\"./" + child + "\"))");
                sourceSB.AppendLine("\t\t{");
                sourceSB.AppendLine("\t\t\tlist" + child + ".Add(new cls_" + child + "(childNode));");
                sourceSB.AppendLine("\t\t}");
            }

            sourceSB.AppendLine("\t}");

            sourceSB.Append("}");

            return sourceSB.ToString();
        }
    }

    public class XMLToClasses
    {
        public Hashtable extantElements;

        public XMLToClasses()
        {
            extantElements = new Hashtable();
        }

        public Element processElement(XmlNode xmlNode)
        {
            Element element;

            if (extantElements.Contains(xmlNode.Name))
            {
                element = (Element)extantElements[xmlNode.Name];
            }
            else
            {
                element = new Element();
                element.Name = xmlNode.Name;

                extantElements.Add(element.Name, element);
            }            

            if (xmlNode.Attributes != null)
            {
                foreach (XmlAttribute attribute in xmlNode.Attributes)
                {
                    if (!element.attributes.Contains(attribute.Name))
                    {
                        element.attributes.Add(attribute.Name);
                    }
                }
            }


            if (xmlNode.ChildNodes != null)
            {
                foreach (XmlNode node in xmlNode.ChildNodes)
                {
                    if (node.Name == "#text")
                    {
                        element.hasText = true;
                    }
                    else
                    {
                        Element childNode = processElement(node);

                        if (!element.children.Contains(childNode.Name))
                        {
                            element.children.Add(childNode.Name);
                        }
                    }
                }
            }

            return element;
        }
    }
}
名称空间XMLToClasses
{
公共类元素
{
公共字符串名称;
公共哈希集属性;
公共儿童;
公共图书馆;
公共元素()
{
Name=“”;
attributes=newhashset();
children=newhashset();
hasText=false;
}
公共字符串getSource()
{
StringBuilder sourceSB=新的StringBuilder();
sourceSB.AppendLine(“[Serializable()]”);
sourceSB.AppendLine(“公共类cls”+名称);
sourceSB.AppendLine(“{”);
sourceSB.AppendLine(“\t//属性”);
if(hasText)
{
sourceSB.AppendLine(“\t字符串InnerText;”);
}
foreach(属性中的字符串属性)
{
sourceSB.AppendLine(“\t公共字符串atr”+属性+”;”;
}
sourceSB.附录行(“”);
sourceSB.AppendLine(“\t//Children”);
foreach(子对象中的字符串子对象)
{
sourceSB.AppendLine(“\t公共列表“+child+”;”);
}
sourceSB.附录行(“”);
sourceSB.AppendLine(“\t//构造函数”);
sourceSB.AppendLine(“\tpublic cls”+Name+”()”;
sourceSB.AppendLine(“\t{”);
foreach(子对象中的字符串子对象)
{
sourceSB.AppendLine(“\t\t列表