XDocument get and set values in XML nodes - c#

I have this XML
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<wss:Security xmlns:wss="http://schemas.xmlsoap.org/ws/2002/12/secext">
<wss:UsernameToken>
<wss:Username>username</wss:Username>
<wss:Password>password</wss:Password>
<wss:Nonce></wss:Nonce>
<wss:Created></wss:Created>
</wss:UsernameToken>
</wss:Security>
</S:Header>
<S:Body>
<TaxRegistrationNumber>213213123</TaxRegistrationNumber>
<CompanyName>sadsadasd</CompanyName>
</S:Body>
</S:Envelope>
I want to accesses to the value of <wss:Username> and set a value in <wss:Nonce> node.
I already try 3 ways to get value of <wss:Username> on C# project:
First:
XDocument xmlFile = XDocument.Load(xmlpathfile);
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("wss", "http://schemas.xmlsoap.org/ws/2002/12/secext/");
XElement UserFinanc = xmlFile.XPathSelectElement("wss:Security/wss:UsernameToken/wss:Username", ns);
Second:
XDocument xmlFile = XDocument.Load(xmlpathfile);
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
var element = xmlFile.Descendants(wss + "Security").Descendants(wss + "UsernameToken").Where(x => x.Descendants(wss + "Username").Any(y => y.Value != "")).First().Element(wss + "UsernameToken");
if (element != null)
MessageBox.Show(element.Element(wss + "Username").Value).Value);
Third:
string grandChild = (string) (from el in xmlFile.Descendants(wss + "Username") select el).First();
MsgBox.Show(grandChild);
I always have similar errors like 'The sequence contains no elements'

Your first attempt is almost right. There are a couple things missing:
The namespace defined in code must be an exact match to the one in the xml. In your case the namespace in code has an extra trailing slash. It should be http://schemas.xmlsoap.org/ws/2002/12/secext.
The XPath expression should be //wss:Security/wss:UsernameToken/wss:Username. The leading slashes basically mean "look for this node anywhere". Alternatively, you could write out the whole path begining with <S:Envelope>. You would need to add the soap envelope namespace to your code as well.

Related

XDocument with multiple namespace in c#

Here is my xml. i am using XDocument in C#. I want to get the "recordSetCount".
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ShowPositionOpening xmlns="http://data.usajobs.gov">
<ApplicationArea xmlns="http://www.openapplications.org/oagis/9">
<CreationDateTime>2014-12-11T04:05:41</CreationDateTime>
</ApplicationArea>
<DataArea xmlns="http://www.hr-xml.org/3">
<Show recordSetCount="6" recordSetTotal="6" recordSetCompleteIndicator="false" recordSetReferenceId="1" xmlns="http://www.openapplications.org/oagis/9">
<OriginalApplicationArea>
<CreationDateTime>2014-12-11T04:05:41</CreationDateTime>
</OriginalApplicationArea>
</Show>
I have tried like below
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
var jobCount = x.XPathSelectElement("/s:Envelope/s:Body/ShowPositionOpening/DataArea/Show/#recordSetCount", namespaceManager).Value;
but it dint work. Also tried like below
XNamespace xmlns = "http://www.openapplications.org/oagis/9";
XNamespace xmlns1 = "http://data.usajobs.gov";
XNamespace x1 = "http://www.hr-xml.org/3";
var jobCount = x.Element("ShowPositionOpening")
.Element(xmlns1 + "DataArea")
.Element(x1 + "Show")
.Attribute("recordSetTotal");
But it didn't work. What went wrong. Can any one help me?
Your data has multiple namespaces where each child is in a different namespace, you'll need to adjust your queries accordingly.
ShowPositionOpening http://data.usajobs.gov
DataArea http://www.hr-xml.org/3
Show http://www.openapplications.org/oagis/9
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
namespaceManager.AddNamespace("X", "http://data.usajobs.gov");
namespaceManager.AddNamespace("XX", "http://www.hr-xml.org/3");
namespaceManager.AddNamespace("XXX", "http://www.openapplications.org/oagis/9");
// you'll need to change this to XPathEvaluate
// since you're not evaluating to an element
var JobCount = x.XPathEvaluate("/s:Envelope/s:Body/X:ShowPositionOpening/XX:DataArea/XXX:Show/#recordSetCount", namespaceManager);
or using linq:
XNamespace n = "http://data.usajobs.gov";
XNamespace nn = "http://www.hr-xml.org/3";
XNamespace nnn = "http://www.openapplications.org/oagis/9";
var JobCount = x.Descendants(n + "ShowPositionOpening")
.Elements(nn + "DataArea")
.Elements(nnn + "Show")
.Attributes("recordSetCount")
.SingleOrDefault();

How to get a node from xml with c#- what am I doing wrong?

