Reputation: 45
I got this 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>
When I post it in SoapUi and it reaches my web service I want to work with the nodes.
XmlDocument docRequest = new XmlDocument();
docRequest.LoadXml(xml);
XmlNodeList models= docRequest.SelectNodes("X_Models/ListOfModels/Model");
models is 0 and the xml looks like this:
<X_Modelsxmlns="http://www.something/">
<ListOfModels>
<Model>
<ModelID>163</ModelID>
</Model>
</ListOfModelss>
</X_Models>
If I post my xml like this with an alias:
<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>
Then models will be 1 and the xml looks like this:
<X_Models>
<ListOfModels>
<Model>
<ModelID>163</ModelID>
</Model>
</ListOfModelss>
</X_Models>
Can anyone point me in the right direction on this? Thank you!
Upvotes: 0
Views: 2144
Reputation: 117026
In the first case, the X_Models
node and all descendants are in the default namespace, which is xmlns="http://www.something/"
. In the second case there is no default namespace, so the X_Models
tree are not in any namespace.
The documentation for XmlNode.SelectNodes Method (String)
specifies that default namespaces must be handled as follows:
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.
Thus, given the XML you show at the start of your question (which doesn't quite match your code) you need to do:
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);
Update
If you really want to run queries on XML hierarchies that ignore namespaces and use only local names, then SelectNodes
isn't going to do that out of the box. You'll need a couple of extension methods like:
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);
}
}
And then you could do:
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");
But this is like saying that typeof(System.Windows.Forms.RichTextBox) == typeof(System.Windows.Controls.RichTextBox)
because their Name
is the same. That's because, the xmlns="http://www.something/"
attribute in the following:
<GetSelectedModels xmlns="http://www.something/">
<!--child elements... -->
</GetSelectedModels>
means "the element GetSelectedModels
and all child elements belong in the namespace http://www.something/
by default". The net:
prefix in
<net:GetSelectedModels>
<!--child elements... -->
</net:GetSelectedModels>
merely means "the element GetSelectedModels
belongs in the namespace http://www.something/
". It does not apply to child elements. Recursive scoping is a special property of the default namespace attribute.
Upvotes: 1