Iterating over string XML to read what is inside the nodes - c#

I have an xml which is string. This is part of it.
<GetModuleInfoResult xmlns =\"http://schemas.datacontract.org/2004/07/GMS.Integration.Service.Contracts.Results\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
<Code>0</Code>
<Message i:nil=\"true\"/>
<Parameters xmlns:a=\"http://schemas.datacontract.org/2004/07/GMS.Integration.Service.Contracts.Entities\">
<a:Parameter>
<a:Key>width</a:Key>
<a:Value>1068</a:Value>
</a:Parameter>
<a:Parameter>
<a:Key>height</a:Key>
<a:Value>600</a:Value>
</a:Parameter>
I want to get all the parameter nodes and read their 'key' and 'Value' nodes.
How can I achieve that?

Here is one way to grab the key-value pairs from your xml
XDocument doc = XDocument.Load(/* YOUR XML FILE **/);
XNamespace x = "http://schemas.datacontract.org/2004/07/GMS.Integration.Service.Contracts.Entities";
var parameters = doc.Descendants(x + "Parameter").ToDictionary(e => e.Descendants(x + "Key").First().Value, e => e.Descendants(x + "Value").First().Value);

Related

Removing header in xml file required?

I have the xml file below and I want to retrieve the sym attribute from the Instrmt element using LINQ to XML. In order to do that, do I have to remove the first line: FIXML element?
If I do that then the code below works:
IEnumerable<string> symbols = from c in xml.Descendants("Instrmt")
select (string)c.Attribute("Sym");
Here is the xml.
<FIXML r="20030618" s="20040109" v="4.4" xr="FIA" xv="1" xmlns="http://www.fixprotocol.org/FIXML-4-4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.fixprotocol.org/FIXML-4-4 https://optionsclearing.com/components/docs/membership/dds_ref/fia_1_1/fixml-main-4-4-FIA-1-1.xsd">
<Batch>
<SecList ListTyp="109" ListID="20175" BizDt="2017-12-07">
<SecL Ccy="USD">
<Instrmt Desc="iShares S&P 100 ETF" SecTyp="OPT" SubTyp="ETO" Sym="OEF" Mult="100.0">
<AID AltID="00013" AltIDSrc="RBHP"/>
</Instrmt>
<InstrmtExt>
<Attrb Typ="101" Val="1.0000"/>
<Attrb Typ="108" Val="1.0000"/>
</InstrmtExt>
<Undly Desc="iShares S&P 100 ETF" Px="117.110000" Ccy="USD" Sym="OEF" ID="464287101" Src="1"/>
<Stip Typ="RBHMIN" Val="2.500"/>
<Stip Typ="CPMMIN" Val="3.750"/>
</SecL>
</SecList>
</Batch>
</FIXML>
Ok, your problem is not about the header, it's about the namespace (defined by the xmlns attribute). As your xml define one, all your xml children elements will have it as their default. Try the following code:
var xml = XDocument.Load(pathToFile);
XNamespace ns = xml.Root.GetDefaultNamespace();
IEnumerable<string> symbols = from c in xml.Descendants(ns + "Instrmt")
select (string)c.Attribute("Sym");

System.InvalidOperationException when modifiying value in an xml file in C#

