Linq To XML - Using XDocument and creating list of objects - c#

I have to read an XML document and insert the values into a List<T> of my objects.
Class (Result)
+Result
-username
-dob
-answer1
-answer2
-uuid
Below is the XML format structure
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<export exportDate="2012-11-07T12:03:52.823+11:00">
<survey type="USER" completion="2012-11-07T11:46:52.754+11:00" reference="2012-11-07T11:30:34.680+11:00" year="2012" uuid="226f2aa3-46e6-46ab-8995-7d52eb21d5f4">
<user xsi:type="USER" created="2012-11-07T11:09:30.409+11:00" dob="08/06/1988" surname="Billy" name="Bob" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<subject created="2012-11-07T11:09:30.409+11:00" dob="08/06/1988" surname="Billy" name="Bob"/>
<version released="1970-01-01T10:00:02.012+10:00" version="1"/>
<result group="2" rawscore="2.4" metric="1"/>
<result group="2" rawscore="2.0" metric="2"/>
<answer score="1" question="6"/>
<answer score="2" question="7"/>
</survey>
</export>
My current progress
I was previously using XmlDocument as I have in the past but now that Im working with Linq im sure this can be done in just a few lines. I dont like the look of the code below, if anyway has some tips please help.
thankyou
List<Result> results = new List<Result>();
XmlDocument doc = new XmlDocument();
doc.Load(filename);
XmlNodeList objects = doc.GetElementsByTagName("survey");
foreach (XmlNode o in objects)
{
Result result = new Result();
if (o.Attributes["type"].Value == "USER" || o.Attributes["type"].Value == "ADMIN")
{
result.surveycompleted = o.Attributes["completion"].Value;
XmlNodeList usernodes = o.SelectNodes("user");
....
if (usernodes.Count > 0)
{}
else
{

Use LINQ2XML:
XElement doc=XElement.Load(filename);
List<Result> lstSurvey=doc.DescendantsAndSelf("Survey").Select(x=>
new Result
{
uuid=x.Element("Survey").Attribute("uuid").Value,
username=x.Element("user").Attribute("name").Value,
dob=x.Element("user").Attribute("dob").Value,
answer1=x.Elements("answer").First().Value,
answer2=x.Elements("answer").Skip(1).First().Value
}
).ToList<Result>();

Related

How can I parse the following xml in c#

I need some help on how to get values of English and studentId from this XML. Maybe it needs some special parsing I can't figure out at the moment.
<?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>
<processResponse xmlns="http://tempuri.org/">
<processResult>
<xml version="1.0" encoding="UTF-8"?>
<returnedResponse>
<English>94</English>
<Remarks>Excellent</Remarks>
<studentId>tst005</studentId>
<Department>6</Department>
</returnedResponse>
</processResult>
</processResponse>
</soap:Body>
</soap:Envelope>
I have tried the following code:
XmlDocument xdr = new XmlDocument();
xdr.LoadXml(thexml);
XmlNodeList nodelist = xdr.SelectNodes("//processResponse/processResult/returnedResponse");
foreach (XmlNode node in nodelist)
{
string eng = node.SelectSingleNode("English").InnerText;
Response.Write("eng");
}
Do you get an error with invalid XML or does your SelectNodes not find anything?
How about this path
XmlNodeList nodelist = xdr.SelectNodes("/*/*/*/processResponse/processResult/returnedResponse");
You can try with SelectSingleNode, just like this.
var English= xdr.DocumentElement.SelectSingleNode("English").InnerText;
var studentId = xdr.DocumentElement.SelectSingleNode("studentId").InnerText;
1) XML ist invalid at line 6 -> because it does not have a closing tag. If you cannot edit the Service and strip the xml header or encode the resulting xml, you have to strip the line 6 in you consumercode.
2) You have to define the Namespaces!
Btw: I highly recommend using (Linq2XML) XDocument. Your code could look like this:
XDocument xdr = XDocument.Parse(...);
var nodes = xdr.XPathSelectElements("//*[name()='returnedResponse']");
Try this
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlString);
XmlNodeList nList = doc.GetElementsByTagName("returnedResponse");
foreach (XmlNode node in nList)
{
XElement xelement = XElement.Parse(node.OuterXml);
var Descendents = xelement.Descendants();
foreach (var item in Descendents)
{
//you'll get each of your descendents here
}
}