Namespaces and XML are still confusing the hell out of me.
Here is my XML (that comes from a SOAP request)
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<MyResponse xmlns="http://tempuri.org/">
<OutputXML xmlns="http://tempuri.org/XMLSchema.xsd">
<Result>
<OutputXML>
<Result>
<Foo>
<Bar />
</Foo>
</Result>
</OutputXML>
</Result>
</OutputXML>
</MyResponse>
</soap:Body>
</soap:Envelope>
I am trying to extract the actual XML part from the SOAP response (starting with the Foo element):
var nsmgr = new XmlNamespaceManager(document.NameTable);
nsmgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
nsmgr.AddNamespace("", "http://tempuri.org/");
nsmgr.AddNamespace("", "http://tempuri.org/XMLSchema.xsd");
var xml = document.DocumentElement
.SelectSingleNode("Foo", nsmgr)
.InnerXml;
But SelectSingleNode returns null. I've tried some different variations on this but can't get anything working. What am I not understanding?
Try this one:
var nsmgr = new XmlNamespaceManager(document.NameTable);
nsmgr.AddNamespace("aaa", "http://tempuri.org/XMLSchema.xsd");
var xml = document.DocumentElement
.SelectSingleNode("aaa:Foo", nsmgr)
.InnerXml;
this is because of Default namespaces has no perfix.
You can use GetElementsByTagName to use namespace uri directly:
var xml = document.GetElementsByTagName("Foo",
"http://tempuri.org/XMLSchema.xsd")[0].InnerXml;
You can use LINQ to XML to get your result, also specify the namespace
XDocument document = XDocument.Load("test.xml");
XNamespace ns = "http://tempuri.org/XMLSchema.xsd";
var test = document.Descendants(ns + "Foo").FirstOrDefault();
Or if you don't want to specify NameSpace then:
var test2 = document.Descendants()
.Where(a => a.Name.LocalName == "Foo")
.FirstOrDefault();

C# Xml Value always getting null

I have the following Xml in my Resources.xmltest:
<?xml version="1.0" encoding="utf-8" ?>
<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://DFISofft.com/SmartPayments/">
<Result>0</Result>
<Message>Pending</Message>
<PNRef>222131</PNRef>
<ExtData>InvNum=123</ExtData>
</Response>
I've tried several ways to get the values, Result,Message,PNRef,ExtData, out of it and I've had no luck. I always get a null value for the NodePath so it never goes into the loop:
var XmlDoc = new XmlDocument();
XmlDoc.LoadXml(Resources.xmltest);
XmlElement NodePath = (XmlElement) XmlDoc.SelectSingleNode("/Response");
while (NodePath != null)
{
foreach (XmlNode Xml_Node in NodePath)
{
Console.WriteLine(Xml_Node.Name + " " + Xml_Node.InnerText);
}
}
I've tried this:
XmlNode node3 = XmlDoc.SelectSingleNode("PNRef");
Console.WriteLine(node3.Value);
Console.WriteLine(XmlDoc.InnerXml);
var tst = XmlDoc.GetElementsByTagName("PNRef");
Console.WriteLine(tst);
And this:
NodePath = (XmlElement) XmlDoc.SelectSingleNode("/Response");
if (NodePath != null)
{
foreach (XmlNode node in NodePath)
{
Console.WriteLine("NodeName: " + Xml_NodeX.Name);
Console.WriteLine("NodeValue: " + node.InnerText);
}
}
Apparently, I'm not getting the xml read/write. I've done it with DataSets but they do all the work for you.
Can someone point me in the right direction? I've been at this for longer than I should have been already.
Thank you!
Your XML has a XML namespace and you're not paying any attention to it:
<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://DFISofft.com/SmartPayments/">
******************************************
When selecting from this XML, you need to use that XML namespace!
You're not taking into account the XML namespace (xmlns="http://nts-de-osm1-pxc/webservices/") on the document!
Try this:
var XmlDoc = new XmlDocument();
// setup the XML namespace manager
XmlNamespaceManager mgr = new XmlNamespaceManager(XmlDoc.NameTable);
// add the relevant namespaces to the XML namespace manager
mgr.AddNamespace("ns", "http://DFISofft.com/SmartPayments/");
XmlDoc.LoadXml(Resources.xmltest);
// **USE** the XML anemspace in your XPath !!
XmlElement NodePath = (XmlElement) XmlDoc.SelectSingleNode("/ns:Response");
while (NodePath != null)
{
foreach (XmlNode Xml_Node in NodePath)
{
Console.WriteLine(Xml_Node.Name + " " + Xml_Node.InnerText);
}
}
The problem is your XPath query, which doesn't specify a namespace - despite the fact that your document only has a Response element in the "http://DFISofft.com/SmartPayments/" namespace. Ditto the PNRef elements.
Personally I'd use LINQ to XML if I actually wanted to use namespaces if at all possible. For example:
XNamespace ns = "http://DFISofft.com/SmartPayments/";
XDocument doc = XDocument.Load(filename);
foreach (var element in doc.Descendants(ns + "PNRef"))
{
// Use element here
}
As you can see, LINQ to XML makes it really easy to use namespaces - but of course it requires .NET 3.5 or higher. If I had to use .NET 2.0, I'd either use XmlNamespaceManager or iterate manually and check local names instead of using XPath.
That is because the node isn't called response; you need to take the namespace into account:
var XmlDoc = new XmlDocument();
var nsmgr = new XmlNamespaceManager(XmlDoc.NameTable);
nsmgr.AddNamespace("x", "http://DFISofft.com/SmartPayments/");
XmlDoc.LoadXml(yourXml);
XmlElement NodePath = (XmlElement)XmlDoc.SelectSingleNode("/x:Response", nsmgr);
For future reference...
Bubba Soft
This site has a great free tool for building XPath Expressions (XPath Builder).
It's because you are using the overload of SelectSingleNode that assumes an empty namespace. Since you have a default namespace you will need to use the version that takes an XmlNamespaceManager. See this article for more info.
from the docs:
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 add a prefix and namespace URI to the XmlNamespaceManager; otherwise, you will not get a node selected. For more information, see Select Nodes Using XPath Navigation.