So i... Have this snippet of code what writes to an existing xml file... the code to me is VERY simple...
XElement element;
XDocument xdoc = XDocument.Load(FileLoc);
element = xdoc.Elements(XName.Get("gold", "http://schemas.datacontract.org/2004/07/DumaLegend")).Single();
element.Value = Gold.Text;
Good Right? good! but why does it give out that error which means that it can't find the thing? it's a very valid thing....
here is the xml file:
<?xml version="1.0" encoding="utf-8"?>
<Save xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/DumaLegend">
<saveInfo>
<energyPieces>0</energyPieces>
<fullEnergyCells>4</fullEnergyCells>
<fullHearts>4</fullHearts>
<globalSwitches xmlns:d3p1="a">
<d3p1:switchList xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
</globalSwitches>
<gold>0</gold>
<hasBigFireball>false</hasBigFireball>
<hasCombo>false</hasCombo>
<hasCrossbow>false</hasCrossbow>
<hasDash>false</hasDash>
<hasDashUpgrade>false</hasDashUpgrade>
<hasDoubleJump>false</hasDoubleJump>
<hasFireball>false</hasFireball>
<hasHookshot>false</hasHookshot>
<hasInvisPot>false</hasInvisPot>
<hasSecondCombo>false</hasSecondCombo>
<hasShieldUpgrade>false</hasShieldUpgrade>
<hasSmallFireball>false</hasSmallFireball>
<heartPieces>0</heartPieces>
<heroPosOnMap>0</heroPosOnMap>
<heroTokens>0</heroTokens>
<itemSlot1 xmlns:d3p1="http://schemas.datacontract.org/2004/07/DumaLegend.Objects.Consumables" i:nil="true" />
<itemSlot2 xmlns:d3p1="http://schemas.datacontract.org/2004/07/DumaLegend.Objects.Consumables" i:nil="true" />
<lives>3</lives>
<worldsUnlocked>0</worldsUnlocked>
<worldsUnlockedOnMap>0</worldsUnlockedOnMap>
</saveInfo>
<saveSlot>0</saveSlot>
</Save>
Use xdoc.Descendants(XName.Get("gold", "http://schemas.datacontract.org/2004/07/DumaLegend")).
From the docs for Elements
Returns a filtered collection of the child elements of this element or document, in document order. Only elements that have a matching XName are included in the collection.
There is only one child elements of your document, and that is the Save element.
What you are looking for is at the path Save/saveInfo/gold. So you can either use Elements like this:
XNamespace ns = "http://schemas.datacontract.org/2004/07/DumaLegend";
var gold = doc.Elements(ns + "Save")
.Elements(ns + "saveInfo")
.Elements(ns + "gold")
.Single();
Or you can use Descendants, which will search all child elements recursively.
XNamespace ns = "http://schemas.datacontract.org/2004/07/DumaLegend";
var gold = doc.Descendants(ns + "gold").Single();

XML: Retrieve a particular value from xml

From the following XML, I want to find a value based on the Employer.
<?xml version="1.0" encoding="UTF-8"?>
<Document>
<Details>
<Employer>Taxes</Employer>
<Adr>
<Strt>Street</Strt>
<Twn>Town</Twn>
</Adr>
</Details>
<DetailsAcct>
<Recd>
<Payroll>
<Id>9</Id>
</Payroll>
</Recd>
<br>
<xy>A</xy>
</br>
</DetailsAcct>
</Document>
the C# code I applied is
detail = root.SelectSingleNode($"//w:Document//w:Employer[contains(text(), 'Taxes']/ancestor::Employer",nsmgr);
But it gives me an invalid token error.
What am I missing?
The error was due to [contains(...], notice closing parentheses is missing. And since you want to return Employer element, no need for ancestor::Employer here :
//w:Document//w:Employer[contains(., 'Taxes')]
If the XML posted resembles structure of the actual XML (except the namespaces), better to use more specific XPath i.e avoid using costly // :
/w:Document/w:Details/w:Employer[contains(., 'Taxes')]
An alternative is to use LINQ to XML.
If the XML is in a string:
string xml = "<xml goes here>";
XDocument document = XDocument.Parse(xml);
XElement element = document.Descendants("Employer").First();
string value = element.Value;
If the XML is in a .xml file:
XDocument document = XDocument.Load("xmlfile.xml");
XElement element = document.Descendants("Employer").First();
string value = element.Value;
You can also find an employer element with a specific value, if that's what you need:
XElement element = document.Descendants("Employer").First(e => e.Value == "Taxes");
Note: this will throw an exception if no element is found with the specified value. If that is not acceptable, then you can replace .First(...) with .FirstOrDefault(...) which will simply return null if no element is found.

get contents from inner xml with xml parsing in c#?

I have a service which returns the below xml as string.I am using Xdocument parse method and XmlDocument load methods to convert the string to xml. but i want to parse and get the status and i_numer which i need to use for further processing.can some one point me in right direction or give some hints.below is the xml i am using.
i tried the innerxml property from the Xdocument and XmlDocument which is returning the whole "" element and this is not what i needed.
<Report>
<Incidentreport Company="company1" ID="sample">
<status i_number="12345678" status="sucessful" />
</Incidentreport>
</Report>
The following should work:
string str = [string of xml goes here];
string i_number = string.Empty;
XmlDocument doc = new XmlDocument();
doc.Load(str);
XmlNode node = doc.SelectSingleNode("//status");
i_number = node.Attributes["i_number"].Value;
You can use SelectSingleNode() which accept XPath parameter to get the target attribute value in one go * :
var raw = #"<Report>
<Incidentreport Company='company1' ID='sample'>
<status i_number='12345678' status='sucessful' />
</Incidentreport>
</Report>";
var doc = new XmlDocument();
doc.LoadXml(raw);
var result = doc.SelectSingleNode("/Report/Incidentreport/status/#i_number");
Console.WriteLine(result.Value);
dotnetfiddle demo
*) notice how XML attribute can be referenced by using #attribute_name syntax in XPath

