C# 使用XPath为已知良好的xml节点路径选择SingleNode返回null

C# 使用XPath为已知良好的xml节点路径选择SingleNode返回null,c#,xml,xpath,C#,Xml,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=

考虑一下这个简单的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>
问题是
SelectSingleNode
方法为给定的XPath表达式返回null


问题:对这个XPath查询的正确性有什么看法,或者为什么这个方法调用+XPath表达式会返回空值?也许名称空间是问题的一部分?

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

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

我强烈怀疑问题与名称空间有关。试着去掉名称空间,你会没事的——但很明显,这对你的实际情况没有帮助,我假设文档是固定的

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

嗯。。。我有同样的问题,这是一个头痛。因为我不太关心名称空间或xml模式,所以我只是从xml中删除了这些数据,它解决了我所有的问题。也许不是最好的答案?可能吧,但是如果您不想处理所有这些问题,并且只关心数据(并且不会将xml用于其他任务),那么删除名称空间可能会解决您的问题

XmlDocument vinDoc = new XmlDocument();
string vinInfo = "your xml string";
vinDoc.LoadXml(vinInfo);

vinDoc.InnerXml = vinDoc.InnerXml.Replace("xmlns=\"http://tempuri.org\/\", "");

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

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']");
}
static void Main(字符串[]args)
{
字符串xml=
“\n”+
“\n”+
“\n”+
"";
XmlDocument doc=新的XmlDocument();
doc.LoadXml(xml);
XmlNode idNode=doc.SelectSingleNode(“/*[local-name()='My_RootNode']/*[local-name()='id']”);
}

在不删除名称空间的情况下,这应该适用于您的情况:

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

为了解决名称空间问题,在我的例子中,我遇到了具有多个名称空间的文档,需要正确处理名称空间。我编写了下面的函数,以便让名称空间管理器处理文档中的任何名称空间:

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);
    }
}
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;
    }
私有XmlNamespaceManager GetNameSpaceManager(XmlDocument xDoc) { XmlNamespaceManager nsm=新的XmlNamespaceManager(xDoc.NameTable); XPathNavigator RootNode=xDoc.CreateNavigator(); RootNode.MoveToFollowing(XPathNodeType.Element); IDictionary NameSpaces=RootNode.GetNamespacesInScope(XmlNamespaceScope.All); foreach(名称空间中的KeyValuePair kvp) { nsm.AddNamespace(kvp.Key,kvp.Value); } 返回nsm; }
只需使用//id而不是/id。它在我的代码中运行良好

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


见文章。Jon Skeet的回答很好地展示了如何使用
XmlNamespaceManager
。(这个答案实际上应该只是对那个答案的一个评论,但我没有足够的代表点来评论。)

Roisgoen的答案对我来说很有用,但为了让它更一般,你可以使用正则表达式:

//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, "");
    }
}
//用“My_RootNode”替换您的根节点
字符串stregex=@“]*)>”;
var myMatch=newregex(strRegex,RegexOptions.None).Match(myXmlDoc.InnerXml);
如果(myMatch.Success)
{
var grp=myMatch.Groups[“xmlns”];
如果(grp.Success)
{
myXmlDoc.InnerXml=myXmlDoc.InnerXml.Replace(grp.Value,“”);
}
}

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

谢谢John,实际上测试数据中缺少/空白名称空间!你怀疑这是问题的一部分吗?我相信John几乎完全正确,因为“id”元素的全名是“urn:h17 org:v3”和“id”对。您正在使用XPATH搜索“”和“id”,因此它找不到任何内容。但是,要真正工作,您需要将ns实例作为SelectSingleNode.Doh的第二个参数传递-花了所有时间编写一个测试程序,结果发现您在这方面击败了我:)@Jon:我应该考虑一下。(好的,不是真的)。此外,史蒂文发现我漏掉了“ns”@史蒂文:接得好,这是我最近听到的最礼貌的说法,“嘿,笨蛋,你忘了使用你刚刚构建的对象”。“几乎完全正确”-我必须记住这一点。首先要检查XML文档是否已正确加载。我可以在根节点的末尾看到一个空的xmlns属性-是吗?@Oded:正确,我们正在查看一个已加载XmlSerializer字符串输出的XmlDocument。@pcampbell:这是一个大文档(HL7!)?如果是这样,那么您可能希望尝试直接序列化到XmlDocument中。如果您想要一个示例,请让我知道。请尝试//id查看这是否确实是一个命名空间问题。您可以在创建xmldoc时添加命名空间。如果root是XmlNode而不是XmlDocument,如何更改代码?请注意,命名空间名称不必与xml名称匹配。这仅适用于您的特定数据。这不是一个一般性的答案,如果您能够控制xsd、xml和使用它的代码,那么这就是处理问题的一个很好的例子。我采用了这个答案,并通过使用正则表达式对其进行了一点概括,并将其上载到此线程。GetElementsByTagName返回一个XmlNodeList,所以只要离开[0]就可以了。若您想要多个匹配的itemOld但为gold,请完美地修复我的问题。