使用 Linq to XML 处理父子关系的子项
我是新尝试学习 LINQ to XML 的人,但在“孩子”方面遇到了麻烦。我有一个有关文档信息的 XML 文件;每个文档都有一定数量的 INDEX 元素,如以下代码段所示:
<DOCUMENTCOLLECTION>
<DOCUMENT>
<FILE filename="Z:\Consulting\ConverterRun4\B0000001\Submission\D003688171.0001.tif" outputpath="Z:\Consulting\ConverterRun4\B0000001\Submission"/>
<ANNOTATION filename=""/>
<INDEX name="CAN(idmDocCustom4)" value=""/>
<INDEX name="Comment(idmComment)" value="GENERAL CORRESPONDENCE 11-6-96 TO 10-29-"/>
<INDEX name="DiagnosticID(idmDocCustom5)" value="983958-0006.MDB-2155504"/>
<INDEX name="Document Class(idmDocType)" value="Submission"/>
<INDEX name="Original File Name(idmDocOriginalFile)" value="40410.TIF"/>
<INDEX name="Title(idmName)" value="1997-12"/>
<FOLDER name="/Accreditation/NCACIHE/1997-12"/>
</DOCUMENT>
<DOCUMENT>
我只需要 INDEX 元素中的一些值 - 那些具有以下名称属性的值:
Comment(idmComment)
Document Class(idmDocType)
Title(idmName)
这是我迄今为止在测试中得到的:
namespace ConsoleApplication1
{
class DocMetaData
{
public string Comment { get; set; }
public string DocClass { get; set; }
public string Title { get; set; }
public string Folder { get; set; }
public string File { get; set; }
}
class Program
{
static void Main(string[] args)
{
XDocument xmlDoc = XDocument.Load(@"convert.B0000001.Submission.xml");
List<DocMetaData> docList =
(from d in xmlDoc.Descendants("DOCUMENT")
select new DocMetaData
{
Folder = d.Element("FOLDER").Attribute("name").Value,
File = d.Element("FILE").Attribute("filename").Value,
// need Comment, DocClass, Title from d.Element("INDEX").Attribute("name")
}
).ToList<DocMetaData>();
foreach (var c in docList)
{
Console.WriteLine("File name = {0}", c.File);
Console.WriteLine("\t" + "Folder = {0}", c.Folder);
}
Console.ReadLine();
}
}
}
我不认为我想要我的 DocMetaData 类中的 List
。我想摆脱 DOCUMENT 中 INDEX 元素的一对多方面,并分配属性,如 DocMetaData 类中所示。我实在不知道该如何对待这些孩子!
--------编辑更新----2011 年 5 月 27 日 ----------------------
进行了以下更改,导致编译错误;已经研究了该错误并尝试了一些 using 指令的重新排列,但到目前为止无法解决这个问题:
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Linq;
namespace ConsoleApplication1
{
class DocMetaData
{
public string Comment { get; set; }
public string DocClass { get; set; }
public string Title { get; set; }
public string Folder { get; set; }
public string File { get; set; }
}
class Program
{
static void Main(string[] args)
{
XDocument xmlDoc = XDocument.Load(@"convert.B0000001.Submission.xml");
List<DocMetaData> docList =
(from d in xmlDoc.Descendants("DOCUMENT")
select new DocMetaData
{
Folder = d.Element("FOLDER").Attribute("name").Value,
File = d.Element("FILE").Attribute("filename").Value,
Comment = d.Element("INDEX")
.Where(i => i.Attribute("name") == "Comment(idmComment)")
.First()
.Attribute("value").Value
}
).ToList<DocMetaData>();
foreach (var c in docList)
{
Console.WriteLine("File name = {0}", c.File);
Console.WriteLine("\t" + "Folder = {0}", c.Folder);
Console.WriteLine("\t\t" + "Comment = {0}", c.Comment);
}
Console.ReadLine();
}
这是错误(注意:我有 System.Xml.Linq 作为参考,并且还有一个 using 指令):
Error 1 'System.Xml.Linq.XElement' does not contain a definition for 'Where' and no extension method 'Where' accepting a first argument of type 'System.Xml.Linq.XElement' could be found (are you missing a using directive or an assembly reference?) C:\ProjectsVS2010\ConsoleApplication_LINQ\ConsoleApplication1\Program.cs 31 37 ConsoleApplication1
I am new trying to learn LINQ to XML and having trouble with "children". I have an XML file of info about documents; each document has some number of INDEX elements as in this snippet:
<DOCUMENTCOLLECTION>
<DOCUMENT>
<FILE filename="Z:\Consulting\ConverterRun4\B0000001\Submission\D003688171.0001.tif" outputpath="Z:\Consulting\ConverterRun4\B0000001\Submission"/>
<ANNOTATION filename=""/>
<INDEX name="CAN(idmDocCustom4)" value=""/>
<INDEX name="Comment(idmComment)" value="GENERAL CORRESPONDENCE 11-6-96 TO 10-29-"/>
<INDEX name="DiagnosticID(idmDocCustom5)" value="983958-0006.MDB-2155504"/>
<INDEX name="Document Class(idmDocType)" value="Submission"/>
<INDEX name="Original File Name(idmDocOriginalFile)" value="40410.TIF"/>
<INDEX name="Title(idmName)" value="1997-12"/>
<FOLDER name="/Accreditation/NCACIHE/1997-12"/>
</DOCUMENT>
<DOCUMENT>
I only need a few values from the INDEX elements - those with name attributes of:
Comment(idmComment)
Document Class(idmDocType)
Title(idmName)
This is what I have so far in my testing:
namespace ConsoleApplication1
{
class DocMetaData
{
public string Comment { get; set; }
public string DocClass { get; set; }
public string Title { get; set; }
public string Folder { get; set; }
public string File { get; set; }
}
class Program
{
static void Main(string[] args)
{
XDocument xmlDoc = XDocument.Load(@"convert.B0000001.Submission.xml");
List<DocMetaData> docList =
(from d in xmlDoc.Descendants("DOCUMENT")
select new DocMetaData
{
Folder = d.Element("FOLDER").Attribute("name").Value,
File = d.Element("FILE").Attribute("filename").Value,
// need Comment, DocClass, Title from d.Element("INDEX").Attribute("name")
}
).ToList<DocMetaData>();
foreach (var c in docList)
{
Console.WriteLine("File name = {0}", c.File);
Console.WriteLine("\t" + "Folder = {0}", c.Folder);
}
Console.ReadLine();
}
}
}
I don't think I want a List<Index>
inside my DocMetaData class. I want to get rid of the one-to-many aspect of the INDEX elements within DOCUMENT and assign properties as shown in the DocMetaData class. I can't get my head around how to handle these children!
--------EDIT-UPDATE----27 May 2011 ----------------------
Made the following change which caused compile error; have researched the error and tried some rearrangement of using directives but so far unable to get past this:
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Linq;
namespace ConsoleApplication1
{
class DocMetaData
{
public string Comment { get; set; }
public string DocClass { get; set; }
public string Title { get; set; }
public string Folder { get; set; }
public string File { get; set; }
}
class Program
{
static void Main(string[] args)
{
XDocument xmlDoc = XDocument.Load(@"convert.B0000001.Submission.xml");
List<DocMetaData> docList =
(from d in xmlDoc.Descendants("DOCUMENT")
select new DocMetaData
{
Folder = d.Element("FOLDER").Attribute("name").Value,
File = d.Element("FILE").Attribute("filename").Value,
Comment = d.Element("INDEX")
.Where(i => i.Attribute("name") == "Comment(idmComment)")
.First()
.Attribute("value").Value
}
).ToList<DocMetaData>();
foreach (var c in docList)
{
Console.WriteLine("File name = {0}", c.File);
Console.WriteLine("\t" + "Folder = {0}", c.Folder);
Console.WriteLine("\t\t" + "Comment = {0}", c.Comment);
}
Console.ReadLine();
}
Here is the error (NOTE: I have System.Xml.Linq as a Reference and a using directive for it also):
Error 1 'System.Xml.Linq.XElement' does not contain a definition for 'Where' and no extension method 'Where' accepting a first argument of type 'System.Xml.Linq.XElement' could be found (are you missing a using directive or an assembly reference?) C:\ProjectsVS2010\ConsoleApplication_LINQ\ConsoleApplication1\Program.cs 31 37 ConsoleApplication1
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可能想要获取 INDEX 元素,然后使用
Where
和First
获取所需的元素。请注意,如果不存在具有正确属性的 INDEX 元素,这将引发异常。如果您想忽略没有相应索引的属性,我会将选择代码拉入其自己的方法中,使用 FirstOrDefault ,并在分配之前执行适当的 null 检查。
You probably want to get the INDEX elements and then use
Where
andFirst
to get the one you want.Note that this will throw an exception if there is not an INDEX element with the right attribute. If you want to ignore properties for which there is not a corresponding index, I would pull the select code into its own method, use
FirstOrDefault
, and do the appropriate null checks before assigning.秘密就在于 SelectMany。这是一篇博客文章,可以帮助您解决这个问题。
http:// craigwatson1962.wordpress.com/2010/11/04/linq-to-xml-using-let-yield-return-and-selectmany/
The secret lies in SelectMany. Here is a blog post that will help you wrap your head around the problem.
http://craigwatson1962.wordpress.com/2010/11/04/linq-to-xml-using-let-yield-return-and-selectmany/