Linq to XML retrieve single node - c#

I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<game>
<name>Space Blaster</name>
<description></description>
<version>1</version>
<fullscreen>false</fullscreen>
<width>640</width>
<height>640</height>
<c2release>6900</c2release>
</game>
It's guaranteed to only have 1 game record in it. I'd like to retrieve each property value, this is what I've tried:
string ArcadeXMLLocation = Settings.GamePergatoryLocation + UserID + "/unzipped/arcade.xml";
XDocument loaded = XDocument.Load(ArcadeXMLLocation);
var q = (from c in loaded.Descendants("game") select (string)c.Element("name")).SingleOrDefault();
Response.Write(q.name);
But I can't seem to take any values like this (intellisense hates it!) Can someone show me how it's done?

I think you just need to get the value of the element:
string val = doc.Descendants("game")
.Select(x => x.Element("name").Value).FirstOrDefault();
As a prerequisite to the above, and so intellisense picks it up, make sure that you import the System.Linq and System.Xml.Linq namespaces.

This will get you all the descendants with the tag "game"
You just need the FirstOrDefault() to get the only record if it exists.
var q = from c in loaded.Descendants("game")
select new { Name = c.Element("name").Value
Description = c.Element("description").Value
};
q.FirstOrDefault();

You query is actually correct (tested and worked for me) for extracting the value of the name node - the (string) cast that you are doing will extract the value of the name node as string and not give you the node object itself, this is one of the shortcuts built into Linq to Xml. All that is left is to print out the name:
Response.Write(q);

var q = (from c in loaded.Descendants("game") select (string)c.Element("name")).SingleOrDefault();
Console.WriteLine(q);
is enough. or to avoid the cast
var q = (from c in loaded.Descendants("game") select c.Element("name").Value).SingleOrDefault();
Console.WriteLine(q);

Related

Count ChildElements of the same name, inside an XML Element, with XDocument

