0

我需要读取 XML 文件中的所有元素,该文件的格式更像树状层次结构,然后用它填充一个类,这是一个示例:

<?xml version="1.0"?>
<WBs>
  <WP GeneralID ="1">
    <P name="General_Header">
      <Q  name= "Category">
        <Tools>
          <Tool id ="1">
            <TName> QT 1</TName>
            <Rev>1</Rev>
          </Tool>
          <Tool id ="2">
            <TName> QT 2</TName>
            <Rev>3</Rev>
          </Tool>
        </Tools>
        <Contacts>
          <Contact>
            <CName>MM</CName>
            <CMail>m.m@i.com</CMai>
          </Contact>
          <Contact>
            <CName>AM</CName>
            <CMail>a.m@i.com</CMail>
          </Contact>
        </Contacts>
      </Q>
      <ss  name= "Category">
        <Tools>
          <Tool id ="1">
            <TName> SST 1</TName>
            <Rev>3</Rev>
          </Tool>
          <Tool id ="2">
            <TName> SST 2</TName>
            <Rev>3</Rev>
          </Tool>
        </Tools>
        <Contacts>
          <Contact>
            <CName>KE</CName>
            <CMail>K.E@i.com</CMai>
          </Contact>
          <Contact>
            <CName>AM</CName>
            <CMail>a.m@i.com</CMail>
          </Contact>
        </Contacts>
      </ss>
</P>
</WP>
</WBs>

我为每个 WP、工具、联系人做了一个类。如下:

class WP
{
    public string GeneralHeader { get; set; } //level 1 i.e p, 
    public string Category { get; set; }// level 2
    public string SubCategory { get; set; }//level 3
    public string SubSubCategory { get; set; } // level 4
    public List<Tool> WPTools { get; set; }
    public List<Contact> WPContacts { get; set; }
}

我想要做的是遍历所有元素和子元素,然后填充 WP 类,这样每当我遇到两个子元素时,它应该在 WP 的两个不同对象中,但具有相同的父属性。

例如:对于上面的示例,我希望从 WP 类中获取两个对象,其“General_Header”参数与“P”相同,但一个对象的“类别”等于“Q”,另一个对象具有“ SS”,然后继续用相应的工具和联系人填充每个人。这个想法与 XML 文件的其余部分在不同级别具有相同的问题相同,例如:WP Tags 在“SubCategory”中有分支,而在“SubSubCategory”中有其他分支。

我能想到的就是更改 xml 文件,以便每个完整的分支(直到工具和联系人标签)都包含在一组单独的<WP>---</WP>标签中,但在这种情况下,我会重复常见的父标签,我不这样做' t 认为是使用 xml 的一种有效方式。

有什么建议么?

提前致谢。

4

1 回答 1

0

这确实是一段特殊的 XML。通常我们会看到这样的结构:

<Category name= "Q">

而不是这个:

<Q name = "Category">

我会说这完全取决于您之后将如何处理该 XML,并且由于我对此一无所知,因此我会怀疑并假设这是正确的结构。但如果不是,请更改它

正如您所说,您不想一遍<P name="General_Header">又一遍地重复每个类别和子类别。

首先,让我们解析您的 xml 文本(或者如果您从文件加载,则加载):

XDocument document = XDocument.Parse(content);

现在让我们使用一些 Linq 来获取标题:

var generalHeader = document.Descendants()
                            .Where(p=>p.Attributes("name")
                                       .Any(a=>a.Value=="General_Header"))
                            .FirstOrDefault();

现在让我们一起获取所有类别、子类别、子类别:

var allCategories = generalHeader.Descendants()
                                 .Where(p=>p.Attributes("name")
                                            .Any(a=>new[]{"Category","SubCategory","SubSubCategory"}
                                                         .Contains(a.Value)));

好的,现在我们需要为WP我们拥有的每个类别创建一个类。但我们还必须填写 Category、SubCategory 和 SubSubCategory 属性。所以我们需要知道对应的类别是什么(cat, sub or subsub)。为此,我创建了以下方法:

public static IEnumerable<WP> CreateWP(XElement header, IEnumerable<XElement> categories)
{       
    foreach(XElement category in categories)
    {
        WP wp = new WP();
        wp.GeneralHeader = header.Name.LocalName;
        wp.Category = category.Ancestors().Concat(new []{category}).Where(c=> c.Attributes("name").Any(a=>a.Value == "Category")).Select(elem=>elem.Name.LocalName).FirstOrDefault();
        wp.SubCategory = category.Ancestors().Concat(new []{category}).Where(c=> c.Attributes("name").Any(a=>a.Value == "SubCategory")).Select(elem=>elem.Name.LocalName).FirstOrDefault();
        wp.SubSubCategory = category.Ancestors().Concat(new []{category}).Where(c=> c.Attributes("name").Any(a=>a.Value == "SubSubCategory")).Select(elem=>elem.Name.LocalName).FirstOrDefault();

        XmlSerializer xt = new XmlSerializer(typeof(Tool));
        wp.WPTools = category.Descendants("Tool").Select(t=> (Tool) xt.Deserialize(t.CreateReader())).ToList();

        XmlSerializer xc = new XmlSerializer(typeof(Contact));
        wp.WPContacts = category.Descendants("Contact").Select(t=> (Contact) xc.Deserialize(t.CreateReader())).ToList();
        yield return wp;
    }
}

重要的一行是这样的:

category.Ancestors().Concat(new []{category})
                    .Where(c=> c.Attributes("name").Any(a=>a.Value == "Category"))
                    .Select(elem=>elem.Name.LocalName).FirstOrDefault();

它(在其自身或其所有祖先中)查找等于“类别”的名称,null如果没有找到则返回。

基本上,我们在这里所做的是展平所有元素(各种类别)并为找到的每个类别创建一个对象。我用这个文件来测试这个:

http://pastebin.com/raw.php?i=cAUnhUgf

这是完整的小提琴,所以你可以自己看看:

https://dotnetfiddle.net/ZBvmhe

编辑解释此声明:

var allCategories = generalHeader.Descendants() 

这将获取所有后代元素(包括类别、子类别、工具、联系人、所有内容)

.Where(p=>p.Attributes("name")       
                            .Any(a=>new[]{"Category","SubCategory","SubSubCategory"}.Contains(a.Value)));

这翻译为

其中 XName 为的任何后代p属性的name值设置为CategorySubCategorySubSubCategory

意思是:

p.Attributes("name")  

获取p标识为“名称”的所有属性

.Any(a=>new[]{"Category","SubCategory","SubSubCategory"}.Contains(a.Value))

无论 name 属性中的任何值是否等于 3 种可能性之一(Category、SubCategory 或 SubSubCategory),这都会返回 true,否则返回 false。

简而言之,它将获取通用标头的所有后代,即 Category、SubCategory 或 SubSubCategory

于 2015-03-09T19:18:29.487 回答