I am a c++ developer and I started working on a C# WPF project. I have a method which should read the xml file. In my c++ application I could do it very efficiently but in WPF I am not sure how to approach the problem. Let me show you my code:
// When Browse Button is clicked this method is called
private void ExecuteScriptFileDialog()
{
var dialog = new OpenFileDialog { InitialDirectory = _defaultPath };
dialog.DefaultExt = ".xml";
dialog.Filter = "XML Files (*.xml)|*.xml";
dialog.ShowDialog();
ScriptPath = dialog.FileName; //ScriptPath contains the Path of the Xml File
if (File.Exists(ScriptPath))
{
LoadAardvarkScript(ScriptPath);
}
}
public void LoadAardvarkScript(string ScriptPath)
{
// I should read the xml file
}
I had achieved in C++ as follows:
File file = m_selectScript->getCurrentFile(); //m_selectScript is combobox name
if(file.exists())
{
LoadAardvarkScript(file);
}
void LoadAardvarkScript(File file)
{
XmlDocument xmlDoc(file);
//Get the main xml element
XmlElement *mainElement = xmlDoc.getDocumentElement();
XmlElement *childElement = NULL;
XmlElement *e = NULL;
int index = 0;
if(!mainElement )
{
//Not a valid XML file.
return ;
}
//Reading configurations...
if(mainElement->hasTagName("aardvark"))
{
forEachXmlChildElement (*mainElement, childElement)
{
//Read Board Name
if (childElement->hasTagName ("i2c_write"))
{
// Some code
}
}
}
How can I get the tagname of both mainElement and childelem as done in my c++ code? :)
not knowing the layout of your xml file here is an example that you can use if you know how to use Linq
class Program
{
static void Main(string[] args)
{
XElement main = XElement.Load(#"users.xml");
var results = main.Descendants("User")
.Descendants("Name")
.Where(e => e.Value == "John Doe")
.Select(e => e.Parent)
.Descendants("test")
.Select(e => new { date = e.Descendants("Date").FirstOrDefault().Value, points = e.Descendants("points").FirstOrDefault().Value });
foreach (var result in results)
Console.WriteLine("{0}, {1}", result.date, result.points);
Console.ReadLine();
}
}
you could also use XPATH as well to parse xml file.. but would really need to see the xml file layout
if you want to do it using xmlreader
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace XmlReading
{
class Program
{
static void Main(string[] args)
{
//Create an instance of the XmlTextReader and call Read method to read the file
XmlTextReader textReader = new XmlTextReader("C:\\myxml.xml");
textReader.Read();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(textReader);
XmlNodeList BCode = xmlDoc.GetElementsByTagName("Brandcode");
XmlNodeList BName = xmlDoc.GetElementsByTagName("Brandname");
for (int i = 0; i < BCode.Count; i++)
{
if (BCode[i].InnerText == "001")
Console.WriteLine(BName[i].InnerText);
}
Console.ReadLine();
}
}
}
Use LINQ queries to extract data from xml (XDocument)
Refer this link for further insights on XLinq.
Sample XML :
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<People>
<Person id="1">
<Name>Joe</Name>
<Age>35</Age>
<Job>Manager</Job>
</Person>
<Person id="2">
<Name>Jason</Name>
<Age>18</Age>
<Job>Software Engineer</Job>
</Person>
</People>
Sample Linq query:
var names = (from person in Xdocument.Load("People.xml").Descendants("Person")
where int.Parse(person.Element("Age").Value) < 30
select person.Element("Name").Value).ToList();
Names will be list of string. This query will return Jason as he is 18 yrs old.
Using Linq2Xml,
var xDoc = XDocument.Load("myfile.xml");
var result = xDoc.Descendants("i2c_write")
.Select(x => new
{
Addr = x.Attribute("addr").Value,
Count = x.Attribute("count").Value,
Radix = x.Attribute("radix").Value,
Value = x.Value,
Sleep = ((XElement)x.NextNode).Attribute("ms").Value
})
.ToList();
var khz = xDoc.Root.Element("i2c_bitrate").Attribute("khz").Value;
Related
I'm trying to get all the list of the different child nodes (not starting from root) of a loaded XML into a list of strings, I had done using System.Xml library but I want to write the same code with LINQ to XML too.
I had found a code that helped me a lot but it starts from Root, here is the code:
List<string> nodesNames = new List<string>();
XDocument xdoc1 = XDocument.Load(destinationPath);
XElement root = xdoc1.Document.Root;
foreach (var name in root.DescendantNodes().OfType<XElement>()
.Select(x => x.Name).Distinct())
{
if (!nodesNames.Contains(name.ToString()))
nodesNames.Add(name.ToString());
}
With this, I get the list of all child nodes + the parent node too, which I don't want to use.FirstChild or to delete manually from the list because I want a TOTALLY dynamic code and I have in input the parent node passed by the user.
For a better comprehension, this is the code that working for me but with System.Xml:
List<string> nodesNames = new List<string>();
XmlDocument doc = new XmlDocument();
doc.Load(destinationPath);
XmlNodeList elemList = doc.GetElementsByTagName(inputParentNode);
for (int i = 0; i < elemList.Count; i++)
{
XmlNodeList cnList = (elemList[i].ChildNodes);
for (int j = 0; j < cnList.Count; j++)
{
string name = cnList[j].Name;
if (!nodesNames.Contains(name))
nodesNames.Add(name);
}
}
And this is an easy sample of the XML:
<?xml version='1.0' encoding='UTF-8'?>
<parentlist>
<parent>
<firstchild>someValue</firstchild>
<secondchild>someValue</secondchild>
</parent>
<parent>
<firstchild>someValue</firstchild>
<secondchild>someValue</secondchild>
<thirdchild>someValue</thirdchild>
</parent>
</parentlist>
To resume:
in the first case i obtain nodesNames = ["parent", "firstchild", "secondchild", "thirdchild"]
in the second case i obtain nodesNames = ["firstchild", "secondchild", "thirdchild"]
I just want to fix the first to obtain the same result as the second.
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication2
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement parentlist= doc.Root;
List<string> descendents = parentlist.Descendants().Where(x => x.HasElements).Select(x => string.Join(",", x.Name.LocalName, string.Join(",", x.Elements().Select(y => y.Name.LocalName)))).ToList();
}
}
}
I can read an XML file named 101.xml in which I can read element named light having two values say 1280 and 128, I can display it. But I want to display only that value which is greater than 800. Check image for XML file.
This is what I did to read both light elements in Listbox:
READING NEW FILE:
XmlTextReader Reader = new XmlTextReader(#"101.xml");
XmlDocument doc = new XmlDocument();
doc.Load(Reader);
XPathNavigator nav = doc.CreateNavigator();
//compile xpath
XPathExpression expr;
expr = nav.Compile("/MotePacket/ParsedDataElement[Name='light']");
XPathNodeIterator iterator = nav.Select(expr);
//iterate node set and see values in list box
listBox1.Items.Clear();
try
{
while (iterator.MoveNext())
{
XPathNavigator nav2 = iterator.Current.Clone();
listBox1.Items.Add("content and value: " + nav2);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
This is very bad xml format. Recommend changing. Here is code to parse xml
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<XElement> parsedDataElements = doc.Descendants("ParsedDataElement").ToList();
for (int i = 0; i < parsedDataElements.Count; i += 2)
{
DataElement newDataElement = new DataElement();
DataElement.elements.Add(newDataElement);
newDataElement.temperature = (double)parsedDataElements[i].Element("ConvertedValue");
newDataElement.light = (int)parsedDataElements[i + 1].Element("ConvertedValue");
}
}
}
public class DataElement
{
public static List<DataElement> elements = new List<DataElement>();
public double temperature { get; set; }
public int light { get; set; }
}
}
As i mentioned in the comment to the question, you can use XDocument class together with Linq query to filter xml data.
See:
XDocument xdoc = XDocument.Load(fullfilename);
var data = xdoc.Descendants("ParsedDataElement")
.Where(x=>x.Element("Name").Value == "light" && Double.Parse(x.Element("ConvertedValue").Value)>800)
.Select(x=> new
{
Name = x.Element("Name").Value,
ConvertedValue = Double.Parse(x.Element("ConvertedValue").Value)
})
.ToList();
Now, you are able to insert data into ListBox object by using foreach loop or by setting DataSource.
For further details, please see:
Basic Queries (LINQ to XML) (C#)
How to: Bind a Windows Forms ComboBox or ListBox Control to Data
Good luck!
Aim:
- Deserialize data from an xml document and storing it as an array.
- Avoiding manually assigning the data to different strings.
- The xml document will be manually generated
public void DeserializeObject(string filename)
{
try
{
XmlSerializer deserializer = new XmlSerializer(typeof(string[]));
FileStream fs = new FileStream(filename, FileMode.Open);
string[] XmlData = (string[])deserializer.Deserialize(fs);
foreach (string p in XmlData)
{
Console.WriteLine(p);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
The XML document is as follows
<?xml version="1.0" encoding="utf-8"?>
<Mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Products>
<Product>
<software>Seiko</software>
</Product>
<Product>
<hardware>Martina</hardware>
</Product>
</Products>
</Mapping>
Thank you, found this solution
<?xml version="1.0" encoding="utf-8" ?>
<Locations>
<Location Name="Location1" IP="127.0.0.1"></Location>
<Location Name="Location2" IP="127.0.0.1"></Location>
<Location Name="Location3" IP="127.0.0.1"></Location>
<Location Name="Location4" IP="127.0.0.1"></Location>
<Location Name="Location5" IP="127.0.0.1"></Location>
</Locations>
using System.Xml.Linq;
class Program
{
static void Main(string[] args)
{
string[] strarr = GetStringArray("Locations.xml");
foreach (string str in strarr)
{
Console.WriteLine(str);
}
}
public static string[] GetStringArray(string url)
{
XDocument doc = XDocument.Load(url);
var locations = from l in doc.Descendants("Location")
select (string)l.Attribute("Name");
return locations.ToArray();
}
}
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication38
{
class Program
{
static void Main(string[] args)
{
string input =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<Mapping xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
"<Products>" +
"<Product>" +
"<software>Seiko</software>" +
"</Product>" +
"<Product>" +
"<hardware>Martina</hardware>" +
"</Product>" +
"</Products>" +
"</Mapping>";
XDocument doc = XDocument.Parse(input);
var results = doc.Descendants("Product").Select(x =>
x.Elements().Select(y => new { type = y.Name, value = (string)y }).ToList()
).SelectMany(z => z).ToList();
var groups = results.GroupBy(x => x.type).ToList();
}
}
}
You need to generate a class from your sample XML.
You can use the xsd.exe to generate a .xsd and from that create a .cs file.
The you need to add this type to your XmlSerializer
See this answer: Generate C# class from XML
XmlSerializer deserializer = new XmlSerializer(typeof(Mapping)); <- Created class type here.
If all you want to do is get the data form the XML-document as an array of strings, you can use XmlDocument to load in the data
XmlDocument doc = new XmlDocument();
doc.Load("file.xml");
Then you can find the nodes you need using an xPath expression:
XmlNodeList nodelist = doc.SelectNodes(...);
I have an XML file, and I want to retrieve the brandname when brandcode is 001 from the following XML.
<Root>
- <data>
<Companycode>TF</Companycode>
<Productcode>00001</Productcode>
<Productname>VPU</Productname>
<Brandcode>001</Brandcode>
<Brandname>DB</Brandname>
</data>
- <data>
<Companycode>TF</Companycode>
<Productcode>00002</Productcode>
<Productname>SENDERCARD</Productname>
<Brandcode>002</Brandcode>
<Brandname>LINSN</Brandname>
</data>
</Root>
This is my code; I need to assign Brand Name here:
XmlTextReader textReader = new XmlTextReader(#"codedata.xml");
textReader.Read();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(textReader);
XmlNodeList BCode = xmlDoc.GetElementsByTagName("Brandcode");
XmlNodeList BName = xmlDoc.GetElementsByTagName("Brandname");
for (int i = 0; i < BCode.Count; i++)
{
if (BCode[i].InnerText =="001")
{
string brandname = BName[i].InnerText;
}
//Console.WriteLine(BName[i].InnerText);
}
Try In this way...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace XmlReading
{
class Program
{
static void Main(string[] args)
{
//Create an instance of the XmlTextReader and call Read method to read the file
XmlTextReader textReader = new XmlTextReader("D:\\myxml.xml");
textReader.Read();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(textReader);
XmlNodeList BCode = xmlDoc.GetElementsByTagName("Brandcode");
XmlNodeList BName = xmlDoc.GetElementsByTagName("Brandname");
for (int i = 0; i < BCode.Count; i++)
{
if (BCode[i].InnerText == "001")
Console.WriteLine(BName[i].InnerText);
}
Console.ReadLine();
}
}
}
Try this XPath: //Brandname[../Brandcode[text()='001']]
Using XmlDocument.SelectSingleNode:
var document = new XmlDocument();
var Brandcode = "001";
var xpath = String.Format(#"//Brandname[../Brandcode[text()='{0}']]",
Brandcode);
var Brandname = document.SelectSingleNode(xpath).InnerText;
and using XDocument.XPathSelectElement:
var document = XDocument.Load("fileName");
var name = document.XPathSelectElement(xpath).Value;
YOu can use Xml.Linq namespace, with XDocument class to load and select the exact element from the xml file:
XDocument doc = XDocument.Load(#"filePath");
var query = doc.Descendants("Root").Where(w => w.Element("Brandcode").Value == "001").Select(s => new { s.Element("Brandname").Value }).FirstOrDefault();
string brandname = query.Value;
Define the brandname as string and assign null to it and then use the brand name where ever you want....
namespace XmlReading
{
class Program
{
static void Main(string[] args)
{
//Create an instance of the XmlTextReader and call Read method to read the file
XmlTextReader textReader = new XmlTextReader("D:\\myxml.xml");
textReader.Read();
string brandname = null;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(textReader);
XmlNodeList BCode = xmlDoc.GetElementsByTagName("Brandcode");
XmlNodeList BName = xmlDoc.GetElementsByTagName("Brandname");
for (int i = 0; i < BCode.Count; i++)
{
if (BCode[i].InnerText == "001")
{
brandname = BName[i].InnerText;
}
}
Console.WriteLine(brandname);
Console.ReadLine();
}
}
}
I am using this XML file:
<root>
<level1 name="A">
<level2 name="A1" />
<level2 name="A2" />
</level1>
<level1 name="B">
<level2 name="B1" />
<level2 name="B2" />
</level1>
<level1 name="C" />
</root>
Could someone give me a C# code using LINQ, the simplest way to print this result:
(Note the extra space if it is a level2 node)
A
A1
A2
B
B1
B2
C
Currently I have written this code:
XDocument xdoc = XDocument.Load("data.xml"));
var lv1s = from lv1 in xdoc.Descendants("level1")
select lv1.Attribute("name").Value;
foreach (var lv1 in lv1s)
{
result.AppendLine(lv1);
var lv2s = from lv2 in xdoc...???
}
Try this.
using System.Xml.Linq;
void Main()
{
StringBuilder result = new StringBuilder();
//Load xml
XDocument xdoc = XDocument.Load("data.xml");
//Run query
var lv1s = from lv1 in xdoc.Descendants("level1")
select new {
Header = lv1.Attribute("name").Value,
Children = lv1.Descendants("level2")
};
//Loop through results
foreach (var lv1 in lv1s){
result.AppendLine(lv1.Header);
foreach(var lv2 in lv1.Children)
result.AppendLine(" " + lv2.Attribute("name").Value);
}
Console.WriteLine(result);
}
Or, if you want a more general approach - i.e. for nesting up to "levelN":
void Main()
{
XElement rootElement = XElement.Load(#"c:\events\test.xml");
Console.WriteLine(GetOutline(0, rootElement));
}
private string GetOutline(int indentLevel, XElement element)
{
StringBuilder result = new StringBuilder();
if (element.Attribute("name") != null)
{
result = result.AppendLine(new string(' ', indentLevel * 2) + element.Attribute("name").Value);
}
foreach (XElement childElement in element.Elements())
{
result.Append(GetOutline(indentLevel + 1, childElement));
}
return result.ToString();
}
A couple of plain old foreach loops provides a clean solution:
foreach (XElement level1Element in XElement.Load("data.xml").Elements("level1"))
{
result.AppendLine(level1Element.Attribute("name").Value);
foreach (XElement level2Element in level1Element.Elements("level2"))
{
result.AppendLine(" " + level2Element.Attribute("name").Value);
}
}
Here are a couple of complete working examples that build on the #bendewey & #dommer examples. I needed to tweak each one a bit to get it to work, but in case another LINQ noob is looking for working examples, here you go:
//bendewey's example using data.xml from OP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
class loadXMLToLINQ1
{
static void Main( )
{
//Load xml
XDocument xdoc = XDocument.Load(#"c:\\data.xml"); //you'll have to edit your path
//Run query
var lv1s = from lv1 in xdoc.Descendants("level1")
select new
{
Header = lv1.Attribute("name").Value,
Children = lv1.Descendants("level2")
};
StringBuilder result = new StringBuilder(); //had to add this to make the result work
//Loop through results
foreach (var lv1 in lv1s)
{
result.AppendLine(" " + lv1.Header);
foreach(var lv2 in lv1.Children)
result.AppendLine(" " + lv2.Attribute("name").Value);
}
Console.WriteLine(result.ToString()); //added this so you could see the output on the console
}
}
And next:
//Dommer's example, using data.xml from OP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
class loadXMLToLINQ
{
static void Main( )
{
XElement rootElement = XElement.Load(#"c:\\data.xml"); //you'll have to edit your path
Console.WriteLine(GetOutline(0, rootElement));
}
static private string GetOutline(int indentLevel, XElement element)
{
StringBuilder result = new StringBuilder();
if (element.Attribute("name") != null)
{
result = result.AppendLine(new string(' ', indentLevel * 2) + element.Attribute("name").Value);
}
foreach (XElement childElement in element.Elements())
{
result.Append(GetOutline(indentLevel + 1, childElement));
}
return result.ToString();
}
}
These both compile & work in VS2010 using csc.exe version 4.0.30319.1 and give the exact same output. Hopefully these help someone else who's looking for working examples of code.
EDIT: added #eglasius' example as well since it became useful to me:
//#eglasius example, still using data.xml from OP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
class loadXMLToLINQ2
{
static void Main( )
{
StringBuilder result = new StringBuilder(); //needed for result below
XDocument xdoc = XDocument.Load(#"c:\\deg\\data.xml"); //you'll have to edit your path
var lv1s = xdoc.Root.Descendants("level1");
var lvs = lv1s.SelectMany(l=>
new string[]{ l.Attribute("name").Value }
.Union(
l.Descendants("level2")
.Select(l2=>" " + l2.Attribute("name").Value)
)
);
foreach (var lv in lvs)
{
result.AppendLine(lv);
}
Console.WriteLine(result);//added this so you could see the result
}
}
XDocument xdoc = XDocument.Load("data.xml");
var lv1s = xdoc.Root.Descendants("level1");
var lvs = lv1s.SelectMany(l=>
new string[]{ l.Attribute("name").Value }
.Union(
l.Descendants("level2")
.Select(l2=>" " + l2.Attribute("name").Value)
)
);
foreach (var lv in lvs)
{
result.AppendLine(lv);
}
Ps. You have to use .Root on any of these versions.
Asynchronous loading of the XML file can improve performance, especially if the file is large or if it takes a long time to load. In this example, we use the XDocument.LoadAsync method to load and parse the XML file asynchronously, which can help to prevent the application from becoming unresponsive while the file is being loaded.
Demo: https://dotnetfiddle.net/PGFE7c (using XML string parsing)
Implementation:
XDocument doc;
// Open the XML file using File.OpenRead and pass the stream to
// XDocument.LoadAsync to load and parse the XML asynchronously
using (var stream = File.OpenRead("data.xml"))
{
doc = await XDocument.LoadAsync(stream, LoadOptions.None, CancellationToken.None);
}
// Select the level1 elements from the document and create an anonymous object for each element
// with a Name property containing the value of the "name" attribute and a Children property
// containing a collection of the names of the level2 elements
var results = doc.Descendants("level1")
.Select(level1 => new
{
Name = level1.Attribute("name").Value,
Children = level1.Descendants("level2")
.Select(level2 => level2.Attribute("name").Value)
});
foreach (var result in results)
{
Console.WriteLine(result.Name);
foreach (var child in result.Children)
Console.WriteLine(" " + child);
}
Result:
A
A1
A2
B
B1
B2
C