I have an XML file that looks like this -
<SST_SignageCompConfig>
<Items>
<Item>
<Index>0</Index>
<Type>1</Type>
<Duration>7</Duration>
<Name>Branding-Colours-for-business.jpg</Name>
</Item>
<Item>
<Index>1</Index>
<Type>1</Type>
<Duration>7</Duration>
<Name>Flower of Life Meditation - Copy.png</Name>
</Item>
</Items>
</SST_SignageCompConfig>
I need to count how many Item Elements there are within the Items Element.
ie how many images there are.
I'm using XDocument, so my XML file is loaded like this -
string configurationPath = System.IO.Path.Combine("C:\\SST Software\\DSS\\Compilations\\" + compName + #"\\Comp.cfg");
XDocument filedoc = XDocument.Load(configurationPath);
I've tried numerous variations of the following, with all returning a null object reference exception
foreach (var item in filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Nodes())
{
string name = filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Attribute("Name").ToString();
files.Append(name + "|");
}
I've found countless examples of how to count how many different child elements are within an element, but I need to know how many instances of the same element exist.
Can anyone point me in the right direction?
You can select all names like so:
var names = from item in filedoc.Descendants("Item")
select (string)item.Element("Name");
Or without the query syntax:
var names = filedoc.Descendants("Item").Elements("Name").Select(e => e.Value);
You can get only unique names by:
var uniqueNames = names.Distinct();
You're on the right track. Try finding out exactly which invocation is giving you the NullReferenceException. My guess is that it's the attempt to find:
.Element("SST_SignageCompConfig")
Which is your root. Try the following instead:
// note the difference between .Element and .Elements
var count = filedoc.Root.Element("Items").Elements("Item").Count();
You could also use XPath to help you nail down the navigation within your XDocument:
// returns the current top level element
var element = filedoc.Root.XPathSelectElement(".");
// If the returned element is "SST_SignageCompConfig", then:
var nextElement = filedoc.Root.XPathSelectElement("./Items")
// If the "." element is *not* "SST_SignageCompConfig", then try and locate where in your XML document that node is.
// You can navigate up with .Parent and down with .Element(s)
And so on.
How about:
var nav = fileDoc.CreateNavigator();
XPathNodeIterator navShape = nav.Select("/SST_SignageCompConfig/Items");
navShape.MoveNext()
var count = navShape.Count;
If your xml has only one Items element, this should do the trick:
filedoc.Descendants("Item")
.GroupBy(e => e.Element("Name")!=null? e.Element("Name").Value:String.Empty)
.Select(g => new
{
Name = g.Key,
Count = g.Count()
});
Because "Name" is an element and not an attribute of your xml structure.
can you try replacing this?
string name = filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Element("Name").ToString();

Check against multiple elements with one query?

Is it possible to check multiple elements inside of a root using where? I have a XML sheet setup in such a way that there are multiple elements with the same name (but only sometimes), like so:
<person>
<name>Joe</name>
<food>orange</food>
<food>apple</food>
</person>
<person>
<name>Roger</name>
<food>apple</food>
</person>
I want to be able to check whether or not a person has a specific type of food and then output them into console. Using this method to grab them from the XML sheet:
var query = from c in xml.Root.Descendants("person")
where (string)c.Element("food") == "apple"
select new c.Element("food").Value;
It will only add Roger to the query. I believe this is because apple is second, since when I switch it to being first on the list I get Joe to show up. Is there a way to check to see if the second element meets the where statement as well?
You can try this out.It will return a projection with the name and food just to prove it has picked both:
var query = from c in xml.Root.Elements("person") //Descendants("person")
from f in c.Descendants("food")
where (string)f == "apple"
select new { Food = f.Value,Name = c.Element("name").Value };
Output of query:
{Food = "apple", Name = "joe"}
{Food = "apple", Name = "Roger"}
Your code won't add "Roger", but "apple" to the resulting IEnumerable. I assume you want to select the name element of your person.
As an alternative to the previous answer, you could also search for all apples and then get the name element of their parents.
var result = xml.Root.Descendants("food")
.Where(x=> (string)x == "apple")
.Select(y=> (string)y.Parent.Element("name"));
should do the trick
Using Lambda (short cutting since apple is hard coded):
var doc = XDocument.Parse(#"<root><person><name>Joe</name><food>orange</food><food>apple</food></person><person><name>Roger</name><food>apple</food></person></root>");
var results = doc.Root.Descendants("person")
.Where(p => p.Elements("food").Any(f => f.Value == "apple"))
.Select(p => "apple");
DotNetFiddle Example

How to import/read data from an XML file?

How to access an XML file in C#?
How to count the number of nodes in that xml file?
How am i supposed to access each and every node in that xml file?
I have two xml files, one of them is dev.xml which has this code
<Devanagri_to_itrans>
<mapping>
<character>अ</character>
<itrans>a</itrans>
</mapping>
...
</Devanagri_to_itrans>
the second file is guj.xml (with a very similar structure)
<Gujrathi_to_itrans>
<mapping>
<character>અ</character>
<itrans>a</itrans>
<mapping>
...
</Gujrathi_to_itrans>
I need to turn this into a two-dimension arraying of the character mappings.
Try this tutorial on Linq to XML - http://www.switchonthecode.com/tutorials/introduction-to-linq-simple-xml-parsing
This question - How to iterate through an XDocument, getting complete XML structure, object by object? - also provides some interesting code
If you're using .net 3.5 or later use LINQ to XML by setting a reference to System.Xml.Linq.
Here's a simple count of elements in a given xml file to a console app's window:
string xml = #"<xml><a/><a/><a/></xml>";
XDocument doc = XDocument.Parse(xml);
Console.WriteLine((from a in doc.Descendants("a")
select a).Count());
Since you've added more details I can now provide a better answer.
Here is a functional xml parsing and joining console app that demonstrates what it is you're looking for (I think). To parse xml files rather than xml strings use the XDocument Load method rather than the displayed Parse method. Good luck,
XDocument docA = XDocument.Parse(
#"<Devanagri_to_itrans>
<mapping>
<character>अ</character>
<itrans>a</itrans>
</mapping>
</Devanagri_to_itrans>");
XDocument docB = XDocument.Parse(
#"<Gujrathi_to_itrans>
<mapping>
<character>અ</character>
<itrans>a</itrans>
</mapping>
</Gujrathi_to_itrans>");
var devanagriKeys = (from d in docA.Descendants("mapping")
select new {
Key = d.Descendants("itrans").FirstOrDefault().Value,
Character = d.Descendants("character").FirstOrDefault().Value
}).ToArray();
var gujrathiKeys = (from g in docB.Descendants("mapping")
select new {
Key = g.Descendants("itrans").FirstOrDefault().Value,
Character = g.Descendants("character").FirstOrDefault().Value
}).ToArray();
var crossReference = (from d in devanagriKeys
join g in gujrathiKeys on d.Key equals g.Key
select new {
d.Key,
Devanagri = d.Character,
Gujrathi = g.Character
}).ToList();
Console.WriteLine("Enter a key character to translate:");
string searchKey = Console.ReadLine();
var translation = crossReference.Where(cr => cr.Key == searchKey).FirstOrDefault();
if (translation == null)
Console.WriteLine("No such key in the cross reference.");
else
Console.WriteLine("{0} is {1} in Devanagri and {2} in Gujrathi",
translation.Key, translation.Devanagri, translation.Gujrathi);
Console.ReadKey(true);
PER REQUEST FOR SESSION VARIABLE:
Anonymous types are only intended for use within a method. To place a list into a Session variable for use elsewhere create a real class of your own that contains the 3 desired properties and change the line of code above very matching this to the below. (The class name I chose was CrossReferenceTranslation.)
Session["CrossReference"] = (from d in devanagriKeys
join g in gujrathiKeys on d.Key equals g.Key
select new CrossReferenceTranslation() {
d.Key,
Devanagri = d.Character,
Gujrathi = g.Character
}).ToList();
...then, at some other point in time you can do this to get your session object list into a variable. Note the assumption that the variable could be null, which would happen whenever a session has timed out...
List<CrossReferenceTranslation>() crossReference = Session["CrossReference"] ??
new List<CrossReferenceTranslation>();

Noob LINQ - reading, filtering XML with XDocument

I'm just learning XDocument and LINQ queries. Here's some simple XML (which doesn't look formatted exactly right in this forum in my browser, but you get the idea . . .)
<?xml version="1.0" encoding="utf-8"?>
<quiz
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.com/name XMLFile2.xsd"
title="MyQuiz1">
<q_a>
<q_a_num>1</q_a_num>
<q_>Here is question 1</q_>
<_a>Here is the answer to 1</_a>
</q_a>
<q_a>
<q_a_num>2</q_a_num>
<q_>Here is question 2</q_>
<_a>Here is the answer to 2</_a>
</q_a>
</quiz>
I can iterate across all elements in my XML file and display their Name, Value, and NodeType in a ListBox like this, no problem:
XDocument doc = XDocument.Load(sPath);
IEnumerable<XElement> elems = doc.Descendants();
IEnumerable<XElement> elem_list = from elem in elems
select elem;
foreach (XElement element in elem_list)
{
String str0 = "Name = " + element.Name.ToString() +
", Value = " + element.Value.ToString() +
", Nodetype = " + element.NodeType.ToString();
System.Windows.Controls.Label strLabel = new System.Windows.Controls.Label();
strLabel.Content = str0;
listBox1.Items.Add(strLabel);
}
...but now I want to add a "where" clause to my query so that I only select elements with a certain name (e.g., "qa") but my element list comes up empty. I tried . . .
IEnumerable<XElement> elem_list = from elem in elems
where elem.Name.ToString() == "qa"
select elem;
Could someone please explain what I'm doing wrong? (and in general are there some good tips for debugging Queries?) Thanks in advance!
The problem is that the Name property is not a string, it's an XName. When you ToString it, you get a lot more than you think.
While it's possible to write the query in the way you're attempting to, also consider these possibilites:
//from nodes immediately below this one
IEnumerable<XElement> elem_list = doc.Elements("qa");
//from nodes of all levels below this node.
IEnumerable<XElement> elem_list = doc.Descendants("qa");
I would perhaps change your query to something that looks more like this
var query = from q_a in document.Descendants("q_a")
select new
{
Number = (int)q_a.Element("q_a_num"),
Question = (string)q_a.Element("q_"),
Answer = (string)q_a.Element("_a")
};
With this, you'll pull from each of your q_a descendants the inner elements into an IEnumerable<[Anonymous Type]>, each object containing the number, question, and answer.
However, if you just want to extract the XElements where the name is q_a, you could do this using a where clause.
IEnumerable<XElement> elem_list = elems.Where(elem => elem.Name.LocalName == "q_a");
Of course, as David B showed, the where clause is not necessary here.
IEnumerable<XElement> elem_list = elems.Elements("q_a");

Why take just one? Linq to XML C#

I can't figure out why my code just taking the first tag and not the rest.
var xml = XDocument.Load(HttpContext.Current.Server.MapPath("~/App_Data/Themes.xml"));
var q = from f in xml.Descendants("themes")
select new ThemesItem
{
Name = f.Element("theme").Element("name").Value,
Description = f.Element("theme").Element("description").Value,
Author = f.Element("theme").Element("author").Value,
};
return q.ToList();
ThemeItem is just a get set with public string
When i write out this data i use a repeater
Thanks for help :)
That is because the Descendants extension method takes all decendants of the xml node, that is named "themes". Since your themes node is the container for the individual theme tags, there is only one, and when you take .Element on that, you get the first occurence.
This code should work:
var q = from f in xml.Descendants("theme")
select new ThemesItem
{
Name = f.Element("name").Value,
Description = f.Element("description").Value,
Author = f.Element("author").Value,
};
<themes>
<theme>
<name>Standard</name>
<description>standard theme</description>
<author>User 1</author>
<folder>standard</folder>
</theme>
<theme>
<name>Standard</name>
<description>standard theme</description>
<author>User 2</author>
<folder>standard</folder>
</theme>
</themes>
Try using XElement.Load() instead of XDocument.Load()
http://msdn.microsoft.com/en-us/library/bb675196.aspx

Categories