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>();
Related
I'm parsing a lot of XML files using Linq to XML synatx, everything works when I try to access top level elements
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot")
select new
{
model = (string)element.Element("MODEL"),
}).FirstOrDefault()
The problem occurs when I need to access lower level childs of that document I tried:
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot")
select new
{
ddName = (string)element.Descendants("DD_NAME").Elements("name").First();
}).FirstOrDefault()
and
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot").Descendants("DD_NAME")
select new
{
ddName = (string)element.Element("name")
}).FirstOrDefault();
Sadly none of that works and i get same error "Sequence contains no elements". And one more thing sometimes the XML document contains those tags and sometimes not is something like this enough for handling this case?
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot").Descendants("DD_NAME")
select new
{
ddName = (string)element.Element("name") ?? "-"
}).FirstOrDefault();
Edit:
I don't think is possible to paste short version of XML that would be simple, so here's full version: http://pastebin.com/uDkP3rnR and for the code example:
XDocument prodcutDocument = XDocument.Load(this.ServerPATHData + file);
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot")
select new
{
modelis = (string)element.Element("MODELIS"),
T2918_0 = (string)element.Descendants("dd_DARBINIS_GRAFIKAS_SPEC").First()
}).FirstOrDefault();
writeTxt.WriteLine("modelis: " + indexroot.modelis);
writeTxt.WriteLine("T2979_0" + indexroot.T2918_0);
In examining the sample XML that you posted on PasteBin, it appears to me that the elements that you mention appear only once. To access them, you can simply specify a path to each as follows:
XElement indexroot = document.Root.Element("indexroot");
XElement modelis = indexroot.Element("MODELIS");
XElement dd_dgs = indexroot.Element("dd_DARBINIS_GRAFIKAS_SPEC");
XElement voltageuv = dd_dgs.Element("VoltageUV");
string t2979_0 = (string)voltageuv.Element("T2979_0");
string t2861_60 = (string)voltageuv.Element("T2861_60");
string t2757_121 = (string)voltageuv.Element("T2757_121");
(Note that you may need to check for null if there is a chance that any of the elements you are trying to access may not be present. Without doing so, you'll encounter a NullReferenceException.)
Here is a snippet of the XML that you posted to give context to the above code:
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<PDB>
<indexroot>
<ed_BENDRA_MAKS_SUV_GALIA>1.45</ed_BENDRA_MAKS_SUV_GALIA>
<ed_BENDRA_MAKS_SROVE>6.48</ed_BENDRA_MAKS_SROVE>
<TIPAS>1</TIPAS>
<MODELIS>RIS 2500 HW EC 3.0</MODELIS>
<dd_DARBINIS_GRAFIKAS_SPEC>
<VoltageUV>
<T2979_0>229,42</T2979_0>
<T2861_60>227,98</T2861_60>
<T2757_121>228,97</T2757_121>
</VoltageUV>
<CurrentIA>
<T2979_0>2,56</T2979_0>
<T2861_60>2,63</T2861_60>
<T2757_121>2,72</T2757_121>
</CurrentIA>
</dd_DARBINIS_GRAFIKAS_SPEC>
</indexroot>
</PDB>
You can just change:
element.Descendants("dd_DARBINIS_GRAFIKAS_SPEC").First()
to this:
element.Descendants("dd_DARBINIS_GRAFIKAS_SPEC").FirstOrDefault() ?? "-"
I have this XML file:
<MyXml>
<MandatoryElement1>value</MandatoryElement1>
<MandatoryElement2>value</MandatoryElement2>
<MandatoryElement3>value</MandatoryElement3>
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
<MyXml>
All 3 elements that are called 'MandatoryElementX' will always appear in the file. The elements called 'CustomElementX' are unknown. These can be added or removed freely by a user and have any name.
What I need is to fetch all the elements that are not MandatoryElements. So for the file above I would want this result:
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
I don't know what the names of the custom elements may be, only the names of the 3 MandatoryElements, so the query needs to somehow exclude these 3.
Edit:
Even though this was answered, I want to clarify the question. Here is an actual file:
<Partner>
<!--Mandatory elements-->
<Name>ALU FAT</Name>
<InterfaceName>Account Lookup</InterfaceName>
<RequestFolder>C:\Documents and Settings\user1\Desktop\Requests\ALURequests</RequestFolder>
<ResponseFolder>C:\Documents and Settings\user1\Desktop\Responses</ResponseFolder>
<ArchiveMessages>Yes</ArchiveMessages>
<ArchiveFolder>C:\Documents and Settings\user1\Desktop\Archive</ArchiveFolder>
<Priority>1</Priority>
<!--Custom elements - these can be anything-->
<Currency>EUR</Currency>
<AccountingSystem>HHGKOL</AccountingSystem>
</Partner>
The result here would be:
<Currency>EUR</Currency>
<AccountingSystem>HHGKOL</AccountingSystem>
You can define a list of mandatory names and use LINQ to XML to filter:
var mandatoryElements = new List<string>() {
"MandatoryElement1",
"MandatoryElement2",
"MandatoryElement3"
};
var result = xDoc.Root.Descendants()
.Where(x => !mandatoryElements.Contains(x.Name.LocalName));
Do you have created this xml or do you get it by another person/application?
If it's yours I would advise you not to number it. You can do something like
<MyXml>
<MandatoryElement id="1">value<\MandatoryElement>
<MandatoryElement id="2">value<\MandatoryElement>
<MandatoryElement id="3">value<\MandatoryElement>
<CustomElement id="1">value<\CustomElement>
<CustomElement id="2">value<\CustomElement>
<MyXml>
In the LINQ-Statement you don't need the List then.
Your question shows improperly formatted XML but I am assuming that is a typo and the real Xml can be loaded into the XDocument class.
Try this...
string xml = #"<MyXml>
<MandatoryElement1>value</MandatoryElement1>
<MandatoryElement2>value</MandatoryElement2>
<MandatoryElement3>value</MandatoryElement3>
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
</MyXml> ";
System.Xml.Linq.XDocument xDoc = XDocument.Parse(xml);
var result = xDoc.Root.Descendants()
.Where(x => !x.Name.LocalName.StartsWith("MandatoryElement"));
lets say TestXMLFile.xml will contain your xml,
XElement doc2 = XElement.Load(Server.MapPath("TestXMLFile.xml"));
List<XElement> _list = doc2.Elements().ToList();
List<XElement> _list2 = new List<XElement>();
foreach (XElement x in _list)
{
if (!x.Name.LocalName.StartsWith("Mandatory"))
{
_list2.Add(x);
}
}
foreach (XElement y in _list2)
{
_list.Remove(y);
}
I've got the following XML, shown in the following image:
But I can't for the life of me, get any code to select the house element between <ArrayOfHouse>.
There will be more than one House element once I've managed to get it to select one, here's my code so far:
// Parse the data as an XML document
XDocument xmlHouseResults = XDocument.Parse(houseSearchResult);
// Select the House elements
XPathNavigator houseNavigator = xmlHouseResults.CreateNavigator();
XPathNodeIterator nodeIter = houseNavigator.Select("/ArrayOfHouse/House");
// Loop through the selected nodes
while (nodeIter.MoveNext())
{
// Show the House id, as taken from the XML document
MessageBox.Show(nodeIter.Current.SelectSingleNode("house_id").ToString());
}
I'm getting the stream of XML, because I have managed to show the data in the MessageBox shown above, but I can't get to the individual houses.
You can select the House nodes like this:
var houses = XDocument.Parse(houseSearchResult).Descendants("House");
foreach(var house in houses)
{
var id = house.Element("house_id");
var location = house.Element("location");
}
Or you can use Select to directly get a strongly typed object:
var houses = XDocument.Parse(houseSearchResult)
.Descendants("House")
.Select(x => new House
{
Id = x.Element("house_id"),
Location = x.Element("location")
});
This assumes that there exists a class House with the properties Id and Location.
Also, please be sure to think about the suggestion by Thomas Levesque to use XML serialization.
With XPath you would need to use an XmlNamespaceManager, however as you have an XDocument you could simply use the LINQ to XML axis methods e.g.
XNamespace df = XmlHouseResults.Root.Name.Namespace;
foreach (XElement house in XmlHouseResults.Descendants("df" + "House"))
{
MessageBox.Show((string)house.Element("df" + "house_id"));
}
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);
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