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
Related
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'
string xml = #"<vehicle> <cars> <car>tet</car> </cars> </vehicle>";
XElement x = XElement.Parse(xml);
var node = x.XPathSelectElement("//vehicle/cars");
why is the node always null?
XPath should be
var node = x.XPathSelectElement("cars");
OUTPUT:
<cars>
<car>tet</car>
</cars>
<root>
<element1>innertext</element1>
<element2>innertext</element2>
<element3>
<child1>innertext</child1>
</element3>
</root>
I have an xml structure shown above.
I would like to "append" the xml file (it is already created) to add another "child" inside element3>, so that it will look like this:
<root>
<element1>innertext</element1>
<element2>innertext</element2>
<element3>
<child1>innertext</child1>
<child2>innertext</child2>
</element3>
</root>
Linq to xml and/or Xpath would be great
EDIT:
I have tried doing this:
XElement doc = XElement.Load(mainDirectory);
XElement newElem = doc.Elements("element3").First();
newElem.Add(new XElement("child2", "child2innertext"));
doc.Add(newElem);
doc.Save(mainDirectory);
XmlDocument xDoc = new XmlDocument();
xDoc.Load("filename.xml");
foreach (XmlNode xNode in xDoc.SelectNodes("/root/element3"))
{
XmlElement newElement = xDoc.CreateElement("Child2");
xNode.AppendChild(newElement);
xNode.InnerText = "myInnerText";
}
With XDocument you can achieve this as:
string xml = "<root><element1>innertext</element1><element2>innertext</element2><element3><child1>innertext</child1></element3></root>";
var doc = XDocument.Parse(xml); //use XDocument.Load("filepath"); in case if your xml is in a file.
var el3 = doc.Descendants("element3").FirstOrDefault();
el3.Add(new XElement("child2", "innertext"));
Please, try this LINQPAD example
void Main()
{
var xml =
#"<root>
<element1>innertext</element1>
<element2>innertext</element2>
<element3>
<child1>innertext</child1>
</element3>
</root>";
var doc = XDocument.Parse(xml);
doc.Root.Element("element3")
.Add(new XElement("child2", "innertext"));
doc.Dump();
}
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);
I am trying to get the values for faultcode, faultstring, and OrderNumber from the SOAP below
<SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP:Body>
<faultcode>1234</faultcode>
<faultstring>SaveOrder:SetrsOrderMain:Cannot change OrderDate if GLPeriod is closed, new OrderDate is 3/2/2010:Ln:1053</faultstring>
<detail>
<SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP:Body UserGUID="test">
<m:SaveOrder xmlns:m="http://www.test.com/software/schema/" UserGUID="test">
<Order OrderNumber="1234-1234-123" Caller="" OrderStatus="A" xmlns="http://www.test.com/software/schema/">
Here is my code in C#
XDocument doc = XDocument.Load(HttpContext.Current.Server.MapPath("XMLexample.xml"));
var errorDetail = new EcourierErrorDetail
{
FaultCode = from fc in doc.Descendants("faultcode")
select fc.Value,
FaultString = from fs in c.Descendants("faultstring")
select fs.Value,
OrderNumber = from o in
doc.Descendants("detail").Elements("Order").Attributes("OrderNumber")
select o.Value
};
return errorDetail;
I am able to get the values for both faultcode and faultstring but not the OrderNumber. I am getting "Enumeration yielded no results." Can anyone help? Thanks.
Yes, you're ignoring the XML namespace when selecting:
<SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
.....
<m:SaveOrder xmlns:m="http://www.test.com/software/schema/" UserGUID="test">
<Order OrderNumber="1234-1234-123" Caller="" OrderStatus="A" xmlns="http://www.test.com/software/schema/">
The <Order> tag is inside the <m:SaveOrder> tag which uses the XML namespace prefixed by the m: prefix.
Also, you're trying to select the "detail" and then you skip to the "Order" node directly (using .Elements()) - you missed the <m:SaveOrder> node in between.
You need to take that into account when selecting:
XDocument doc = XDocument.Load(HttpContext.Current.Server.MapPath("XMLexample.xml"));
XNamespace xmlns = "http://www.test.com/software/schema/";
var orderNode = doc.Descendants(xmlns + "SaveOrder").Elements(xmlns + "Order");
var value = from o in orderNode.Attributes("OrderNumber")
select o.Value;
Does that give you a result??
A clean solution could be using System.Xml.XPath. Here is where I found the example
This is how to use it in this case:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(result);
XmlNamespaceManager xmlnsManager = new XmlNamespaceManager(xmlDoc.NameTable);
xmlnsManager.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
xmlnsManager.AddNamespace("m", "http://www.test.com/software/schema/");
var orderNode = xmlDoc.SelectSingleNode("//m:Order", xmlnsManager);
var value = orderNode.Attributes["OrderNumber"].Value;