Linq to XML : Increase performance - c#

I want to read a xml file using Linq. This xml file is composed of 1 header and N Sub-Elements like this :
<rootNode>
<header>
<company name="Dexter Corp." />
</header>
<elements>
<element>one</element>
<element>eleven</element>
<element>three</element>
<element>four</element>
<element>five</element>
<element>three</element>
<element>two</element>
<element>two</element>
</elements>
</rootNode>
I want to retrieve elements which are a value (example : two). And only if I retrieve elements, I get the header elements.
Today, I do like this :
string xmlFilePath = #"C:\numbers.xml";
XDocument doc = XDocument.Load(xmlFilePath);
var header = doc.Descendants().Elements("header");
var elements = doc.Descendants()
.Elements("elements")
.Elements("element")
.Where(el => el.Value == "two");
// I get this values even if there is no 'elements'
string companyName = header.Descendants("company").Attributes("name").Single().Value;
string serialNumber = header.Descendants("serial").Single().Value;
return elements.Select(el => new {Company = companyName, Serial = serialNumber, Value = el.Value});
Is there a better way to parse the file ? (and increase performance ?)

If performance is important to you, you shouldn't use doc.Descendants() to look for an element at some known location. It always scans the whole document, which is slow if the document is big.
Instead, do something like
doc.Root.Element("header")
or
doc.Root.Element("elements")
.Elements("element")
.Where(el => el.Value == "two")
That should be much faster.

Related

Get data from XML document using c#

