Simple Linq to Xml Queries setting Properties with Elements (instead of IEnumerable) - c#

I am trying to write a simple Linq to Xml query to extract the serverOpen and onlinePlayers values into my class which has two properties ServerStatus and Players respectively.
The xml I'm dealing with is of this simple form:
<eveapi version="2">
<currentTime>2013-02-10 05:27:33</currentTime>
<result>
<serverOpen>True</serverOpen>
<onlinePlayers>32723</onlinePlayers>
</result>
<cachedUntil>2013-02-10 05:28:39</cachedUntil>
</eveapi>
I feel like this should be a very trivial solution. There is only ever one result in the XML and I want to be able to query it out very simply.
I've tried things like (Results is my xml in a XDocument):
var result = Results.Elements().Elements().First(); //successfully gets result
ServerStatus = result.Element("serverOpen").Value; //this doesn't work
//null reference exception with .Value
Also a few other queries that all return IEnumerables and I have never been able to successfully extract the values.
Thanks.

It would be a bit easier to use xpath here instead, particularly if there isn't a result.
var serverStatus = (bool?)doc.XPathSelectElement("/eveapi/result/serverOpen");
var onlinePlayers = (int?)doc.XPathSelectElement("/eveapi/result/onlinePlayers");

Well, I was able to use an overlooked XDocument method Single() to get just the single result XElement. Then use Element() to extract the correct XElements from the result XElement.
var result = Results.Descendants("result").Single();
ServerStatus = result.Element("serverOpen").Value;
If there is a better way or another flashy way please submit your own suggestion.
Thanks.

I am not completely sure, but if I understand Linq correctly, you already have your object.
Instead of:
ServerStatus = result.Element("serverOpen").Value;
do:
ServerStatus = result.Value;
Hope this helps.

Related

Retrieve Values manually from XML InnerXML