How to get the values of all the elements in an XML into an array?

For example :
I have the following XML,
<?xml version="1.0" encoding="utf-8"?>
<TEST>
<Name>TESTRUN</Name>
<SyncByte>ff</SyncByte>
<SOM>53</SOM>
<PDADD>7e</PDADD>
<LENLSB>08</LENLSB>
</Test>
I would like to get the values from the tags "SyncByte", "SOM", "PADD" and "LENLSB" into a single array. Is there an option within XML to accomplish this?
P.S. There are close to 20+ tags in the XML and not all tags contain values all the time. Hence if there is a single command to get all the values of the XML, then it would be great.
With Linq to Xml:
var xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<Test>
<Name>TESTRUN</Name>
<SyncByte>ff</SyncByte>
<SOM>53</SOM>
<PDADD>7e</PDADD>
<LENLSB>08</LENLSB>
</Test>";
var doc = XDocument.Parse(xml);
string[] values = doc.Root.Descendants().Select(x => x.Value).ToArray();
First:
your xml formate is wrong , it should be:
<?xml version="1.0" encoding="utf-8"?>
<TEST>
<Name>TESTRUN</Name>
<SyncByte>ff</SyncByte>
<SOM>53</SOM>
<PDADD>7e</PDADD>
<LENLSB>08</LENLSB>
</TEST> <!--END Tag should same as TEST -->
Then you can query them like this:
var xml=XDocument.Load("d:\\test.xml");
var list=xml.Descendants("TEST")
.Select(x=>new
{
SyncByte=x.Element("SyncByte")==null?"":x.Element("SyncByte").Value,
SOM=x.Element("SOM")==null?"":x.Element("SOM").Value,
LENLSB=x.Element("LENLSB")==null?"":x.Element("LENLSB").Value
});
You can get the child of any parent node through 'XmlElement.ChildNodes' and store it in collection.
for eg :
static int Main(string[] args)
{
string strFilename = "Input.xml";
XmlDocument xmlDoc = new XmlDocument();
if (File.Exists(strFilename))
{
xmlDoc.Load(strFilename);
XmlElement elm = xmlDoc.DocumentElement;
XmlNodeList lstVideos = elm.ChildNodes;
for (int i = 0; i < lstVideos.Count; i++)
Console.WriteLine("{0}",lstVideos[i].InnerText );
}
else
Console.WriteLine("The file {0} could not be located",
strFilename);
Console.WriteLine();
return 0;
}

Reading XML File - reading a child node which has any number of subnodes

So I'm currentlty trying to parse an XML file which looks like so:
<employees>
<employee>
<id>1</id>
<projects>
<projectID>7</projectID>
<projectID>3</projectID>
</projects>
</employee>
<employee>
<id>2</id>
<projects>
<projectID>4</projectID>
</projects>
</employee>
</employees>
I'm trying to read in each employee and any number of projects which appear. The Employee object is a string and list(int).
Currently I have:
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlNodeList xmlNodes = doc.DocumentElement.SelectNodes("/employees/employee");
foreach (XmlNode xmlNode in xmlNodes)
{
string id;
List<int> projects = new List<int>();
id = xmlNode.SelectSingleNode("id").InnerText;
//this is the bit. What I have works but it feels like it could
//be majorly refined. Is there a better way to construct the foreach below?
foreach (XmlNode node in xmlNode.ChildNodes.Item(1))
//index 1 is the projects node
{
projects.Add(int.Parse(node.InnerText));
}
//
Employee e = new Employee(id, projects);
e.Add(e);
}
If the XML file itself is an issue it can be changed to accomodate the parsing.
Thank you.
It will be much easier with LINQ to XML:
var xDoc = XDocument.Load(path);
var employees = (from e in xDoc.Root.Elements("employee")
let projects = e.Element("projects")
.Elements("projectID")
.Select(p => (int)p)
.ToList()
let id = (string)e.Element("id")
select new Employee(id, projects)).ToList();
You need using System.Linq and using System.Xml.Linq to make it work.

Using XmlTextReader to Loop though XML attributes that have the same name

