Parsing XML in a foreach loop - c#

I am making a Application on Windows Phone 8. The bit I am struggling with is getting the XML parsed.
Here the XML File:
<ArrayOfThemeParkList xmlns="http://schemas.datacontract.org/2004/07/WCFServiceWebRole1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ThemeParkList>
<id>1</id>
<name>Alton Towers</name>
</ThemeParkList>
<ThemeParkList>
<id>2</id>
<name>Thorpe Park</name>
</ThemeParkList>
<ThemeParkList>
<id>3</id>
<name>Chessington World Of Adventures</name>
</ThemeParkList>
<ThemeParkList>
<id>4</id>
<name>Blackpool Pleasure beach</name>
</ThemeParkList>
</ArrayOfThemeParkList>
And the c# code that tries to parse it is:
void ThemeParksNames_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
//Now need to get that data and display it on the page
//check for errors
if (e.Error == null)
{
//No errors have been passed now need to take this file and parse it
//Its in XML format
XDocument xdox = XDocument.Parse(e.Result);
//need a list for them to be put in to
List<ThemeParksClass> ParkList = new List<ThemeParksClass>();
//Now need to get every element and add it to the list
foreach (XElement item in xdox.Root.Elements("ThemeParkList"))
{
ThemeParksClass content = new ThemeParksClass();
content.ID = Convert.ToInt32(item.Element("id").Value);
content.ThemeParkName = item.Element("name").Value.ToString();
ParkList.Add(content);
}
parkList.ItemsSource = ParkList.ToList();
}
else
{
//There an Error
}
}
Now when using Break points it get to the for each loop but does not run at all just moves on. I am guessing i have the for each loop set wrong.
Many Thanks.

Your ThemeParkList elements are in a namespace http://schemas.datacontract.org/2004/07/WCFServiceWebRole1 - you'll need to adjust accordingly:
XNamespace ns = "http://schemas.datacontract.org/2004/07/WCFServiceWebRole1";
foreach (XElement item in xdox.Descendants(ns + "ThemeParkList"))
You'll need to handle the other elements in the same way.

Related

How to get id and state attibutes in LINQ to XML

