How can I get value of element attribute in XML - c#

Please how can I get value of attribute Value of element StatusCode in this XML:
<LogoutResponse
ID="_f525259e-7e91-4282-9dc3-a0da65a4a17a"
Version="2.0"
IssueInstant="2021-05-17T15:41:55Z"
InResponseTo="_5089729f-5cc0-4a66-a3c1-e710cde92897"
Destination="https://idp.xyz/logout.aspx"
xmlns="urn:oasis:names:tc:SAML:2.0:protocol">
<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://abc.xyz/api</Issuer>
<Status>
<StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
</Status>
</LogoutResponse>

Due to the namespaces, this gets a little messy:
var doc = new XmlDocument();
doc.LoadXml(#"<LogoutResponse
ID=""_f525259e-7e91-4282-9dc3-a0da65a4a17a""
Version=""2.0""
IssueInstant=""2021-05-17T15:41:55Z""
InResponseTo=""_5089729f-5cc0-4a66-a3c1-e710cde92897""
Destination=""https://idp.xyz/logout.aspx""
xmlns=""urn:oasis:names:tc:SAML:2.0:protocol"">
<Issuer xmlns=""urn:oasis:names:tc:SAML:2.0:assertion"">https://abc.xyz/api</Issuer>
<Status>
<StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success"" />
</Status>
</LogoutResponse>");
var ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("oasis", "urn:oasis:names:tc:SAML:2.0:protocol");
var attr = (XmlAttribute)doc.SelectSingleNode(
"/oasis:LogoutResponse/oasis:Status/oasis:StatusCode/#Value", ns);
System.Console.WriteLine(attr.Value);

You can try with XmlDocument.
Note: since you have a name space (xmlns ...) all elements in your xml
by default in name space. hence you are getting object reference
error. I have updated the code and .netfiddle. please have a look
var xml = #"<LogoutResponse
ID=""_f525259e-7e91-4282-9dc3-a0da65a4a17a""
Version=""2.0""
IssueInstant=""2021-05-17T15:41:55Z""
InResponseTo=""_5089729f-5cc0-4a66-a3c1-e710cde92897""
Destination=""https://idp.xyz/logout.aspx""
xmlns=""urn:oasis:names:tc:SAML:2.0:protocol"">
<Issuer xmlns=""urn:oasis:names:tc:SAML:2.0:assertion"">https://abc.xyz/api</Issuer>
<Status>
<StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success"" />
</Status>
</LogoutResponse>";
var doc = new System.Xml.XmlDocument();
doc.LoadXml(xml);
var nsManager = new XmlNamespaceManager(doc.NameTable);
nsManager.AddNamespace("ns", "urn:oasis:names:tc:SAML:2.0:protocol");
var result = doc.SelectSingleNode("/ns:LogoutResponse/ns:Status/ns:StatusCode/#Value", nsManager).InnerText;
Console.WriteLine(result);

By using LINQ to XML API.
It is available in the .Net Framework since 2007.
c#
void Main()
{
XDocument xdoc = XDocument.Parse(#"<LogoutResponse xmlns='urn:oasis:names:tc:SAML:2.0:protocol'
Destination='https://idp.xyz/logout.aspx' ID='_f525259e-7e91-4282-9dc3-a0da65a4a17a'
InResponseTo='_5089729f-5cc0-4a66-a3c1-e710cde92897' IssueInstant='2021-05-17T15:41:55Z' Version='2.0'>
<Issuer xmlns='urn:oasis:names:tc:SAML:2.0:assertion'>https://abc.xyz/api</Issuer>
<Status>
<StatusCode Value='urn:oasis:names:tc:SAML:2.0:status:Success'></StatusCode>
</Status>
</LogoutResponse>");
XNamespace ns1 = "urn:oasis:names:tc:SAML:2.0:protocol";
string StatusCodeValue = xdoc.Descendants(ns1 + "StatusCode")
.Attributes("Value")
.FirstOrDefault()?.Value;
Console.WriteLine("StatusCodeValue='{0}'", StatusCodeValue);
}
Output
StatusCodeValue='urn:oasis:names:tc:SAML:2.0:status:Success'

Related

Get XElement with XPathSelectElement

I have a XML file and loaded the specific tag <Acct> to an XElement. From this XElement in want to give a path to get the specific child <BIC> element. I don't wan't to use the Descendates() method. I tried using XPathSelectElement to get the job done but it always return null.
The XML Snippet i have looks something like this:
<Acct>
<Id>
<IBAN>TestIban</IBAN>
</Id>
<Ccy>EUR</Ccy>
<Svcr>
<FinInstnId>
<BIC>TestBic</BIC>
<ClrSysMmbId>
<ClrSysId>
<Cd>TestSysId</Cd>
</ClrSysId>
<MmbId>TestMemberId</MmbId>
</ClrSysMmbId>
<Nm>TestName</Nm>
<PstlAdr>
<AdrLine>TestAdrLine</AdrLine>
</PstlAdr>
<Othr>
<Id>OtherId</Id>
<Issr>Issr</Issr>
</Othr>
</FinInstnId>
</Svcr>
</Acct>
Like i said the XElement i have is the tag called acct. I tried now following to get the
<BIC> tag:
var bic = acct.XPathSelectElement("Svrc/FinInstnId/BIC");
var bic = acct.XPathSelectElement("/Svrc/FinInstnId/BIC");
var bic = acct.XPathSelectElement("./Svrc/FinInstnId/BIC");
var bic = acct.XPathSelectElement("Svrc//FinInstnId//BIC");
var bic = acct.XPathSelectElement("//Svrc//FinInstnId//BIC");
var bic = acct.XPathSelectElement(".//Svrc//FinInstnId//BIC");
var bic = acct.XPathSelectElement("Acct/Svrc/FinInstnId/BIC");
var bic = acct.XPathSelectElement("/Acct/Svrc/FinInstnId/BIC");
var bic = acct.XPathSelectElement("./Acct/Svrc/FinInstnId/BIC");
var bic = acct.XPathSelectElement("Acct//Svrc//FinInstnId//BIC");
var bic = acct.XPathSelectElement("//Acct//Svrc//FinInstnId//BIC");
var bic = acct.XPathSelectElement(".//Acct//Svrc//FinInstnId//BIC");
But all of these returning null. I found this article and they are doing in my point of view exactly the same:
https://codereview.stackexchange.com/questions/204464/get-a-xelement-at-a-given-path
SO my question is how do i use the XPathSelectElement method from my XElement to get a child like the tag as XElement?
Edit:
I inspected the XElement in Debug Mode with the Text Visualizer. The XML in the Element looks like the following (The values of the elements are different but this shouldn't change the problem):
<Acct xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
<Id>
<IBAN>TestIban</IBAN>
</Id>
<Ccy>EUR</Ccy>
<Svcr>
<FinInstnId>
<BIC>TestBic</BIC>
<ClrSysMmbId>
<ClrSysId>
<Cd>Cd</Cd>
</ClrSysId>
<MmbId>TestMbmId</MmbId>
</ClrSysMmbId>
<Nm>SomeName</Nm>
<PstlAdr>
<AdrLine>Address</AdrLine>
</PstlAdr>
<Othr>
<Id>Id</Id>
<Issr>Issr</Issr>
</Othr>
</FinInstnId>
</Svcr>
</Acct>
Code works fine
var xmlString = #"<root>
<Acct>
<Id>
<IBAN>TestIban</IBAN>
</Id>
<Ccy>EUR</Ccy>
<Svcr>
<FinInstnId>
<BIC>TestBic</BIC>
<ClrSysMmbId>
<ClrSysId>
<Cd>TestSysId</Cd>
</ClrSysId>
<MmbId>TestMemberId</MmbId>
</ClrSysMmbId>
<Nm>TestName</Nm>
<PstlAdr>
<AdrLine>TestAdrLine</AdrLine>
</PstlAdr>
<Othr>
<Id>OtherId</Id>
<Issr>Issr</Issr>
</Othr>
</FinInstnId>
</Svcr>
</Acct>
</root>";
XDocument doc = XDocument.Parse(xmlString);
XElement acct = doc.XPathSelectElement("root/Acct");
XElement element = acct.XPathSelectElement("Svcr/FinInstnId/BIC");
Console.WriteLine(element.Value);
//print "TestBic"
are you sure acct contain right XElement?
update with namespace
XDocument doc = XDocument.Parse(xmlString);
var reader = doc.CreateReader();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(reader.NameTable);
nsmgr.AddNamespace("ns", "urn:iso:std:iso:20022:tech:xsd:camt.053.001.02");
XElement acct = doc.XPathSelectElement("//ns:Acct", nsmgr);
XElement element = acct.XPathSelectElement("*[local-name() = 'Svcr']/*[local-name() = 'FinInstnId']/*[local-name() = 'BIC']");
var anotherWay = acct.XPathSelectElement("ns:Svcr/ns:FinInstnId/ns:BIC", nsmgr);
with local-name() you can ignore namespace

Use XPath with XML namespace

I want to process the xml below with XPath:
<?xml version="1.0" encoding="utf-8"?>
<ServiceConfiguration serviceName="Cloud" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="4" osVersion="*" schemaVersion="2014-01.2.3">
<Role name="Worker">
<Instances count="2" />
<ConfigurationSettings>
<Setting name="setting1" value="value1" />
<Setting name="setting2" value="value2" />
</ConfigurationSettings>
<Certificates>
</Certificates>
</Role>
</ServiceConfiguration>
Tere's a xmlns for the root element.
My code is this:
XElement doc = XElement.Load(xmlFilePath);
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("prefix", "http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration");
XElement settingDiagConnectionString = doc.XPathSelectElement("//prefix:ServiceConfiguration/Role/ConfigurationSettings/Setting[1]", ns); // <===== always return null.
settingDiagConnectionString.SetAttributeValue("value", "hello, world!");
But the settingDiagConnectionString is always null.
Why?
Add 1
Thanks har07.
After adding prefix for every element, the following code works:
XmlDocument doc = new XmlDocument();
doc.Load(cscfgPath);
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("prefix", "http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration");
XmlNode settingDiagConnectionString = doc.SelectNodes("/prefix:ServiceConfiguration/prefix:Role/prefix:ConfigurationSettings/prefix:Setting[1]", ns)[0];
settingDiagConnectionString.Attributes["value"].Value = "hello,world!";
But the following code still don't work. The settingDiagConnectionString is still null. Why?
XElement doc = XElement.Load(cscfgPath);
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("prefix", "http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration");
XElement settingDiagConnectionString = doc.XPathSelectElement("/prefix:ServiceConfiguration/prefix:Role/prefix:ConfigurationSettings/prefix:Setting[1]", ns);
settingDiagConnectionString.SetAttributeValue("value", "hello, world!");
Default namespace has different nature. The element where the default namespace declared and all of it's descendant without different namespace declaration considered in the same default namespace. Therefore, you need to use the prefix for all descendats as well. This XPath worked fine for me :
XElement doc = XElement.Parse(xml);
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("prefix", "http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration");
XElement settingDiagConnectionString = doc.XPathSelectElement("//prefix:Role/prefix:ConfigurationSettings/prefix:Setting[1]", ns);
settingDiagConnectionString.SetAttributeValue("value", "hello, world!");
UPDATE :
Responding to your update. That's because you're using XElement. In this case, doc it self already represents <ServiceConfiguration> element so you don't need to include "ServiceConfiguration" in the XPath :
/prefix:Role/prefix:ConfigurationSettings/prefix:Setting[1]
..or use XDocument instead of XElement if you want to make it work using the same XPath as for XmlDocument.

Trying to parse xml in Windows Phone 7.8 app but getting null value?

I am trying to parse a xml that I got as response to webservice response the xml is shown below
<GeneralSearchResponse>
<serverDetail></serverDetail>
<exceptions exceptionCount="1"></exceptions>
<clientTracking height="19" type="logo" width="106"></clientTracking>
<searchHistory></searchHistory>
<categories matchedCategoryCount="1" returnedCategoryCount="1">
<category id="0">
<name>bart</name>
<categoryURL>http://www.shopping.com/bart/products?oq=bart&linkin_id=7000610
</categoryURL>
<items matchedItemCount="1045" pageNumber="1" returnedItemCount="5">
<product id="130506037"></product>
<product id="104483377"></product>
<offer featured="false" id="tp-VCdOoO1RL6xICeRONqg==" smartBuy="false" used="false"></offer>
<offer featured="false" id="12evWWi57lddzFufngUWsg==" smartBuy="false" used="false"></offer>
<product id="96754577"></product>
</items>
<attributes matchedAttributeCount="5" returnedAttributeCount="5"></attributes>
<contentType>hybrid</contentType>
</category>
<intActualCategoryCount>4</intActualCategoryCount>
</categories>
<relatedTerms></relatedTerms>
</GeneralSearchResponse>
But when I am trying to parse using following code I am not able to get any descend or any node
XDocument xDoc = new XDocument();
xDoc = XDocument.Parse(data);
var xEle = xDoc.Root.Descendants("categories");
But xEle is not having any categories. Please let me know what is the issue??
Your XML has a default namespace - so the elements in it are in that namespace. The methods which find elements in LINQ to XML are namespace sensitive. You want:
XNamespace ns = "urn:types.partner.api.shopping.com";
XDocument xDoc = new XDocument();
xDoc = XDocument.Parse(data);
var xEle = xDoc.Root.Descendants(ns + "categories");

Whats wrong with my XPath?

I try to parse this XML file (config file from Chirpy):
<?xml version="1.0" encoding="utf-8" ?>
<root xmlns="urn:ChirpyConfig"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:ChirpyConfig http://www.weirdlover.com/chirpy/chirp.xsd">
<FileGroup Name="Built.debug.js" Minify="false">
<File Path="jquery/jquery-1.7.2.js"/>
<File Path="jquery.address/jquery.address-1.4.js" />
</FileGroup>
</root>
with this code:
var path = Server.MapPath("~/Scripts/ScriptfilesMashup.chirp.config");
var file = new XPathDocument(path);
var nav = file.CreateNavigator();
var nodes = nav.Select("/root/FileGroup/File");
but nodes is always empty, regardless of how I call the nav.Select method. I barely used XPath before so maybe I'm doing it wrong - but what? Only the selector * gives me the root node.
What would be the selector to get the Path Attribute of all File nodes?
EDIT: SOLUTION
Thanks to Kirill, the final solution looks like this:
var path = Server.MapPath("~/Scripts/ScriptfilesMashup.chirp.config");
var file = new XPathDocument(path);
var nav = file.CreateNavigator();
var ns = "urn:ChirpyConfig";
XmlNamespaceManager nsMgr = new XmlNamespaceManager(nav.NameTable);
nsMgr.AddNamespace("x", ns);
var nodes = nav.Select("/x:root/x:FileGroup/x:File/#Path", nsMgr);
while(nodes.MoveNext())
{
var path = nodes.Current.Value;
}
It is because elements root, FileGroup and File are defined in urn:ChirpyConfig namespace.
Use this:
XPathDocument xmldoc = new XPathDocument(xmlFile);
XPathNavigator nav = xmldoc.CreateNavigator();
XmlNamespaceManager nsMgr = new XmlNamespaceManager(nav.NameTable);
nsMgr.AddNamespace("x", "urn:ChirpyConfig");
XPathNavigator result = nav.SelectSingleNode("/x:root/x:FileGroup/x:File", nsMgr);

Why doesn't this XPath query returns any nodes?

I'm querying Sharepoint server-side and getting back results as Xml. I want to slim down the Xml into something more lightweight before sending it to jQuery through a WebMethod.
However my XPath query isn't working. I thought the following code would return all Document nodes, but it returns nothing. I've used XPath a little before, I thought //Document do the trick.
C# XPath query
XmlDocument xmlResults = new XmlDocument();
xmlResults.LoadXml(xml); // XML is a string containing the XML source shown below
XmlNodeList results = xmlResults.SelectNodes("//Document");
XML being queried
<ResponsePacket xmlns="urn:Microsoft.Search.Response">
<Response domain="QDomain">
<Range>
<StartAt>1</StartAt>
<Count>2</Count>
<TotalAvailable>2</TotalAvailable>
<Results>
<Document relevance="126" xmlns="urn:Microsoft.Search.Response.Document">
<Title>Example 1.doc</Title>
<Action>
<LinkUrl size="32256" fileExt="doc">http://hqiis99/Mercury/Mercury documents/Example 1.doc</LinkUrl>
</Action>
<Description />
<Date>2010-08-19T14:44:56+01:00</Date>
</Document>
<Document relevance="31" xmlns="urn:Microsoft.Search.Response.Document">
<Title>Mercury documents</Title>
<Action>
<LinkUrl size="0" fileExt="aspx">http://hqiis99/mercury/Mercury documents/Forms/AllItems.aspx</LinkUrl>
</Action>
<Description />
<Date>2010-08-19T14:49:39+01:00</Date>
</Document>
</Results>
</Range>
<Status>SUCCESS</Status>
</Response>
</ResponsePacket>
You're trying to select Document elements which don't have a namespace... whereas the default namespace is actually "urn:Microsoft.Search.Response" here.
I think you want something like this:
XmlDocument xmlResults = new XmlDocument();
xmlResults.LoadXml(xml);
XmlNamespaceManager manager = new XmlNamespaceManager(xmlResults.NameTable);
manager.AddNamespace("ns", "urn:Microsoft.Search.Response.Document");
XmlNodeList results = xmlResults.SelectNodes("//ns:Document", manager);
This finds two elements.
If you can use LINQ to XML instead, it makes it all somewhat easier:
XDocument results = XDocument.Parse(xml);
XNamespace ns = "urn:Microsoft.Search.Response.Document";
var documents = results.Descendants(ns + "Document");
I love LINQ to XML's namespace handling :)
Alternatively, you could try the following and ignore the namespaces:
XmlDocument xmlResults = new XmlDocument();
xmlResults.LoadXml(xmlString);
XmlNodeList results = xmlResults.SelectNodes("//*[local-name()='Document']");

Categories