I am doing some practice code with the XmlTextReader. I have written some very basic XML as shown here:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<task name="mixed_sprite_task_test">
<sprite>
<type>animatedSprite</type>
<id>0</id>
<name>animatedSprite</name>
<fileName>iyezoz</fileName>
<startingPositionX>200</startingPositionX>
<startingPositionY>200</startingPositionY>
<sheetSizeX>12</sheetSizeX>
<sheetSizeY>35</sheetSizeY>
<startingFrameX>0</startingFrameX>
<startingFrameY>0</startingFrameY>
<startingState>standing</startingState>
<movementSpeed>15</movementSpeed>
<frameDelay>0.055</frameDelay>
</sprite>
<sprite>
<type>staticSprite</type>
<id>0</id>
<name>staticSprite</name>
<fileName>Super_Mario_63</fileName>
<startingPositionX>0</startingPositionX>
<startingPositionY>0</startingPositionY>
</sprite>
<sprite>
<type>simpleSprite</type>
<id>0</id>
<name>simpleSprite</name>
<fileName>imgres</fileName>
<startingPositionX>100</startingPositionX>
<startingPositionY>100</startingPositionY>
<movementSpeed>15</movementSpeed>
</sprite>
</task>
This file shows that I have a task. In the task I have 3 sprites.
In my code I want to loop through each sprite and collect the information.
I can get the data from the first sprite with no issue. Is there a certain way to loop through an xml with attributes of the same name?
Thank-you!
I prefer Linq2Xml.
var xDoc = XDocument.Parse(xmlstring); //or XDocument.Load(filename);
var sprites = xDoc.Descendants("sprite")
.Select(s=>s.Elements()
.ToDictionary(e=>e.Name.LocalName,e=>(string)e))
.ToList();
You can use it as
var type = sprites[0]["type"];
or can take a safe action
string delay;
if (sprites[1].TryGetValue("frameDelay", out delay))
{
Console.WriteLine(delay);
}
You can select all the nodes named "sprite"
var myXml = new XmlDocument();
myXml.Load(myDocument);
XmlNode rootElement = myXml.DocumentElement;
foreach (XmlNode item in rootElement.SelectNodes(#"/task/sprite"))
{
// do stuff with node
}

Filter XDocument more efficiently

I would like to filter with high performance XML elements from an XML document.
Take for instance this XML file with contacts:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="asistentes.xslt"?>
<contactlist evento="Cena Navidad 2010" empresa="company">
<contact type="1" id="1">
<name>Name1</name>
<email>xxxx#zzzz.es</email>
<confirmado>SI</confirmado>
</contact>
<contact type="1" id="2">
<name>Name2</name>
<email>xxxxxxxxx#zzzze.es</email>
<confirmado>Sin confirmar</confirmado>
</contact>
</contaclist>
My current code to filter from this XML document:
using System;
using System.Xml.Linq;
class Test
{
static void Main()
{
string xml = #" the xml above";
XDocument doc = XDocument.Parse(xml);
foreach (XElement element in doc.Descendants("contact")) {
Console.WriteLine(element);
var id = element.Attribute("id").Value;
var valor = element.Descendants("confirmado").ToList()[0].Value;
var email = element.Descendants("email").ToList()[0].Value;
var name = element.Descendants("name").ToList()[0].Value;
if (valor.ToString() == "SI") { }
}
}
}
What would be the best way to optimize this code to filter on <confirmado> element content?
var doc = XDocument.Parse(xml);
var query = from contact in doc.Root.Elements("contact")
let confirmado = (string)contact.Element("confirmado")
where confirmado == "SI"
select new
{
Id = (int)contact.Attribute("id"),
Name = (string)contact.Element("name"),
Email = (string)contact.Element("email"),
Valor = confirmado
};
foreach (var contact in query)
{
...
}
Points of interest:
doc.Root.Elements("contact") selects only the <contact> elements in the document root, instead of searching the whole document for <contact> elements.
The XElement.Element method returns the first child element with the given name. No need to convert the child elements to a list and take the first element.
The XElement and XAttribute classes provide a wide selection of convenient conversion operators.
You could use LINQ:
foreach (XElement element in doc.Descendants("contact").Where(c => c.Element("confirmado").Value == "SI"))

Categories