我需要读取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的有效方法。
有什么建议吗?
提前致谢。
答案 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
属性都有 值设置为Category
,SubCategory
或SubSubCategory
这意味着:
p.Attributes("name")
获取标识为“name”
的p
的所有属性
.Any(a=>new[]{"Category","SubCategory","SubSubCategory"}.Contains(a.Value))
返回true,无论name属性中的任何值是否等于3种可能性(Category,SubCategory或SubSubCategory)之一,否则返回false。
简而言之,它将获得一般标题的所有后代,分别是Category,SubCategory或SubSubCategory 。