I would like to get datas from a specific XML format. The XML document looks like that:
<MyXML>
<Sources>
<S1>www.example1.org</S1>
<S2>www.example2.org</S2>
</Sources>
<Books>
<Book id="1">
<Datas>
<Data name="Book_1_Name" tag="1111" />
<Data name="Book_2_Name" tag="2222" />
</Datas>
</Book >
<Book id="2">
<Datas>
<Data name="Book_1_Name" tag="3333" />
<Data name="Book_2_Name" tag="4444" />
</Datas>
</Book >
</Books>
My question is:
How can I get www.example1.org if I know S1?
How can I search "Book_1_name" from Book id=1?
I am using C# with XDocument like this:
XDocument.Load(_XML_path);
var node = _configXml.XPathSelectElement("MyXML/Books/Datas");
You can use XPath, About XPath see this : http://www.xfront.com/xpath/
var _configXml = XDocument.Load(_XML_path);
//how to get S1 element.
var s1 = _configXml.XPathSelectElement("MyXML/Sources/S1");
//how search
var search = _configXml.XPathSelectElements("//Book[#id='1']//Data[#name='Book_1_Name']");
Console.WriteLine(s1.Value);
Console.WriteLine(search.First().Attribute("tag").Value);
You should map the XML to a C# object. Then, you can use the following to get what you want:
XmlSerializer serializer = new XmlSerializer(typeof(MyXML));
var xml = (MyXML)serializer.Deserialize(new StringReader(XDocument.Load(_XML_path)));
var s1 = xml.Sources.S1;
If you want to stick with a XDocument, you can do the following
var books = doc.Element("MyXML").Element("Books");
var bookById = books.Elements("Book")
.Where(b => b.Attribute("id").Value == "1")
.Select(b => b.Element("Datas"));
In the first line, you are selecting the Books node (please note, in a real-world scenario, I'd add some checks here). In the following line you are first getting all Book sub-elements (books.Elements("Book")), checking them for the ID you are searching for (Where(b => b.Attribute("id").Value == "1")) and then select the data node from the respective book. You could re-write this to query syntax
var bookById = from book in books.Elements("Book")
let id = book.Attribute("id")
let datas = book.Element("Datas")
where id != null
&& datas != null
&& id.Value == "1"
select datas;
which is a bit longer, but easier to read.

C# Read a specific element which is in a XML Node

I searched a long time in order to get an answer but as i can see is not working.
I have an XML File and I would like to read a specific element from a node.
For example, this is the XML:
<Root>
<TV>
<ID>2</ID>
<Company>Samsung</Company>
<Series>13523dffvc</Series>
<Dimesions>108</Dimesions>
<Type>LED</Type>
<SmartTV>Yes</SmartTV>
<OS>WebOS</OS>
<Price>1993</Price>
</TV>
</Root>
I want to get the ID element in the code as a variable so i can increment it for the next item which i will add.
This is the code at this moment, but i can not find a way to select something from the item itself.
XDocument doc = XDocument.Load("C:TVList.XML");
XElement TV = doc.Root;
var lastElement = TV.Elements("TV").Last()
A query for the last TV's id (this will return 0 if there are no elements):
var lastId = (int) doc.Descendants("TV")
.Elements("ID")
.LastOrDefault();
You might also want the highest id (in case they're not in order):
var maxId = doc.Descendants("TV")
.Select(x => (int)x.Element("ID"))
.DefaultIfEmpty(0)
.Max();
See this fiddle for a working demo.
Use like this to get id value
XDocument doc = XDocument.Load(#"C:\TVList.XML");
XElement root = doc.Element("Root");
XElement tv = root.Element("TV");
XElement id = tv.Element("ID");
string idvalue = id.Value;
also make your <Type>LED</Tip> tag of xml to <Type>LED</Type> for match

how to take a specific child node value in xml using C#

I have an xml schema like below
<library>
<book>
<id>1</id>
<name>abc</name>
<read>
<data>yes</data>
<num>20</num>
</read>
</book>
<book>
<id>20</id>
<name>xyz</name>
<read>
<data>yes</data>
<num>32</num>
</read>
</book>
</library>
Now if the id is 20 i need to take the value of tag <num> under <read>
I done the code as below
var xmlStr = File.ReadAllText("e_test.xml");
var str = XElement.Parse(xmlStr);
var result = str.Elements("book").Where(x => x.Element("id").Value.Equals("20")).ToList();
this give the whole <book> tag with id 20. From this how can I extract only the value of tag <num>.
ie is i need to get the value 32 in to a variable
Before you try to extract the num value, you need to fix your Where clause - at the moment you're comparing a string with an integer. The simplest fix - if you know that your XML will always have an id element which has a textual value which is an integer - is to cast the element to int.
Next, I'd use SingleOrDefault to make sure there's at most one such element, assuming that's what your XML document should have.
Then you just need to use Element twice to navigate down via read and then num, and cast the result to int again:
// Or use XDocument doc = ...; XElement book = doc.Root.Elements("book")...
XElement root = XElement.Load("e_test.xml")
XElement book = root.Elements("book")
.Where(x => (int) x.Element("id") == 20)
.SingleOrDefault();
if (book == null)
{
// No book with that ID
}
int num = (int) book.Element("read").Element("num");
If you're not dead set on using Linq, how about this XPath? XPath is probably more widely understood, and is really simple. The XPath to find your node would be:
/library/book[id=20]/read/num
Which you could use in C# thus:
var doc = new XmlDocument();
doc.LoadXml(myString);
var id = 20;
var myPath = "/library/book[id=" + id + "]/read/num";
var myNode = doc.SelectSingleNode(myPath);
Then you can do whatever you like with myNode to get its value etc..
Helpful reference:
http://www.w3schools.com/xsl/xpath_syntax.asp

How can I search through xml using linq

I want to search through my xml file. The structure looks like this:
<AForetag xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Place ID="1006">
<Foretag>
<Epost>info#abc123.se</Epost>
<Namn>Abe</Namn>
<Ort>Abc123</Ort>
<Adress>Abc123</Adress>
<Postnummer>Abc123</Postnummer>
<Landskap>Abc123</Landskap>
<Telefon>Abc123</Telefon>
<Medlemskap>Abc123</Medlemskap>
</Foretag>
<Foretag>
<Epost>def456</Epost>
<Namn>def456</Namn>
<Ort>def456</Ort>
<Adress>def456</Adress>
<Postnummer>def456</Postnummer>
<Landskap>def456</Landskap>
<Telefon>def456</Telefon>
<Medlemskap>def456</Medlemskap>
</Foretag>
</Place>
</Aforetag>
And I want to search for the Element <Landskap>. And if I get and match I should pick all the other elements, Epost, Namn, Ort, Adress, Postnummer, Landskap, Telefon and Medlemskap. The info I want to put in an array.
I have tried this:
var aforetag = from foretag in doc.Descendants("Place")
where foretag.Attribute("ID").Value == "1006"
select foretag;
var landskap = aforetag.Elements("Foretag")
.Descendants()
.Where(x => x.Element("Landskap")
.Value
.Contains(s)
.Descendants()
.Select(c => (string)c)
.ToArray();
Your code is not well formed. Copy this into VS and there are a few errors, fix one and more errors!...
And most importantly, your XML is not XML as the start and end tag don't even match! Plus, there are other issues.
Fix all these and I'm sure it will help.
var landskap = aforetag.Elements("Foretag")
.Where(e=>e.Element("Landskap").Value.Contains(s))
.Select(e=>e.Elements().Select(x=>x.Value).ToArray());
//the result is an IEnumerable<string[]> for the matched keyword s

Best way to query XDocument with LINQ?

I have an XML document that contains a series of item nodes that look like this:
<data>
<item>
<label>XYZ</label>
<description>lorem ipsum</description>
<parameter type="id">123</parameter>
<parameter type="name">Adam Savage</parameter>
<parameter type="zip">90210</parameter>
</item>
</data>
and I want to LINQ it into an anonymous type like this:
var mydata =
(from root in document.Root.Elements("item")
select new {
label = (string)root.Element("label"),
description = (string)root.Element("description"),
id = ...,
name = ...,
zip = ...
});
What's the best way to pull each parameter type according to the value of its 'type' attribute? Since there are many parameter elements you wind up with root.Elements("parameter") which is a collection. The best way I can think to do it is like this by method below but I feel like there must be a better way?
(from c in root.Descendants("parameter") where (string)c.Attribute("type") == "id"
select c.Value).SingleOrDefault()
I would use the built-in query methods in LINQ to XML instead of XPath. Your query looks fine to me, except that:
If there are multiple items, you'd need to find the descendants of that instead; or just use Element if you're looking for direct descendants of the item
You may want to pull all the values at once and convert them into a dictionary
If you're using different data types for the contents, you might want to cast the element instead of using .Value
You may want to create a method to return the matching XElement for a given type, instead of having several queries.
Personally I don't think I'd even use a query expression for this. For example:
static XElement FindParameter(XElement element, string type)
{
return element.Elements("parameter")
.SingleOrDefault(p => (string) p.Attribute("type") == type);
}
Then:
var mydata = from item in document.Root.Elements("item")
select new {
Label = (string) item.Element("label"),
Description = (string) item.Element("description"),
Id = (int) FindParameter(item, "id"),
Name = (string) FindParameter(item, "name"),
Zip = (string) FindParameter(item, "zip")
};
I suspect you'll find that's neater than any alternative using XPath, assuming I've understood what you're trying to do.
use XPATH - it is very fast ( except xmlreader - but a lot of if's)
using (var stream = new StringReader(xml))
{
XDocument xmlFile = XDocument.Load(stream);
var query = (IEnumerable)xmlFile.XPathEvaluate("/data/item/parameter[#type='id']");
foreach (var x in query.Cast<XElement>())
{
Console.WriteLine( x.Value );
}
}

Categories

Resources