XDocument and Linq returns null if the element has xmlns attribute

Newbie with XDocuments and Linq, please suggest a solution to retrieve the data from a particular tag in the xml string:
If I have a XML string from webservice response (I formatted xml for ease):
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetCashFlowReportResponse xmlns="http://tempuri.org/">
<GetCashFlowReportPdf>Hello!</GetCashFlowReportPdf>
</GetCashFlowReportResponse>
</soap:Body>
</soap:Envelope>
Using the following code, I can get the value only if GetCashFlowReportResponse tag doesn't have "xmlns" attribute. Not sure why? Otherwise, it always return null.
string inputString = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><soap:Body><GetCashFlowReportResponse xmlns=\"http://tempuri.org/\"><GetCashFlowReportPdf>Hello!</GetCashFlowReportPdf></GetCashFlowReportResponse></soap:Body></soap:Envelope>"
XDocument xDoc = XDocument.Parse(inputString);
//XNamespace ns = "http://tempuri.org/";
XNamespace ns = XNamespace.Get("http://tempuri.org/");
var data = from c in xDoc.Descendants(ns + "GetCashFlowReportResponse")
select (string)c.Element("GetCashFlowReportPdf");
foreach (string val in data)
{
Console.WriteLine(val);
}
I can't change the web service to remove that attribute. IS there a better way to read the response and get the actual data back to the user (in more readable form)?
Edited:
SOLUTION:
XDocument xDoc = XDocument.Parse(inputString);
XNamespace ns = "http://tempuri.org/";
var data = from c in xDoc.Descendants(ns + "GetCashFlowReportResponse")
select (string)c.Element(ns + "GetCashFlowReportPdf");
foreach (string val in data)
{
Console.WriteLine(val);
}
Note: Even if all the child elements doesn't have the namespace attribute, the code will work if you add the "ns" to the element as I guess childs inherit the namespace from parent (see response from SLaks).
You need to include the namespace:
XNamespace ns = "http://tempuri.org/";
xDoc.Descendants(ns + "GetCashFlowReportResponse")
XName qualifiedName = XName.Get("GetCashFlowReportResponse",
"http://tempuri.org/");
var data = from d in xDoc.Descendants(qualifiedName)
Just ask for the elements using the qualified names:
// create a XML namespace object
XNamespace ns = XNamespace.Get("http://tempuri.org/");
var data = from c in xDoc.Descendants(ns + "GetCashFlowReportResponse")
select (string)c.Element(ns + "GetCashFlowReportPdf");
Note the use of the overloaded + operator that creates a QName with a XML namespace and a local name string.

issue to get specific XML element value using C#

Suppose I have the following XML document, how to get the element value for a:name (in my sample, the value is Saturday 100)? My confusion is how to deal with the name space. Thanks.
I am using C# and VSTS 2008.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<PollResponse xmlns="http://tempuri.org/">
<PollResult xmlns:a="http://schemas.datacontract.org/2004/07/FOO.WCF" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:name>Saturday 100</a:name>
</PollResult>
</PollResponse>
</s:Body>
</s:Envelope>
Use System.Xml.XmlTextReader class,
System.Xml.XmlTextReader xr = new XmlTextReader(#"file.xml");
while (xr.Read())
{
if (xr.LocalName == "name" && xr.Prefix == "a")
{
xr.Read();
Console.WriteLine(xr.Value);
}
}
It's easier if you use the LINQ to XML classes. Otherwise namespaces really are annoying.
XNamespace ns = "http://schemas.datacontract.org/2004/07/FOO.WCF";
var doc = XDocument.Load("C:\\test.xml");
Console.Write(doc.Descendants(ns + "name").First().Value);
Edit. Using 2.0
XmlDocument doc = new XmlDocument();
doc.Load("C:\\test.xml");
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("a", "http://schemas.datacontract.org/2004/07/FOO.WCF");
Console.Write(doc.SelectSingleNode("//a:name", ns).InnerText);
XPath is the direct way to get at bits of an XML document in 2.0
XmlDocument xml = new XmlDocument();
xml.Load("file.xml")
XmlNamespaceManager manager = new XmlNamespaceManager(xml.NameTable);
manager.AddNamespace("a", "http://schemas.datacontract.org/2004/07/FOO.WCF");
string name = xml.SelectSingleNode("//a:name", manager).InnerText;

Categories

Resources