将变量Length XML Child Elements读入Object

时间:2015-03-09 18:17:59

标签: c# xml parsexml

我需要读取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类获得两个对象,具有与“P”相同的“General_Header”参数,但是一个对象具有相等的“类别”到“Q”,另一个有“SS”,然后继续用相应的工具和联系人填充每一个。 该想法与XML文件的其余部分在不同级别具有相同问题的想法相同,例如:WP标签在“SubCategory”中具有分支,而在“SubSubCategory”中具有分支。

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

有什么建议吗?

提前致谢。

1 个答案:

答案 0 :(得分: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();

现在让我们完全了解所有类别,子类别,SubSubCategories:

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

好的,现在我们需要为每个类别创建一个WP类。但我们还必须填写Category,SubCategory和SubSubCategory属性。所以我们需要知道相应的类别是什么(cat,sub或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();

它(在其本身或所有祖先中)找到一个等于“Category”的名称,或者如果找不到则返回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")  

获取标识为“name”

p的所有属性
.Any(a=>new[]{"Category","SubCategory","SubSubCategory"}.Contains(a.Value))

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

简而言之,它将获得一般标题的所有后代,分别是Category,SubCategory或SubSubCategory