Trouble Parsing XML - c#

I am having some trouble parsing some XML from centovacast v3 XML API. I've worked with their 2.x API and parsed it, but the responses have totally changed and I cannot seem to make any of my existing parsers work. Every example I've tried I cannot seem to get at the data correctly.
I'm using .NET 3.5 (4.0 is acceptable as well), any examples would be greatly appreciated.
An example XML document is:
<?xml version=""1.0"" encoding=""UTF-8""?>
<centovacast version=""3.0.0"" host=""0.0.0.0:2199"">
<response type=""success"">
<message>OK</message>
<data>
<row>
<id>1</id>
<parameters>
<ipaddress>127.0.0.1</ipaddress>
<port>2198</port>
<title>Local server</title>
<isrelay>1</isrelay>
<ismaster>1</ismaster>
<defaultip>0.0.0.0</defaultip>
<daemontype>RPC</daemontype>
<hostname/>
</parameters>
<status>
<memfree>101879808</memfree>
<memtotal>1073741824</memtotal>
<memavail>778653696</memavail>
<swapfree>1077501952</swapfree>
<swaptotal>1077501952</swaptotal>
<buffers>172535808</buffers>
<cpuload>0.00</cpuload>
<uptime>13372713</uptime>
<machine>Intel(R) Xeon(R) CPU E5620</machine>
<osbrief>Linux</osbrief>
<osdetails>2.6.18</osdetails>
<other>
<Processes>
<field>n</field>
<field>72</field>
</Processes>
<Kernel>
<field>s</field>
<field>Linux version 2.6.18</field>
</Kernel>
<row>
<field>f</field>
<field>0.000000</field>
</row>
<row>
<field>f</field>
<field>0.000000</field>
</row>
<row>
<field>f</field>
<field>0.000000</field>
</row>
</other>
<online>1</online>
</status>
<accounts>
<licensed>-1</licensed>
<active>1</active>
<inactive>0</inactive>
</accounts>
</row>
</data>
</response>
</centovacast>
I've tried using the following code:
var xml = XDocument.Parse(xmldata);
var query = from p in xml.Descendants("status")
select p;
foreach (var record in query)
MessageBox.Show(record.Value);
but it returns all the data inside the <status> and <parameters> in one big jumble, rather then in separate values.
I would love to serialize / deserialize, as the XML call I'm making returns the above for each server in the cluster, so it could be quite a large result set, but I am not picky, I would be happy just being able to get the data into the correct variables so I can use them.

Here's an example storing some of the elements in an anonymous type:
var data =
XDocument.Parse(xml)
.Root
.Element("response")
.Element("data")
.Elements("row")
.Select(row =>
new
{
Id = Int32.Parse(row.Element("id").Value),
Parameters = new
{
IpAddress = row.Element("parameters").Element("ipaddress").Value,
port = Int32.Parse(row.Element("parameters").Element("port").Value),
},
Status = new
{
MemFree = Int32.Parse(row.Element("status").Element("memfree").Value),
},
});
You can always plug in your own concrete types and null checks where option values might be.

xml.Descendants("status") returns the whole element status together with it's child elements. If you want to enumerate it's elements use the following code:
xml.Descendants("status").Descendants();

Related

C# Parse XML Response to Array