Using XmlDocument() with Nodes that have xmlns attributes? [duplicate]

This question already has answers here:
XPath doesn't work as desired in C#
(2 answers)
Closed 10 years ago.
I'm trying to use XmlDocument() to read an XML file node by node an output each element.
After much trial-and-error, I determined that having an xmlns attribute on my node causes no nodes to be returned from SelectNodes() call. Not sure why.
Since I can't change the output format and don't have access to the actual namespace, what are my options to get around this issue?
In addition, I have some elements that have subnodes. How do I access these while looping through the XML file? I.E., I need to decrypt the CipherValue elements, but not sure how to access this node anyway?
Sample below:
doc.Load(#"test.xml");
XmlNodeList logEntryNodeList = doc.SelectNodes("/Logs/LogEntry");
Console.WriteLine("Nodes = {0}", logEntryNodeList.Count);
foreach (XmlNode xn in logEntryNodeList)
{
string dateTime = xn["DateTime"].InnerText;
string sequence = xn["Sequence"].InnerText;
string appId = xn["AppID"].InnerText;
Console.WriteLine("{0} {1} {2}", dateTime, sequence, appId);
}
Sample XML looks like this:
<Logs>
<LogEntry Version="1.5" PackageVersion="10.10.0.10" xmlns="http://private.com">
<DateTime>2013-02-04T14:05:42.912349-06:00</DateTime>
<Sequence>5058</Sequence>
<AppID>TEST123</AppID>
<StatusDesc>
<EncryptedData>
<CipherData xmlns="http://www.w3.org/2001/04/xmlenc#">
<CipherValue>---ENCRYPTED DATA BASE64---</CipherValue>
</CipherData>
</EncryptedData>
</StatusDesc>
<Severity>Detail</Severity>
</LogEntry>
<LogEntry Version="1.5" PackageVersion="10.10.0.10" xmlns="http://private.com">
<DateTime>2013-02-04T14:05:42.912350-06:00</DateTime>
<Sequence>5059</Sequence>
<AppID>TEST123</AppID>
<StatusDesc>
<EncryptedData>
<CipherData xmlns="http://www.w3.org/2001/04/xmlenc#">
<CipherValue>---ENCRYPTED DATA BASE64---</CipherValue>
</CipherData>
</EncryptedData>
</StatusDesc>
<Severity>Detail</Severity>
</LogEntry>
</Logs>
After much trial-and-error, I determined that having an xmlns attribute on my node causes no nodes to be returned from SelectNodes() call. Not sure why.
The xmlns attribute effectively changes the default namespace within the element, including that element itself. So the namespace of your LogEntry element is "http://private.com". You'd need to include this appropriately in your XPath query, probably via an XmlNamespaceManager.
(If you can use LINQ to XML instead, it makes it much easier to work with namespaces.)
You can use Linq to Xml (as Jon stated). Here is code which parses your xml file with respect to namespaces. Result is a strongly typed sequence of anonymous objects (i.e. Date property has type of DateTime, Sequence is integer, and AppID is a string):
XDocument xdoc = XDocument.Load("test.xml");
XNamespace ns = "http://private.com";
var entries = xdoc.Descendants("Logs")
.Elements(ns + "LogEntry")
.Select(e => new {
Date = (DateTime)e.Element(ns + "DateTime"),
Sequence = (int)e.Element(ns + "Sequence"),
AppID = (string)e.Element(ns + "AppID")
}).ToList();
Console.WriteLine("Entries count = {0}", entries.Count);
foreach (var entry in entries)
{
Console.WriteLine("{0}\t{1} {2}",
entry.Date, entry.Sequence, entry.AppID);
}

Categories