我有一个简单的XML
<AllBands>
<Band>
<Beatles ID="1234" started="1962">greatest Band<![CDATA[lalala]]></Beatles>
<Last>1</Last>
<Salary>2</Salary>
</Band>
<Band>
<Doors ID="222" started="1968">regular Band<![CDATA[lalala]]></Doors>
<Last>1</Last>
<Salary>2</Salary>
</Band>
</AllBands>
然而,
当我想要到达“门乐队”并更改其ID时:
using (var stream = new StringReader(result))
{
XDocument xmlFile = XDocument.Load(stream);
var query = from c in xmlFile.Elements("Band")
select c;
...
query
没有结果
但是
如果我写xmlFile.Elements().Elements("Band")
所以它确实找到了它。
有什么问题?
是否需要Root的完整路径?
如果是这样,为什么没有指定AllBands
?
XDocument
导航是否要求我知道所需元素的完整级别结构?
答案 0 :(得分:75)
Elements()
只检查直接子节点 - 在第一种情况下是根元素,在第二种情况下是根元素的子节点,因此在第二种情况下得到匹配。如果您只想要任何匹配的后代,请改为使用Descendants()
:
var query = from c in xmlFile.Descendants("Band") select c;
另外我建议你重新构建你的Xml:波段名称应该是一个属性或元素值,而不是元素名称本身 - 这使得查询(以及对此问题的模式验证)更加困难,例如:
<Band>
<BandProperties Name ="Doors" ID="222" started="1968" />
<Description>regular Band<![CDATA[lalala]]></Description>
<Last>1</Last>
<Salary>2</Salary>
</Band>
答案 1 :(得分:27)
你可以这样做:
xml.Descendants().SingleOrDefault(p => p.Name.LocalName == "Name of the node to find")
其中xml是XDocument。
请注意,属性Name返回一个具有LocalName和Namespace的对象。这就是为什么如果要按名称进行比较,必须使用Name.LocalName。
答案 2 :(得分:25)
您应该使用Root
来引用根元素:
xmlFile.Root.Elements("Band")
如果您想在文档中的任何位置找到元素,请改用Descendants
:
xmlFile.Descendants("Band")
答案 3 :(得分:6)
问题是Elements
只接受你所称的直接子元素。如果您想要所有后代,请使用Descendants
方法:
var query = from c in xmlFile.Descendants("Band")
答案 4 :(得分:4)
我在使用大型和大型机器人时的经验复杂的XML文件有时候,Elements和Descendants似乎都无法检索特定的元素(我仍然不知道为什么)。
在这种情况下,我发现更安全的选择是手动搜索元素,如以下MSDN帖子所述:
简而言之,您可以创建一个GetElement函数:
static void Main(string[] args)
{
Request.ServicesExternalClient se = new Request.ServicesExternalClient();
se.ClientCredentials.UserName.UserName = "UserName";
se.ClientCredentials.UserName.Password = "Password1";
BindingElementCollection elements = se.Endpoint.Binding.CreateBindingElements();
elements.Add(SecurityBindingElement.CreateUserNameForSslBindingElement());
System.Uri uri = new System.Uri("--url--");
se.Endpoint.ListenUri = uri;
StringBuilder xml = new StringBuilder();
xml.Append(@"<?xml version=""1.0"" encoding=""UTF-8""?>");
xml.Append(@"<NewInvoiceRequest >");
xml.Append(@"<tracingLevel>OFF</tracingLevel>");
'
'
'
'
'
'
'
xml.Append(@"</NewInvoiceRequest>");
Request.NewInvoiceRequest newInvRequest = new Request.NewInvoiceRequest();
Request.Invoice inv = new Request.Invoice();
using (MemoryStream stream = new MemoryStream())
{
XmlSerializer s = new XmlSerializer(typeof(Request.NewInvoiceRequest));
StreamWriter sw = new StreamWriter(stream);
using (StringReader sr = new StringReader(xml.ToString()))
{
newInvRequest = (Request.NewInvoiceRequest)s.Deserialize(sr);
}
}
Request.NewInvoiceResponse res = se.createNewInvoice(newInvRequest);
}
然后你可以这样打电话:
private XElement GetElement(XDocument doc,string elementName)
{
foreach (XNode node in doc.DescendantNodes())
{
if (node is XElement)
{
XElement element = (XElement)node;
if (element.Name.LocalName.Equals(elementName))
return element;
}
}
return null;
}
请注意,如果找不到匹配的元素,这将返回 null 。
答案 5 :(得分:2)
Elements()
方法返回包含当前节点的所有子元素的IEnumerable<XElement>
。对于XDocument,该集合仅包含Root元素。因此,需要以下内容:
var query = from c in xmlFile.Root.Elements("Band")
select c;
答案 6 :(得分:0)
Sebastian的答案是检查xaml文档时唯一对我有用的答案。如果像我一样,您想要一个所有元素的列表,则该方法看起来就像上面塞巴斯蒂安的答案,只是返回一个列表...
private static List<XElement> GetElements(XDocument doc, string elementName)
{
List<XElement> elements = new List<XElement>();
foreach (XNode node in doc.DescendantNodes())
{
if (node is XElement)
{
XElement element = (XElement)node;
if (element.Name.LocalName.Equals(elementName))
elements.Add(element);
}
}
return elements;
}
这样称呼它:
var elements = GetElements(xamlFile, "Band");
或者在我想要所有TextBlocks的xaml文档的情况下,这样调用它:
var elements = GetElements(xamlFile, "TextBlock");