XmlNodeList 为空
XmlNodeList empty
我知道了 xml。
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
<soapenv:Header/>
<soapenv:Body>
<GetSelectedModels xmlns="http://www.something/">
<!--Optional:-->
<input xmlns="http://www.something/">
<X_Models>
<ListOfModels>
<Model>
<ModelId>163</ModelId>
</Model>
</ListOfModels>
</X_Models>
</input>
</GetSelectedModels>
</soapenv:Body>
</soapenv:Envelope>
当我在 SoapUi 中 post 它到达我的 Web 服务时,我想使用这些节点。
XmlDocument docRequest = new XmlDocument();
docRequest.LoadXml(xml);
XmlNodeList models= docRequest.SelectNodes("X_Models/ListOfModels/Model");
模型为 0,xml 如下所示:
<X_Modelsxmlns="http://www.something/">
<ListOfModels>
<Model>
<ModelID>163</ModelID>
</Model>
</ListOfModelss>
</X_Models>
如果我 post 我的 xml 像这样使用别名:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:net="http://www.something/" >
<soapenv:Header/>
<soapenv:Body>
<net:GetSelectedModels>
<!--Optional:-->
<net:input>
<X_Models>
<ListOfModels>
<Model>
<ModelId>163</ModelId>
</Model>
</ListOfModels>
</X_Models>
</net:input>
</net:GetSelectedModels>
</soapenv:Body>
</soapenv:Envelope>
然后模型将为 1,xml 如下所示:
<X_Models>
<ListOfModels>
<Model>
<ModelID>163</ModelID>
</Model>
</ListOfModelss>
</X_Models>
任何人都可以指出我正确的方向吗?谢谢!
在第一种情况下,X_Models
节点和所有后代都在默认命名空间中,即 xmlns="http://www.something/"
。在第二种情况下,没有默认命名空间,因此 X_Models
树不在任何命名空间中。
XmlNode.SelectNodes Method (String)
的文档指定必须按如下方式处理默认名称空间:
If the XPath expression does not include a prefix, it is assumed that the namespace URI is the empty namespace. If your XML includes a default namespace, you must still use the XmlNamespaceManager and add a prefix and namespace URI to it; otherwise, you will not get any nodes selected. For more information, see Select Nodes Using XPath Navigation.
因此,考虑到您在问题开头显示的 XML(与您的代码不完全匹配),您需要执行以下操作:
string xml = @"<?xml version=""1.0"" standalone=""no"" ?>
<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" >
<soapenv:Header/>
<soapenv:Body>
<GetSelectedModels xmlns=""http://www.something/"">
<!--Optional:-->
<input xmlns=""http://www.something/"">
<X_Models>
<ListOfModels>
<Model>
<ModelId>163</ModelId>
</Model>
</ListOfModels>
</X_Models>
</input>
</GetSelectedModels>
</soapenv:Body>
</soapenv:Envelope>
";
XmlDocument docRequest = new XmlDocument();
docRequest.LoadXml(xml);
XmlNamespaceManager ns = new XmlNamespaceManager(docRequest.NameTable);
ns.AddNamespace("models", "http://www.something/");
ns.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
XmlNodeList models = docRequest.SelectNodes("/soapenv:Envelope/soapenv:Body/models:GetSelectedModels/models:input/models:X_Models/models:ListOfModels/models:Model", ns);
Debug.Assert(models.Count == 1);
更新
如果您真的想 运行 查询忽略名称空间并仅使用本地名称的 XML 层次结构,那么 SelectNodes
不会开箱即用。您将需要一些扩展方法,例如:
public static class XmlNodeExtensions
{
public static IEnumerable<XmlElement> ChildElements(this IEnumerable<XmlElement> elements)
{
return elements.SelectMany(e => e.ChildNodes.OfType<XmlElement>());
}
public static IEnumerable<XmlElement> OfLocalName(this IEnumerable<XmlElement> elements, string localName)
{
return elements.Where(e => e.LocalName == localName);
}
}
然后你可以这样做:
string xml1 = @"<?xml version=""1.0"" standalone=""no"" ?>
<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" >
<soapenv:Header/>
<soapenv:Body>
<GetSelectedModels xmlns=""http://www.something/"">
<!--Optional:-->
<input xmlns=""http://www.something/"">
<X_Models>
<ListOfModels>
<Model>
<ModelId>163</ModelId>
</Model>
</ListOfModels>
</X_Models>
</input>
</GetSelectedModels>
</soapenv:Body>
</soapenv:Envelope>
";
string xml2 = @"
<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:net=""http://www.something/"" >
<soapenv:Header/>
<soapenv:Body>
<net:GetSelectedModels>
<!--Optional:-->
<net:input>
<X_Models>
<ListOfModels>
<Model>
<ModelId>163</ModelId>
</Model>
</ListOfModels>
</X_Models>
</net:input>
</net:GetSelectedModels>
</soapenv:Body>
</soapenv:Envelope>
";
XmlDocument doc1 = new XmlDocument();
doc1.LoadXml(xml1);
XmlDocument doc2 = new XmlDocument();
doc2.LoadXml(xml2);
var ignoreNameSpaceModels1 = doc1.DocumentElement.ChildNodes.OfType<XmlElement>().OfLocalName("Body").ChildElements().OfLocalName("GetSelectedModels").ChildElements().OfLocalName("input").ChildElements().OfLocalName("X_Models").ToArray();
var ignoreNameSpaceModels2 = doc2.DocumentElement.ChildNodes.OfType<XmlElement>().OfLocalName("Body").ChildElements().OfLocalName("GetSelectedModels").ChildElements().OfLocalName("input").ChildElements().OfLocalName("X_Models").ToArray();
Debug.Assert(ignoreNameSpaceModels1.Length == 1 && ignoreNameSpaceModels2.Length == 1 && ignoreNameSpaceModels1[0].LocalName == "X_Models" && ignoreNameSpaceModels2[0].LocalName == "X_Models");
但这就像说typeof(System.Windows.Forms.RichTextBox) == typeof(System.Windows.Controls.RichTextBox)
因为他们的Name
是一样的。那是因为,下面的xmlns="http://www.something/"
属性:
<GetSelectedModels xmlns="http://www.something/">
<!--child elements... -->
</GetSelectedModels>
表示"the element GetSelectedModels
and all child elements belong in the namespace http://www.something/
by default"。
中的net:
前缀
<net:GetSelectedModels>
<!--child elements... -->
</net:GetSelectedModels>
仅仅意味着"the element GetSelectedModels
belongs in the namespace http://www.something/
"。它不适用于子元素。递归作用域是 default namespace attribute.
的特殊 属性
我知道了 xml。
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
<soapenv:Header/>
<soapenv:Body>
<GetSelectedModels xmlns="http://www.something/">
<!--Optional:-->
<input xmlns="http://www.something/">
<X_Models>
<ListOfModels>
<Model>
<ModelId>163</ModelId>
</Model>
</ListOfModels>
</X_Models>
</input>
</GetSelectedModels>
</soapenv:Body>
</soapenv:Envelope>
当我在 SoapUi 中 post 它到达我的 Web 服务时,我想使用这些节点。
XmlDocument docRequest = new XmlDocument();
docRequest.LoadXml(xml);
XmlNodeList models= docRequest.SelectNodes("X_Models/ListOfModels/Model");
模型为 0,xml 如下所示:
<X_Modelsxmlns="http://www.something/">
<ListOfModels>
<Model>
<ModelID>163</ModelID>
</Model>
</ListOfModelss>
</X_Models>
如果我 post 我的 xml 像这样使用别名:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:net="http://www.something/" >
<soapenv:Header/>
<soapenv:Body>
<net:GetSelectedModels>
<!--Optional:-->
<net:input>
<X_Models>
<ListOfModels>
<Model>
<ModelId>163</ModelId>
</Model>
</ListOfModels>
</X_Models>
</net:input>
</net:GetSelectedModels>
</soapenv:Body>
</soapenv:Envelope>
然后模型将为 1,xml 如下所示:
<X_Models>
<ListOfModels>
<Model>
<ModelID>163</ModelID>
</Model>
</ListOfModelss>
</X_Models>
任何人都可以指出我正确的方向吗?谢谢!
在第一种情况下,X_Models
节点和所有后代都在默认命名空间中,即 xmlns="http://www.something/"
。在第二种情况下,没有默认命名空间,因此 X_Models
树不在任何命名空间中。
XmlNode.SelectNodes Method (String)
的文档指定必须按如下方式处理默认名称空间:
If the XPath expression does not include a prefix, it is assumed that the namespace URI is the empty namespace. If your XML includes a default namespace, you must still use the XmlNamespaceManager and add a prefix and namespace URI to it; otherwise, you will not get any nodes selected. For more information, see Select Nodes Using XPath Navigation.
因此,考虑到您在问题开头显示的 XML(与您的代码不完全匹配),您需要执行以下操作:
string xml = @"<?xml version=""1.0"" standalone=""no"" ?>
<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" >
<soapenv:Header/>
<soapenv:Body>
<GetSelectedModels xmlns=""http://www.something/"">
<!--Optional:-->
<input xmlns=""http://www.something/"">
<X_Models>
<ListOfModels>
<Model>
<ModelId>163</ModelId>
</Model>
</ListOfModels>
</X_Models>
</input>
</GetSelectedModels>
</soapenv:Body>
</soapenv:Envelope>
";
XmlDocument docRequest = new XmlDocument();
docRequest.LoadXml(xml);
XmlNamespaceManager ns = new XmlNamespaceManager(docRequest.NameTable);
ns.AddNamespace("models", "http://www.something/");
ns.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
XmlNodeList models = docRequest.SelectNodes("/soapenv:Envelope/soapenv:Body/models:GetSelectedModels/models:input/models:X_Models/models:ListOfModels/models:Model", ns);
Debug.Assert(models.Count == 1);
更新
如果您真的想 运行 查询忽略名称空间并仅使用本地名称的 XML 层次结构,那么 SelectNodes
不会开箱即用。您将需要一些扩展方法,例如:
public static class XmlNodeExtensions
{
public static IEnumerable<XmlElement> ChildElements(this IEnumerable<XmlElement> elements)
{
return elements.SelectMany(e => e.ChildNodes.OfType<XmlElement>());
}
public static IEnumerable<XmlElement> OfLocalName(this IEnumerable<XmlElement> elements, string localName)
{
return elements.Where(e => e.LocalName == localName);
}
}
然后你可以这样做:
string xml1 = @"<?xml version=""1.0"" standalone=""no"" ?>
<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" >
<soapenv:Header/>
<soapenv:Body>
<GetSelectedModels xmlns=""http://www.something/"">
<!--Optional:-->
<input xmlns=""http://www.something/"">
<X_Models>
<ListOfModels>
<Model>
<ModelId>163</ModelId>
</Model>
</ListOfModels>
</X_Models>
</input>
</GetSelectedModels>
</soapenv:Body>
</soapenv:Envelope>
";
string xml2 = @"
<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:net=""http://www.something/"" >
<soapenv:Header/>
<soapenv:Body>
<net:GetSelectedModels>
<!--Optional:-->
<net:input>
<X_Models>
<ListOfModels>
<Model>
<ModelId>163</ModelId>
</Model>
</ListOfModels>
</X_Models>
</net:input>
</net:GetSelectedModels>
</soapenv:Body>
</soapenv:Envelope>
";
XmlDocument doc1 = new XmlDocument();
doc1.LoadXml(xml1);
XmlDocument doc2 = new XmlDocument();
doc2.LoadXml(xml2);
var ignoreNameSpaceModels1 = doc1.DocumentElement.ChildNodes.OfType<XmlElement>().OfLocalName("Body").ChildElements().OfLocalName("GetSelectedModels").ChildElements().OfLocalName("input").ChildElements().OfLocalName("X_Models").ToArray();
var ignoreNameSpaceModels2 = doc2.DocumentElement.ChildNodes.OfType<XmlElement>().OfLocalName("Body").ChildElements().OfLocalName("GetSelectedModels").ChildElements().OfLocalName("input").ChildElements().OfLocalName("X_Models").ToArray();
Debug.Assert(ignoreNameSpaceModels1.Length == 1 && ignoreNameSpaceModels2.Length == 1 && ignoreNameSpaceModels1[0].LocalName == "X_Models" && ignoreNameSpaceModels2[0].LocalName == "X_Models");
但这就像说typeof(System.Windows.Forms.RichTextBox) == typeof(System.Windows.Controls.RichTextBox)
因为他们的Name
是一样的。那是因为,下面的xmlns="http://www.something/"
属性:
<GetSelectedModels xmlns="http://www.something/">
<!--child elements... -->
</GetSelectedModels>
表示"the element GetSelectedModels
and all child elements belong in the namespace http://www.something/
by default"。
net:
前缀
<net:GetSelectedModels>
<!--child elements... -->
</net:GetSelectedModels>
仅仅意味着"the element GetSelectedModels
belongs in the namespace http://www.something/
"。它不适用于子元素。递归作用域是 default namespace attribute.