【问题标题】:SelectSingleNode returning null for known good xml node path using XPathSelectSingleNode 为使用 XPath 的已知良好 xml 节点路径返回 null
【发布时间】:2010-11-08 12:28:27
【问题描述】:

考虑这个简单的 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 表达式会返回一个空值?也许命名空间是问题的一部分?

【问题讨论】:

  • 首先要检查的是 XML 文档是否已正确加载。我可以在根节点的末尾看到一个空的 xmlns 属性 - 对吗?
  • @Oded:正确,我们正在查看已加载 XmlSerializer 的字符串输出的 XmlDocument。
  • @pcampbell:这是一个大文件(HL7!)吗?如果是这样,那么您可能想尝试直接序列化到 XmlDocument。如果你想要一个样本,请告诉我。

标签: c# xml xpath


【解决方案1】:

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, "");
    }
}

我完全承认这不是最佳实践答案,但它很容易解决,有时这就是我们所需要的。

【讨论】:

    【解决方案2】:

    要记住的规则是:如果您的文档指定了namespace,您必须在调用SelectNodes()SelectSingleNode() 时使用XmlNamespaceManager。这是好事。

    见文章Advantages of namespaces。 Jon Skeet 在他的回答中做得很好,展示了如何使用 XmlNamespaceManager。 (这个答案实际上应该只是对该答案的评论,但我没有足够的 Rep Points 来评论。)

    【讨论】:

      【解决方案3】:

      只需使用//id 而不是/id。它在我的代码中运行良好

      【讨论】:

        【解决方案4】:

        只是为了解决命名空间问题,就我而言,我遇到了具有多个命名空间的文档,并且需要正确处理命名空间。我写了下面的函数来获得一个命名空间管理器来处理文档中的任何命名空间:

        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;
            }
        

        【讨论】:

          【解决方案5】:

          这应该适用于您的情况,而无需删除命名空间:

          XmlNode idNode = myXmlDoc.GetElementsByTagName("id")[0];
          

          【讨论】:

          • GetElementsByTagName 返回一个 XmlNodeList,所以如果您想要超过 1 个匹配项,只需离开 [0] 即可
          【解决方案6】:

          如果你想完全忽略命名空间,你可以使用这个:

          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']");
          }
          

          【讨论】:

          • 老而金,完美解决了我的问题。
          【解决方案7】:

          嗯...我遇到了同样的问题,很头疼。由于我不太关心命名空间或 xml 模式,我只是从我的 xml 中删除了这些数据,它解决了我所有的问题。可能不是最好的答案?可能,但是如果您不想处理所有这些并且您只关心数据(并且不会将 xml 用于其他任务)删除命名空间可能会解决您的问题。

          XmlDocument vinDoc = new XmlDocument();
          string vinInfo = "your xml string";
          vinDoc.LoadXml(vinInfo);
          
          vinDoc.InnerXml = vinDoc.InnerXml.Replace("xmlns=\"http://tempuri.org\/\", "");
          

          【讨论】:

          • 这仅适用于您的特定数据。这不是一个普遍的答案。
          • 如果您可以控制 xsd、xml 和使用它的代码,这是处理问题的一种方法的绝佳示例。我已经接受了这个答案,并通过使用正则表达式对其进行了概括,并将其上传到了这个线程。
          【解决方案8】:

          抱歉,您忘记了命名空间。你需要:

          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 命名空间存在问题。

          【讨论】:

          • 感谢 John,实际上测试数据中的命名空间缺失/空白!你怀疑这是问题的一部分吗?
          • 我相信 John 几乎是完全正确的,因为“id”元素的全名是“urn:h17-org:v3”和“id”这对。您正在使用 XPATH 搜索“”和“id”,所以它不会找到任何东西。但是,要实际工作,您需要将 ns 实例作为 SelectSingleNode 的第二个参数传递。
          • Doh - 花了这么多时间想出一个测试程序,却发现你比我强 :)
          • @Jon:我应该把它框起来。 (好吧,不是真的)。此外,史蒂文发现我离开了“,ns”
          • @Steven:很好,也是最礼貌的说法,“嘿,笨蛋,你忘了使用刚刚构建的对象”,这是我一段时间以来听到的。 “几乎完全正确”——我必须记住这一点。
          【解决方案9】:

          我强烈怀疑问题与命名空间有关。尝试摆脱命名空间,你会没事的 - 但显然这对你的实际情况没有帮助,我假设文档是固定的。

          我不记得如何在 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);
              }
          }
          

          【讨论】:

          • 尝试 //id 看看是否确实是命名空间问题。
          • 您可以在创建 xmldoc 时添加命名空间。
          • 如果根是 XmlNode,而不是 XmlDocument,如何更改代码?
          • 注意命名空间名称不必与xml匹配。
          猜你喜欢
          • 2020-10-05
          • 2017-10-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-02-23
          • 1970-01-01
          • 2023-03-07
          相关资源
          最近更新 更多