[英]SelectSingleNode returning null for known good xml node path using XPath
考虑这个简单的 XML 文档。 此处显示的序列化 XML 是来自复杂 POCO 对象的 XmlSerializer 的结果,我无法控制其架构。
<My_RootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="">
<id root="2.16.840.1.113883.3.51.1.1.1" extension="someIdentifier" xmlns="urn:hl7-org:v3" />
<creationTime xsi:nil="true" xmlns="urn:hl7-org:v3" />
</My_RootNode>
目标是在 id 节点上提取扩展属性的值。 在这种情况下,我们使用 SelectSingleNode 方法,并给出一个 XPath 表达式,如下所示:
XmlNode idNode = myXmlDoc.SelectSingleNode("/My_RootNode/id");
//idNode is evaluated to null at this point in the debugger!
string msgID = idNode.Attributes.GetNamedItem("extension").Value;
问题是SelectSingleNode
方法为给定的 XPath 表达式返回 null。
问题:关于这个 XPath 查询的正确性的任何想法,或者为什么这个方法调用 + XPath 表达式会返回一个空值? 也许命名空间是问题的一部分?
我强烈怀疑这个问题与命名空间有关。 尝试摆脱命名空间,你会没事的 - 但显然这在你的真实情况下无济于事,我假设文档是固定的。
我不记得如何在 XPath 表达式中指定命名空间,但我确定这就是问题所在。
编辑:好的,我现在记得怎么做。 不过,这并不是很令人愉快——您需要为它创建一个XmlNamespaceManager
。 以下是一些适用于您的示例文档的示例代码:
using System;
using System.Xml;
public class Test
{
static void Main()
{
XmlDocument doc = new XmlDocument();
XmlNamespaceManager namespaces = new XmlNamespaceManager(doc.NameTable);
namespaces.AddNamespace("ns", "urn:hl7-org:v3");
doc.Load("test.xml");
XmlNode idNode = doc.SelectSingleNode("/My_RootNode/ns:id", namespaces);
string msgID = idNode.Attributes["extension"].Value;
Console.WriteLine(msgID);
}
}
如果你想完全忽略命名空间,你可以使用这个:
static void Main(string[] args)
{
string xml =
"<My_RootNode xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"\">\n" +
" <id root=\"2.16.840.1.113883.3.51.1.1.1\" extension=\"someIdentifier\" xmlns=\"urn:hl7-org:v3\" />\n" +
" <creationTime xsi:nil=\"true\" xmlns=\"urn:hl7-org:v3\" />\n" +
"</My_RootNode>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNode idNode = doc.SelectSingleNode("/*[local-name()='My_RootNode']/*[local-name()='id']");
}
这应该适用于您的情况,而无需删除名称空间:
XmlNode idNode = myXmlDoc.GetElementsByTagName("id")[0];
抱歉,您忘记了命名空间。 你需要:
XmlNamespaceManager ns = new XmlNamespaceManager(myXmlDoc.NameTable);
ns.AddNamespace("hl7","urn:hl7-org:v3");
XmlNode idNode = myXmlDoc.SelectSingleNode("/My_RootNode/hl7:id", ns);
事实上,无论是在这里还是在 Web 服务中,从 XPath 操作或任何依赖于 XPath 的操作返回 null 通常都表明 XML 名称空间存在问题。
嗯...我有同样的问题,这是一个头痛。 由于我不太关心命名空间或 xml 模式,我只是从我的 xml 中删除了这些数据,它解决了我所有的问题。 可能不是最好的答案? 可能,但是如果您不想处理所有这些并且您只关心数据(并且不会将 xml 用于其他任务),则删除命名空间可能会解决您的问题。
XmlDocument vinDoc = new XmlDocument();
string vinInfo = "your xml string";
vinDoc.LoadXml(vinInfo);
vinDoc.InnerXml = vinDoc.InnerXml.Replace("xmlns=\"http://tempuri.org\/\", "");
只是为了解决命名空间问题,就我而言,我遇到了具有多个命名空间的文档,需要正确处理命名空间。 我编写了下面的函数来获得一个命名空间管理器来处理文档中的任何命名空间:
private XmlNamespaceManager GetNameSpaceManager(XmlDocument xDoc)
{
XmlNamespaceManager nsm = new XmlNamespaceManager(xDoc.NameTable);
XPathNavigator RootNode = xDoc.CreateNavigator();
RootNode.MoveToFollowing(XPathNodeType.Element);
IDictionary<string, string> NameSpaces = RootNode.GetNamespacesInScope(XmlNamespaceScope.All);
foreach (KeyValuePair<string, string> kvp in NameSpaces)
{
nsm.AddNamespace(kvp.Key, kvp.Value);
}
return nsm;
}
要记住的规则是:如果您的文档指定了一个namespace
,则您必须在对SelectNodes()
或SelectSingleNode()
调用中使用XmlNamespaceManager
。 这是好事。
请参阅文章名称空间的优势。 Jon Skeet 在他的回答中做得很好,展示了如何使用XmlNamespaceManager
。 (这个答案实际上应该只是对该答案的评论,但我没有足够的代表点数来评论。)
只需使用 //id 而不是 /id。 它在我的代码中运行良好
Roisgoen 的回答对我有用,但为了使它更通用,您可以使用 RegEx:
//Substitute "My_RootNode" for whatever your root node is
string strRegex = @"<My_RootNode(?<xmlns>\s+xmlns([\s]|[^>])*)>";
var myMatch = new Regex(strRegex, RegexOptions.None).Match(myXmlDoc.InnerXml);
if (myMatch.Success)
{
var grp = myMatch.Groups["xmlns"];
if (grp.Success)
{
myXmlDoc.InnerXml = myXmlDoc.InnerXml.Replace(grp.Value, "");
}
}
我完全承认这不是最佳实践答案,但它很容易解决,有时这就是我们所需要的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.