首页 文章

SelectSingleNode使用XPath为已知良好的xml节点路径返回null

提问于
浏览
35

考虑这个简单的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 .

Question: 关于这个XPath查询的正确性的任何想法,或者为什么这个方法调用XPath表达式会返回一个空值?也许命名空间是问题的一部分?

9 回答

  • -1

    我强烈怀疑问题与名称空间有关 . 尝试摆脱名称空间,你会没事的 - 但显然这对你的实际情况没有帮助,我认为这个文件是固定的 .

    我不记得如何在XPath表达式中指定命名空间,但我确信这是问题所在 .

    编辑:好的,我've remembered how to do it now. It'虽然不是很令人愉快 - 你需要为它创建一个 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);
        }
    }
    
  • 2

    如果要完全忽略名称空间,可以使用:

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

    这应该适用于你的情况而不删除命名空间:

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

    对不起,您忘记了命名空间 . 你需要:

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

  • 8

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

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

    只是为了解决命名空间问题,在我的情况下,我一直在运行具有多个命名空间的文档,并且需要正确处理命名空间 . 我编写了下面的函数来获取命名空间管理器来处理文档中的任何命名空间:

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

    只需使用// id而不是/ id . 它在我的代码中工作正常

  • 0

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

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

  • 7

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

    我完全承认这不是一个最佳实践答案,但它是一个简单的解决方案,有时这就是我们所需要的 .

相关问题