I'm trying to learn C# (and Linq-to-Xml) for handling of XML files, yet having some troubles.
I can get the elements and values, but my output is missing the information I need for logic decisions (id and state attributes in the transaction and target elements).
I think it has to do with the Descendants, but not sure how to grab them.
A little nudge in the right direction?
My XML File
<?xml version="1.0"?>
<xliff version="1.2">
<file source-language="en-US" datatype="plaintext" category="framework">
<body>
<transaction approved="no" id="1">
<source xml:lang="en-US">Product Family</source>
<target state="translated" xml:lang="en-US">Product Family</target>
</transaction>
</body>
</file>
</xliff>
C# Code
private void btnOpen_Click(object sender, EventArgs e)
{
// Show the dialog, using defaults, and get result
OpenFileDialog ofdResult = new OpenFileDialog();
if (ofdResult.ShowDialog() == DialogResult.OK)
{
try
{
if (ofdResult.OpenFile() != null)
{
XDocument xmlFile = XDocument.Load(ofdResult.FileName);
// print elements recursively
PrintElement(xmlFile.Root);
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk." + ex.Message);
}
}
}
// display an element (and its children, if any) in the TextBox
private void PrintElement( XElement element )
{
// get element name without namespace
string name = element.Name.LocalName;
// display the element's name within its tag
tbxOutput.AppendText( '<' + name + ">\n" );
// check for child elements and print value if none contained
if ( element.HasElements )
{
// print all child elements at the next indentation level
foreach ( var child in element.Elements() )
// Display all attributes
PrintElement(child);
} // end if
else
{
// display the text inside this element
tbxOutput.AppendText( element.Value.Trim() + '\n' );
} // end else
// display end tag
tbxOutput.AppendText( "</" + name + ">\n" );
} // end method PrintElement
My Output...
<xliff>
<file>
<body>
<trans-unit>
<source>
Product Family
</source>
<target>
Product Family
</target>
</trans-unit>
</body>
</file>
</xliff>
If you need attributes why don't you read attributes? XElement has a boolean property HasAttributes and you can loop over all attributes in the same way like you do it for Elements(). Use Attributes() method for that. Please refer to the MSDN article 1
You can write a method like
string GetAttributesString(XElement element)
{
if(element == null || !element.HasAttributes)
return string.Empty;
string format = " {0}={1}";
StringBuider result = new StringBuilder();
foreach(var attribute in element.Attributes())
{
result.AppendFormat(format, attribute.Name, attribute.Value);
}
return result.ToString();
}
And use it in your PrintElement method
tbxOutput.AppendText("<" + name + GetAttributesString(element) + ">\n");

Selecting random nodes from xml file

My application allows the user to speak into a microphone and the program will talk back to the user. I have responses saved in a XML file and when the user talks, I want the program to check in the XML file and display a random node in the section it should. The following is what my XML file looks like:
<?xml version="1.0" encoding="utf-8" ?>
<MarcusXMLFile xmlns:Responses="http://www.rewardstrike.com/XMLFile1.xml"">
<response>
<posanswer>
<answer>Yes, sir.</answer>
<answer>Right away.</answer>
</posanswer>
</response>
</MarcusXMLFile>
My code for selecting a random node is:
private void Responses()
{
string query = String.Format("http://www.rewardstrike.com/XMLFile1.xml");
XmlDocument Responses = new XmlDocument();
Responses.Load(query);
XmlNode channel = Responses.SelectSingleNode("MarcusXMLFile");
XmlNodeList nodes = Responses.SelectNodes("MarcusXMLFile/response");
try
{
XmlNodeList positiveresponses = Responses.SelectNodes("./posanswer/answer");
foreach (XmlNode ans in positiveresponses.Cast<XmlNode>().OrderBy(elem => Guid.NewGuid()))
{
response = ans.InnerText;
}
QEvent = "positiveresponse";
}
catch { }
}
And my code for calling the event is:
case "Hello":
case "Hello Jarvis":
Responses();
if (QEvent == "positiveresponse")
{
JMARCUS.Speak(response);
}
break;
However, when I speak to the application, it gives me an error saying the text he is supposed to speak in null. Any ideas?
Your second Responses.SelectNodes finds no nodes, so positiveresponses has no nodes, the foreach loop has nothing to iterate on, and response is never assigned a value.
Try changing:
XmlNodeList positiveresponses = Responses.SelectNodes("./posanswer/answer");
To:
XmlNodeList positiveresponses
= Responses.SelectNodes("MarcusXMLFile/response/posanswer/answer");

How to parse XML in a Windows Phone 7 application

Could someone tell me how to parse a XML-String that i receive from a wcf-rest service?
my webserive XML-String looks like
<WS>
<Info>
<Name>Beta</Name>
<Description>Prototyps</Description>
</Info>
<Pages>
<Page>
<Name>Custom</Name>
<Description>toDo</Description>
</Page>
...many other pages...
</Pages>
</WS>
an my phone sourcecode:
public void DownloadCompleted(Object sender, DownloadStringCompletedEventArgs e)
{
if (!e.Cancelled && e.Error == null)
{
var answer = XElement.Parse(e.Result).Descendants("WS"); // null
...
}
}
if i try to parse it through XDocument.Load(e.Result) then i get the exception: File not Found.
i just want the "unique" information of the Info-Node and a list of all Page-Nodes with their values
Update
Even if i try to load the Root-Element via var item = xdoc.Root.Descendants(); item will be assigned to the whole xml-file.
Update 2 it seems the problem occurs with the namespaces in the root-element. with namespaces xdocument will parse the webservice output not correctly. if i delete the namespaces it works fine. could someone explain me this issue? and is there a handy solution for deleting all namespaces?
update 3 A Handy way for removing namespaces1
With really simple XML if you know the format wont change, you might be interested in using XPath:
var xdoc = XDocument.Parse(e.Result);
var name = xdoc.XPathSelectElement("/WS/Info/Name");
but for the multiple pages, maybe some linq to xml
var xdoc = XDocument.Parse(xml);
var pages = xdoc.Descendants("Pages").Single();
var PagesList = pages.Elements().Select(x => new Page((string)x.Element("Name"), (string)x.Element("Description"))).ToList();
Where Page is a simple class:
public class Page
{
public string Name { get; set; }
public string Descrip { get; set; }
public Page(string name, string descrip)
{
Name = name;
Descrip = descrip;
}
}
Let me know if you need more explanation.
Also to select the Info without XPath:
var info = xdoc.Descendants("Info").Single();
var InfoName = info.Element("Name").Value;
var InfoDescrip = info.Element("Description").Value;
Viktor - XDocument.Load(string) attempts to load an XDocument by the supplied filename, not a string representation of an XML element.
You say var answer = XElement.Parse(e.Result).Descendants("WS"); // null, but which part is null? The parsed XElement or the attempt to grab a descendant? If <WS>...</WS> is your root element, would the .Descendents("WS") call return the root element? Based on the documentation for XElement.DescendantsAndSelf(), I'm guessing not. Have you instead tried calling:
var answer = XElement.Parse(e.Result).Descendants("Info");
A quick test on my end showed that, with WS as the root element, calling XElement.Parse(e.Result).Descendants("WS"); yielded no results, while XElement.Parse(e.Result).Descendants("Info"); yielded the <Info>...</Info> element.

c# - Want to perform an action if a user enters a node that already exists in a xml file

Bassicly im creating a schedule where the user can input his on time such as 19/4/2012, and it gets saved into an xml file such as 19/4/2012. Im trying to perform an action that if the user enters information that has already been entered into the xml file then display a error. Im still unsure how to do such a task so any help would be appreciated thanx.
Example of xml:
<Schedule>
<Date>19/4/2012</Date>
</Schedule>
Code Example:
private void button1_Click(object sender, EventArgs e)
{
XmlDocument doc = new XmlDocument();
doc.Load("xmldoc.xml");
XmlNode schedule = doc.CreateElement("Schedule");
XmlNode date = doc.CreateElement("Date");
date.InnerText = monthCalendar1.SelectionStart.ToString();
schedule.AppendChild(date);
doc.DocumentElement.AppendChild(schedule);
doc.Save("xmldoc.xml");
if(date.InnerText == monthCalander1.SelectionStart.ToString())
{
label6.Text = "Incorrect";
}
}
}
Well you need to check if the date exists before you add a new schedule. You can probably do something like :
if (doc.SelectSingleNode("/Schedule/Date[text()='" + monthCalander1.SelectionStart.ToString() + "']") != null){
// already exists, do something here
}