I'm working with a third party system that returns the following xml response
{<origenxml type="data">
<data>
<item>
<id><![CDATA[PIN/4590/67]]></id>
<filename><![CDATA[CS.STAR]]></filename>
<group>
<id>MAIN</id>
<dictionary id="CS.ST.BOXNO">
<desc><![CDATA[boxes]]></desc>
<value ln="0"></value>
<raw-value ln="0"></raw-value>
<value ln="1"><![CDATA[121880 ]]></value>
<raw-value ln="1"><![CDATA[B-FILE394**BCBF*BC*121880*]]></raw-value>
<value ln="2"><![CDATA[121881 ]]></value>
<raw-value ln="2"><![CDATA[B-FILE394**BCBF*BC*121881*]]></raw-value>
<value ln="3"><![CDATA[121882 ]]></value>
<raw-value ln="3"><![CDATA[B-FILE394**BCBF*BC*121882*]]></raw-value>
<value ln="4"><![CDATA[940288 ]]></value>
<raw-value ln="4"><![CDATA[B-FILE80**BCBF*BC*940288*]]></raw-value>
<value ln="5"><![CDATA[170415 ]]></value>
<raw-value ln="5"><![CDATA[ALPHA**BC*BC*170415*]]></raw-value>
</raw-value>
</dictionary>
</group>
</item>
</data>
</origenxml>}
Each line under Boxes, represents an object where the value is the Id and the raw-value is the data ( so line 5 - ID = 170415 and the Value = ALPHA**BC*BC*170415*) but i really can't figure out the best way to parse the xml. I have no control over the response xml, so i can't anything helpful like extra node names
First thing, I want to make sure this is really the XML, because it's malformed. There's a floating end tag for the </raw-value> right before the </dictionary>. Assuming that is a mistake then the solution is easy with Linq over XML.
You'll need to add the following using statements:
using System.IO;
using System.Xml.Linq;
I created this sample in a Console application, but you should be able to easily adapt it. The first step is to create an XDocument object from your XML. I'm pulling the text from a constant string value.
static XDocument CreateDocument()
{
using (var reader = new StringReader(testData)) {
return XDocument.Load(reader);
}
}
The rest is simply to create a query and enumerate it.
var doc = CreateDocument();
var results = from value in doc.Descendants("value")
join rawValue in doc.Descendants("raw-value")
on value.Attribute("ln").Value equals rawValue.Attribute("ln").Value
select new { Value = value.Attribute("ln").Value, RawValue = rawValue.Value };
foreach (var result in results) {
Console.WriteLine($"{result.Value} => {result.RawValue}");
}

Delete XML node that contains a certain value