I have been trying to deserialize the InnerXML into a class and for some reason the XML keeps changing shape and however many times I try to get the class right it seems to change shape again.
So I have given up and decided to try another method.
Is it possible to retrieve the value of a parameter within the InnerXML manually using c#?
Say for example, my XML innerXML looked like this:
<Timestamp>2014-08-22T21:45:00Z</Timestamp>
<Subscriber>https://www.dogdoza.co.uk</Subscriber>
<Order>
<OrderID>111867</OrderID>
<InvoiceNumber>DOZA-9725410</InvoiceNumber>
<CustomerID>4542</CustomerID>
Is it possible to pull out say the value of Subscriber
If this is possible I can just pull out the values I want manually. Not ideal, but there are only about 10...
I have looked around but not managed to find any code I can get working..
Can anyone please give me any guidance?
Thanks
You can do achieve what you want using LINQ to XML:
XElement myXml = XElement.Load(#"XmlLocationHere");
XElement subscriber = myXml.Descendants("Subscriber").FirstOrDefault();
XElement.Descendants returns a collection of the descendant elements for this document or element, in document order. This method will return an IEnumerable<XElement>, since there might be more than one "Subscriber" element, but in your case, we choose FirstOrDefault, which returns the first occurrence.
Try loading your XML into an XDocument. Then try to use XPathSelectElement to find the specific value you want.
It could be that you need to wrap your inner xml into a root element, because it doesn't accept multiple roots.
Pseudo example:
// set up your xml document
string xml = "<rootelement>" + myInnerXml + "</rootelement>";
XDocument doc = new XDocument();
doc.Parse(xml);
XElement subscriber = doc.XPathSelectElement("/rootelement/Subscriber");
string value = subscriber.Value;

Performance issue with Linq to object taking more time

I have an xml code which is getting updated based on the object value. The 'foreach' loop here is taking almost 12-15 minutes to fetch a 200 kb xml file. Please suggest how I can improve the performance. (the xml file consists of a four leveled tag in which the child (4th level) tags are each 10 in number)
Code:
IEnumerable<XElement> elements = xmlDoc.Descendants();
foreach (DataSource Data in DataLst)
{
XElement xmlElem = (from xmlData in elements
where Data.Name == xmlData.Name.LocalName //Name
&& Data.Store == xmlData.Element(XName.Get("Store", "")).Value
&& Data.Section == xmlData.Element(XName.Get("Section", "")).Value
select xmlData.Element(XName.Get("Val", ""))).Single();
xmlElem.ReplaceWith(new XElement(XName.Get("Val", ""), Data.Value));
}
It looks like you have an O(n)×O(m) issue here, for n = size of DataList and m = size of the xml. To make this O(n)+O(m), you should index the data; for example:
var lookup = elements.ToLookup(
x => new {
Name = x.Name.LocalName,
Store = x.Element(XName.Get("Store", "")).Value,
Section = x.Element(XName.Get("Section", "")).Value},
x => x.Element(XName.Get("Val", ""))
);
foreach (DataSource Data in DataLst)
{
XElement xmlElem = lookup[
new {Data.Name, Data.Store, Data.Section}].Single();
xmlElem.ReplaceWith(new XElement(XName.Get("Val", ""), Data.Value));
}
(untested - to show general approach only)
i think better approach would be to Deserialize XML to C# Classes and then use LINQ on that, should be fast.
"Well thanks everyone for your precious time and effort"
Problem answer: Actually the object 'DataLst' was of the type IEnumerable<> which was taking time in obtaining the values but after I changed it to the List<> type the performance improved drastically (now running in 20 seconds)
If it really takes this long to run this, then maybe do something like this:
Don't iterate both - only iterate the XML-File and load the Data from your DataLst (make a SQL-command or simple linq-statement to load the data based on Name/Store/Section), make a simple struct/class for your key with this data (Name/Store/Section) - don't forget to implement equals, and GetHashCode
iterate through your XML-Elements and use the dictionary to find the values to replace
This way you will only iterate the XML-File once not once for every data in your DataSource.
It's not clear why it's taking that long - that's a very long time. How many elements are in DataLst? I would rewrite the query for simplicity to start with though:
IEnumerable<XElement> elements = xmlDoc.Descendants();
foreach (DataSource data in DataLst)
{
XElement valElement = (from element in xmlDoc.Descendants(data.Name)
where data.Store == element.Element("Store").Value
&& data.Section == element.Element("Section").Value
select element.Element("Val")).Single();
valElement.ReplaceWith(new XElement("Val"), data.Value));
}
(I'm assuming none of your elements actually have namespaces, by the way.)
Next up: consider replacing the contents of valElement instead of replacing the element itself. Change it to:
valElement.ReplaceAll(data.Value);
Now, this has all been trying to keep to the simplicity of avoiding precomputation etc... because it sounds like it shouldn't take this long. However, you may need to build lookups as Marc and Carsten suggested.
Try by replacing Single() call in the LINQ with First().
At the risk of flaming, have you considered writing this in XQuery instead? There's a good chance that a decent XQuery processor would have a join optimiser that handles this query efficiently.

xml parsing in C#

Hi Guys
I would like to parse the following xml string in C#
i tried reading the entire string into the dataset and then using it .. there are simply no tables in the dataset.
here is the xml that I am interested to parse.
xml code is here
http://pastebin.com/VfT2wAwY
C# code is here
http://pastebin.com/iwqDK2S6
Thanks and regards,
Gagan Janjua
Have you considered LINQ to XML? If you're using .NET Framework 3.5 or later, then LINQ can save you a lot of time.
I haven't tested this, but you could do something like:
XDocument doc = XDocument.Load(#"C:\mydocument.xml");
var allCases = doc.Element("response").Element("cases").Descendants("case");
foreach (var currentCase in allCases) {
// I can now access each case specifically
var allEvents = currentCase.Descendants("events");
foreach (var currentEvent in allEvents) {
// now I can access each event
int ixBugEvent = (int)currentEvent.Element("ixBugEvent");
// etc...
}
}
Are you aware of XmlReader from System.Xml?
There is no schema in XML that you have provided, so you cannot expect that you can use it to populate DataSet... Unless you define your own schema, that is.
Your code is returning null because of your catch is making it null. It hits the catch, with the following error:
Column name 'ixBugEvent' is defined for different mapping types.
I have the impression that the reason for it is that you have ixBugEvent as both an attribute and a element
<event ixBugEvent='3' ixBug='2'>
<ixBugEvent>3</ixBugEvent>
Removing one of them fix the issue. The code is working, but your xml schema cannot be translated to a dataset.
You can change Scott's code to make it work by changing the following line of code:
// I can now access each case specifically
var allEvents = currentCase.Descendants("events");
Make it:
// I can now access each case specifically
var allEvents = currentCase.Descendants("event");
Doing that you have access to each event element. And from there you can definitely have access to ixBugEvent element.
I hope this helps.
P.s.: Sorry to make this another answer, but I would like to highlight the code, and it seems the only way to do it...

How can I grab all the child elements in XML?

From this XML response: http://www.dreamincode.net/forums/xml.php?showuser=335389
I want to grab all of the users friends. I need to grab all of the information from the friends. Usually with the help I've been given I would use the unique ID to grab each, but since each users doesn't have the same friends it has to be dynamic and I can't hard code anything.
Would anyone please share some XDocument magic?
You can get all <user> elements in the <friends> element from the XML document like this:
var url = "http://www.dreamincode.net/forums/xml.php?showuser=335389";
var doc = XDocument.Load(url);
var friends = doc.Element("ipb").Element("profile")
.Element("friends").Elements("user");
// Or if you don't want to specify the whole path and you know that
// there is only a single element named <friends>:
var friends = doc.Descendant("friends").Elements("user");
Then you can use LINQ to process the collection. For example, to create a IEnumerable<string> with the names of all frineds, you could write:
var names = from fr in friends
select fr.Element("name").Value;
If you needed unique ID, you could read the <id> element in a similar way (if it is an integer, you could also parse it using Int32.Parse for example...)
you can probably use XPath query
http://www.w3schools.com/xpath/xpath_examples.asp
the examples uses JScript but it is also supported in .NET

How can I query an xml file and save the result in another xml file

I am new with learning the possibilities of Linq to XML and I recently have found that I can query an xml like a data base (I am quite fascinated by it now).
My question is How can I query an xml file and save the result in another xml file?:
string url = "employees.xml";
XElement employees= XElement.Load(url);
if (employees.Element("employee") != null)
{
var query = from f in employees.Element("employee").Elements("item").Take(10)
select new { Name = f.Element("name").Value, Surname= f.Element("surname").Value };
foreach (var feed in query)
{
//here... I like to write the result in a different xml file, I tried the
//common
doc.save("xmlout.xml");
}
}
Thanks a lot for your help,
Well, you could do this by creating an XDocument/XElement instance and then populating it with the results of your query (by passing the query to the constructor of the XDocument/XElement), and then saving that.
However, you might want to consider using an XSLT transformation instead, as that is really what you are trying to do here.
This article should help you solve your problem. Btw if you new up as XElement and then populate you can use the save method, instead of using anonymous types.

Categories

Resources