Saving Particular Node in XML File

I am populating textboxes with information from a particular node based on it's ConfirmNum. Then when all info is updated, I am saving information (by a submit button) back to the particular node.
However, upon saving, every node in the XML file that was empty drops down to another line.
Example XML Before Save:
<OnlineBanking>
<Transactions>
<Txn>
<Login></Login>
<UserName>userName</UserName>
<CustomerName>CustomerName</CustomerName>
<ConfirmNum>1234</ConfirmNum>
</Txn>
</Transactions>
</OnlineBanking>
My code (below) will save the information for that node, based on the Page.aspx?CID=1234 number. However, every node in the entire XML file that was blank, will now have a line break in it. Not just the Txn we just edited, but all.
Here is my code:
protected void btnSubmit_Click(object sender, EventArgs e)
{
XmlDocument item = new XmlDocument();
item.Load(xmlFileName);
if (CID != "")
{
XmlNode xlist = item.SelectSingleNode("OnlineBanking/Transactions/Txn[ConfirmNum=" + CID + "]");
if (xlist != null)
{
xlist.ChildNodes.Item(0).InnerText = tbLogin.Text;
xlist.ChildNodes.Item(1).InnerText = tbUserName.Text;
xlist.ChildNodes.Item(2).InnerText = tbCustomerName.Text;
item.Save(xmlFileName);
}
}
}
Example XML After Save:
<OnlineBanking>
<Transactions>
<Txn>
<Login>
</Login>
<UserName>userName</UserName>
<CustomerName>CustomerName</CustomerName>
<ConfirmNum>1234</ConfirmNum>
</Txn>
</Transactions>
</OnlineBanking>
Note how the <login> is on another line than </login>. This is what I am talking about. Hope someone can see clearly what I am not doing.
Try setting the PreserveWhitespace property to True and see if it will stop inserting line breaks upon calling Save:
XmlDocument item = new XmlDocument();
item.PreserveWhitespace = true;
item.Load(xmlFileName);
One compromise is:
if(string.IsNullOrWhiteSpace(tbLogin.Text))
xlist.ChildNodes.Item(0).IsEmpty = true;
else
xlist.ChildNodes.Item(0).InnerText = tbLogin.Text;
This will give you:
<Login />

Categories