Here is the xml structure.
I am trying to delete each Status node where State contains the word failed.
What is the best way to remove these?
<Stats>
<Status>
<Desc>something here</Desc>
<State>pending - ok</State>
</Status>
<Status>
<Desc>something here</Desc>
<State>failed</State>
</Status>
</Stats>
void Main()
{
XDocument xml = XDocument.Parse(#"<Stats>
<Status>
<Desc>something here</Desc>
<State>pending - ok</State>
</Status>
<Status>
<Desc>something here</Desc>
<State>failed</State>
</Status>
</Stats>");
xml.Descendants("State").Where (x => x.Value.Contains("fail")).Ancestors("Status").Remove();
Console.WriteLine(xml.ToString());
}
Parse will load the xml in-memory, Load is used for loading it from a stream or via I/O means.
#Gregory Pilar's answer heavily influenced this answer; I believe he wrote that from memory, the snippet I provided was testing via LinqPad and returns expected results.
You can use Linq to XMl, to do the job
var xdoc = XDocument.Load(path_to_xml);
xdoc.Descendants("Status")
.Where(os => (int)os.Attribute("State") == "failed")
.Remove();
xdoc.Save(path_to_xml);
xDoc.Descendants("Status").Where(status => status.Element("State").Value.ToLower().Contains("fail")).Remove();

How can I get the Status element from the following XML?

I'm getting the following block of xml back from a web service:
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfItemResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ItemResponse>
<Item xmlns="http://www.xyz.com/ns/2006/05/01/webservices/abc/def">
<RequestKey Name="ESM.PA" Service="" />
<QoS>
<TimelinessInfo Timeliness="REALTIME" TimeInfo="0" />
<RateInfo Rate="TIME_CONFLATED" TimeInfo="10" />
</QoS>
<Status>
<StatusMsg>OK</StatusMsg>
<StatusCode>0</StatusCode>
</Status>
<Fields>
<Field DataType="Utf8String" Name="DSPLY_NAME">
<Utf8String>D15 |ASDFDSAA ETF </Utf8String>
</Field>
</Fields>
</Item>
</ItemResponse>
</ArrayOfItemResponse>
I'm trying to capture the Status element in an object as follows, but it's not working.
var _xml = XDocument.Parse(xmlFromService);
var stat = _xml
.Descendants("ArrayOfItemResponse")
.Descendants("ItemResponse")
.Descendants("Item")
.Descendants("Status");
What's the best way for me to get this element?
If you want to use System.Xml.Linq, you can use this expression:
var stat = (from n in _xml.Descendants() where n.Name.LocalName == "Status" select n).ToList();
You are not able to get the required results with your existing code because of the xmlns attribute in Item
<Item xmlns="http://www.xyz.com/ns/2006/05/01/webservices/abc/def">
This question addresses the problem you are actually facing. If you don't know the namespace then you should take a look at this question.
I don't know the best way, but you can read it like this
XmlDocument xdoc = new XmlDocument();
xdoc.Load(stream);
var statMsg = xdoc.GetElementsByTagName("StatusMsg")[0].InnerText;
var statCode = xdoc.GetElementsByTagName("StatusCode")[0].InnerText;
use xpath, something like
var stat = _xml.SelectSingleNode(#"ArrayOfItemResponse/ItemResponse/ItemStatus/StatusCode").Value;
that should put the value 0 into stat.
Your xml code use Namespace.
XNamespace ns = "http://www.xyz.com/ns/2006/05/01/webservices/abc/def";
var stat = _xml.Descendants(ns + "Status");

Linq to XML: Returning attributes from Children of the same name and Parents of the same name

So, the noobie question of the day...
I have an XML file with the following structure:
<result>
<rowSet name="name">
<row name="name1" />
<row name="name2" />
<row name="name3" />
</rowSet>
<rowSet name="type">
<row type="type1" />
<row type="type2" />
<row type="type3" />
</rowSet>
etc..
</result>
I've been trying, and rather unsuccessfully, to get the row attributes from a single rowSet based off the rowSet:attribute name. In other words, I'm trying to pull the attributes from the "row"s which are only contained under a rowSet:Attribute of a specific name.
I've tried using:
var rowSet = from rs in xmlDoc.Descendants("result")
where rs.Descendants("rowset").Attributes("name").Any(a => a.Value == "name")
select new
{
row = from r in xmlDoc.Descendants("row")
select new
{
skillID = r.Attribute("name"),
}
};
However, this doesn't give more than a single result. Been rather frustrating, and yet again...after trying 15 different suggestions I have been unable to remember the original code for the reader.
An answer was given:
var rowNames = (from i in xmlDoc.Descendants("rowSet")
where (string)i.Attribute("name") == "name"
select i.Descendants("row")) // Had to move this last ) to...
.Select(r => (string)r.Attribute("name")); // Here in order to get it to work
I believe this worked but I'm too noobish to find out how to iterate through it, even though I can get to the data through the "Result View" of the debugger when the program is running.
Ok, so I'm able to get that query to run right (by moving a ")" first) and when I start stepping through the code, the rowNames has values in it. However, I've been very unsuccessful in displaying the information in any way. I'm playing with this in a console app, fyi.
So that's the first problem.
The second is, if I start making my XML a bit more complicated, by adding multiple attributes to the row elements, like:
<result>
<rowSet name="name">
<row fName="Ted" lName = "Happy" />
<row name="Billy" lName = "Maddison"/>
<row name="John" lName = "Doe"/>
</rowSet>
<rowSet name="type">
<row type="type1" type2 ="Hero"/>
<row type="type2" type2 ="Villain" />
<row type="type3" type2 ="Neutral"/>
</rowSet>
</result>
How then do I retrieve all attributes in an element?
This seems so basic, which is why I'm feeling quite retarded at the moment.
You can get the names alone easily enough:
var rowNames = from i in xmlDoc.Descendants("rowSet")
where (string)i.Attribute("name") == "name"
from r in i.Descendants("row")
select (string)r.Attribute("name");
Just adjust the strings to get the other. (You might throw this in a method and use parameters, for example.)
This is a bit guesswork, but do you mean:
from rs in xmlDoc.Descendants("result").Descendants("rowSet")
where (string)rs.Attribute("name") == "name"
from r in rs.Descendants("row")
select new { skillID = (string)r.Attribute("type") }

linq to xml access data based on field attribute

I have some xml like this:
<Data>
<Rows>
<Row>
<Field Name="title">Mr</Field>
<Field Name="surname">Doe</Field>
<Row>
<Rows>
<Data>
using linq how can I get the value contained in the field element where the attribute is surname
thanks
Here is how you can express your query using LINQ to XML:
XDocument doc = XDocument.Parse("<Data><Rows><Row><Field Name=\"title\">Mr</Field><Field Name=\"surname\">Doe</Field></Row></Rows></Data>");
string[] matches = (from e in doc.Descendants("Field")
where (string)e.Attribute("Name") == "surname"
select (string)e).ToArray();
Actually, you're trying to do an XML-to-Linq thing here. Linq to XML is more meant to create an XML structure from objects through Linq.
Since you have an XML file, you can use something like this:
XmlDocument xml = new XmlDocument();
xml.LoadXml(Content);
string Surname = xml.SelectSingleNode("//Field/[#Name='surname']").Value.ToString();
In other use, to get data from XML, use XPath instead.

Categories