I have a complex XML which is loaded into XDocument and I want to turn it into a DataTable please. Here is the actual XML:
<?xml version="1.0"?>
<XMLCONTAINER version="1.0" incCount="3">
<APPLICATION id="APPLICATION_NODE_1" title="" name="55038170812RDOpayment" application="Excel" appfile="%SAAR%\xmlsFs\Dbxmls.xla" command="openDatabaseDesigner">
<MAINDATA id="CONTAINER_NODE_2" title=""/>
<SKY.FORM id="CONTAINER_NODE_3" title="" table="tblMain" database="\\c\E\DARA\Data\Link\RDOpayments.mdb">
<FIELD fieldName="RID">1234567</FIELD>
<FIELD fieldName="DateTime">05/02/2019 09:00:50</FIELD>
<FIELD fieldName="DateOfLetter">29/01/2019</FIELD>
<FIELD fieldName="Name">Mr Joe Bloggs</FIELD>
<FIELD fieldName="NRID">XXX1234X</FIELD>
<FIELD fieldName="paymentAmount">776.40</FIELD>
<FIELD fieldName="Amountfor0809">776.40</FIELD>
<FIELD fieldName="ACE">FALSE</FIELD>
<FIELD fieldName="Telephone">123456789</FIELD>
<FIELD fieldName="AcceptWithheldNumber">FALSE</FIELD>
<FIELD fieldName="PotentialACECase">FALSE</FIELD>
<FIELD childID="CONTAINER_NODE_3_field7option1" fieldName="CustomerType">3</FIELD>
<FIELD fieldName="ContactHistory">05/02/2019</FIELD>
<FIELD fieldName="ACERed">FALSE</FIELD>
</SKY.FORM>
The DataTable needs to look like this:
RID DateTime DateOfLetter Name NRID paymentAmount ACE
--- ----------- ------------ ---- ---- ------------- ----------
1234567 2019-02-05 10:23:51 2019-02-05 MISS LL TEST X1234X 123.45 FALSE
7654321 2019-01-11 11:11:11 2019-02-03 MR I WONG Y4321Y 321.21 TRUE
My current attempts of coding this are not picking up the xml as Node? I think it should be attributes xattribute??
// Conversion Xml file to DataTable
public DataTable CreateDataTableXML(string XmlFile)
{
XmlDocument doc = new XmlDocument();
doc.Load(XmlFile);
DataTable Dt = new DataTable();
try
{
Dt.TableName = GetTableName(XmlFile);
XmlNode NodeStructure = doc.DocumentElement.ChildNodes.Cast<XmlNode>().ToList()[0];
progressBar1.Maximum = NodeStructure.ChildNodes.Count;
progressBar1.Value = 0;
foreach (XmlNode column in NodeStructure.ChildNodes)
{
Dt.Columns.Add(column.Name, typeof(String));
Progress();
}
XmlNode Xnodes = doc.DocumentElement;
progressBar1.Maximum = Xnodes.ChildNodes.Count;
progressBar1.Value = 0;
foreach (XmlNode xnode in Xnodes.ChildNodes)
{
List<string> Values = xnode.ChildNodes.Cast<XmlNode>().ToList().Select(x => x.InnerText).ToList();
Dt.Rows.Add(Values.ToArray());
Progress();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
return Dt;
}
So I finally solved this. For anyone reading this, you need to solve in two parts, first get the column names into the datatable and then the xml values.
For the column names as we already have a table in sql we want to upload to, I simply used a dataadapter to fill the datatable with the schema which made sure every column name was there in the dt.
For the xml values, this proved more tricky as in a normal xml I could have just looped on the fieldname=, however as each attribute tag is different I extracted each manually with .attribute ?.value. It was a simple case then of creating datarows which would coalesce to DBNull if data was null.
Related
I have an xml document of type like this:
<?xml version="1.0" encoding="UTF-16"?>
<Recordset>
<Table>Recordset</Table>
<Rows>
<Row>
<Fields>
...
<Field>
<Alias>StatusName</Alias>
<Value>Scheduled</Value>
</Field>
<Field>
<Alias>U_Revision</Alias>
<Value>code00</Value>
</Field>
<Field>
<Alias>U_Quantity</Alias>
<Value>10.000000</Value>
</Field>
<Field>
<Alias>U_ActualQty</Alias>
<Value>0.000000</Value>
</Field>
...
</Fields>
</Row>
...
<Row>
<Fields>
...
<Field>
<Alias>StatusName</Alias>
<Value>Scheduled</Value>
</Field>
<Field>
<Alias>U_Revision</Alias>
<Value>code00</Value>
</Field>
<Field>
<Alias>U_Quantity</Alias>
<Value>150.000000</Value>
</Field>
<Field>
<Alias>U_ActualQty</Alias>
<Value>0.000000</Value>
</Field>
...
</Fields>
</Row>
</Rows>
</Recordset>
I have different values in field with alias of StatusName. There are some Scheduled, notScheduled, Realeased, Finished etc values. What I would like to do is to delete each node that contain node with alias StatusName and value lets say Scheduled or Finished.
I was thinking to do this more or less in that way however I am doing something wrong. May anybody let me on right way ?
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
XmlNodeList nodes = xmlDocument.SelectNodes("//Rows[#StatusName='Finished']");
for (int i = nodes.Count - 1; i >= 0; i--)
{
nodes[i].ParentNode.RemoveChild(nodes[i]);
}
var newXml = nodes.ToString();
I would like to delete the whole node if contains with alias StatusName and specific value lets say Finished.
I would expect the result in new string variable.
I like to work with DataTable with xml, I found it very easy.
I used a DataTable to work with your nodes.
So, I took your xml file and wrote some code for you that might help you:
//READ THE XML FILE
XmlDocument xmlDoc = new XmlDocument();
//My path
xmlDoc.LoadXml(Properties.Resources.test);
//Read the xml file into a dataSet
DataSet ds = new DataSet();
XmlNodeReader xnr = new XmlNodeReader(xmlDoc);
ds.ReadXml(xnr);
//Your data will be store in the 4's dataTable of the dataSet ( the <field> )
for(int i=0;i<ds.Tables[4].Rows.Count;i++)
{
//Check the value as you wish
//Here i want to suppress all the <Field> nodes with <Value> = "Scheduled"
if ( ds.Tables[4].Rows[i]["Value"].ToString().Equals("Scheduled"))
{
//RemoteAt will remove all the node, so the node <Field> in your example data
ds.Tables[4].Rows.RemoveAt(i);
//If you want to only remove the node <Value> (and not all the <Field> node ) just do ds.Tables[4].Rows["Value"]=null;
}
}
//Write your new content in a new xml file
//As you wanted here you just read the new xml file created as a string
using (var stringWriter = new StringWriter())
using (var xmlTextWriter = XmlWriter.Create(stringWriter))
{
ds.WriteXml(xmlTextWriter);
xmlTextWriter.Flush();
stringWriter.GetStringBuilder().ToString();
//Here the result is in stringWriter, and there is 6 <Field> nodes, and not 8 like before the suppress
}
//If you want to create a new xml file with the new content just do
ds.WriteXml(yourPathOfXmlFile);
//( like rewriting the previous xml file )
I assume, you are going to delete entire <Row> which matches your condition
i.e.,
<Row>
<Fields>
...
<Field>
<Alias>StatusName</Alias>
<Value>Finished</Value>
</Field>
</Fields>
</Row>
The required XPath:
//Row[Fields[Field[Alias[text()='StatusName'] and Value[text() = 'Finished']]]]
C#
string xPath = #"//Row[Fields[Field[Alias[text()='StatusName'] and Value[text() = 'Finished']]]]";
var nodes = xmlDocument.SelectNodes(xPath);
for (int i = nodes.Count - 1; i >= 0; i--)
{
nodes[i].ParentNode.RemoveChild(nodes[i]);
}
var newXml = xmlDocument.OuterXml;
I have the following XML doc... I have just left one "line" in for simplicity.
<?xml version="1.0" encoding="UTF-8"?>
<files>
<file type="INVOICES">
<document>blah.pdf</document>
<line>
<field name="JobNo">321654</field>
<field name="Issues">1</field>
<field name="PageCount">200</field>
<field name="PrintRun">250</field>
<field name="Size">Small</field>
</line>
</file>
</files>
C# code:
static void Main(string[] args)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("input.xml");
XmlNodeList itemNodes = xmlDoc.SelectNodes("//files/file/line");
foreach (XmlNode itemNode in itemNodes)
{
XmlNode jobNo = itemNode.SelectSingleNode("field");
if (jobNo != null)
Console.WriteLine(jobNo.InnerText);
}
Console.ReadKey();
}
This iterates through each line and displays the job number however I want to access the field by it's name JobNo i.e.
<field name="JobNo">321654</field> accessed with...
jobNo = itemNode.SelectSingleNode("JobNo");
I know I can change the xml but the XML is supplied by a customer so this is not really an option.
You can access that specific field element by providing the attribute name and value such as
XmlNode jobNo = itemNode.SelectSingleNode("field[#name='JobNo']");
I've got XML that describes certain data (a template) that I want to be able to edit. I load XML into DataSet (see fig. 1 below), plug DataSet tables into DataGridView (switch between them using a separate comboBox), make changes and then save XML (simple DataSet.WriteXML directive). The XML I read looks very nice and humanly readable (see fig. 2 below), however, the written XML is nowhere near the original (see fig. 3 below).
My goal is to allow editing of XML document and preserve it in the same form on save.
What am I doing wrong? Code/XML blocks are below.
fig.1 - Reading XML into DataSet:
using (XmlReader xrMeta = XmlReader.Create(new StreamReader(ofdOpenXML.FileName)))
{
while (!xrMeta.EOF)
{
xrMeta.ReadToFollowing("record");
if (xrMeta.NodeType == XmlNodeType.Element)
{
xrMeta.ReadToFollowing("fields");
xrSub = xrMeta.ReadSubtree();
dt = new DataTable();
ds = new DataSet();
ds.ReadXml(xrSub);
dt = ds.Tables[0].Copy();
dt.TableName = "recordTypeId " + iTableNumber.ToString().PadLeft(2, '0');
MetaXML.Tables.Add(dt);
iTableNumber++;
}
}
dgvMetaXML.DataSource = MetaXML.Tables[0];
fig.2 - Input XML:
<?xml version='1.0'?>
<records>
<record>
<recordTypeId>01</recordTypeId>
<fields>
<field>
<fieldNID>entityID</fieldNID>
<fieldID>1</fieldID>
<fieldName>Entity ID</fieldName>
<fieldStartPos>1</fieldStartPos>
<fieldEndPos>6</fieldEndPos>
<fieldLength>6</fieldLength>
<fieldType>Alpha</fieldType>
<fieldRequired>Y</fieldRequired>
<fieldDefaultValue></fieldDefaultValue>
</field>
<field>
<fieldNID>reserved0101</fieldNID>
<fieldID>2</fieldID>
<fieldName>Reserved</fieldName>
<fieldStartPos>7</fieldStartPos>
<fieldEndPos>8</fieldEndPos>
<fieldLength>2</fieldLength>
<fieldType>Alpha</fieldType>
<fieldRequired>Y</fieldRequired>
<fieldDefaultValue> </fieldDefaultValue>
</field>
<field>
<fieldNID>deviceID</fieldNID>
<fieldID>3</fieldID>
<fieldName>Device ID</fieldName>
<fieldStartPos>9</fieldStartPos>
<fieldEndPos>23</fieldEndPos>
<fieldLength>15</fieldLength>
<fieldType>Alpha</fieldType>
<fieldRequired>Y</fieldRequired>
<fieldDefaultValue></fieldDefaultValue>
</field>
</fields>
</record>
<record>
<recordTypeId>02</recordTypeId>
<fields>
<field>
<fieldNID>userID</fieldNID>
<fieldID>1</fieldID>
<fieldName>User ID</fieldName>
<fieldStartPos>1</fieldStartPos>
<fieldEndPos>6</fieldEndPos>
<fieldLength>6</fieldLength>
<fieldType>Alpha</fieldType>
<fieldRequired>Y</fieldRequired>
<fieldDefaultValue></fieldDefaultValue>
</field>
<field>
<fieldNID>reserved0201</fieldNID>
<fieldID>2</fieldID>
<fieldName>Reserved</fieldName>
<fieldStartPos>7</fieldStartPos>
<fieldEndPos>8</fieldEndPos>
<fieldLength>2</fieldLength>
<fieldType>Alpha</fieldType>
<fieldRequired>Y</fieldRequired>
<fieldDefaultValue> </fieldDefaultValue>
</field>
<field>
<fieldNID>testField</fieldNID>
<fieldID>3</fieldID>
<fieldName>Test Sequence</fieldName>
<fieldStartPos>9</fieldStartPos>
<fieldEndPos>23</fieldEndPos>
<fieldLength>15</fieldLength>
<fieldType>Alpha</fieldType>
<fieldRequired>Y</fieldRequired>
<fieldDefaultValue></fieldDefaultValue>
</field>
</fields>
</record>
</records>
fig.3 - output XML:
<records>
<recordTypeId_x0020_01>
<fieldNID>entityID</fieldNID>
<fieldID>1</fieldID>
<fieldName>Entity ID</fieldName>
<fieldStartPos>1</fieldStartPos>
<fieldEndPos>6</fieldEndPos>
<fieldLength>6</fieldLength>
<fieldType>Alpha</fieldType>
<fieldRequired>Y</fieldRequired>
<fieldDefaultValue />
</recordTypeId_x0020_01>
<recordTypeId_x0020_01>
<fieldNID>reserved0101</fieldNID>
<fieldID>2</fieldID>
<fieldName>Reserved</fieldName>
<fieldStartPos>7</fieldStartPos>
<fieldEndPos>8</fieldEndPos>
<fieldLength>2</fieldLength>
<fieldType>Alpha</fieldType>
<fieldRequired>Y</fieldRequired>
<fieldDefaultValue />
</recordTypeId_x0020_01>
<recordTypeId_x0020_01>
<fieldNID>deviceID</fieldNID>
<fieldID>3</fieldID>
<fieldName>Device ID</fieldName>
<fieldStartPos>9</fieldStartPos>
<fieldEndPos>23</fieldEndPos>
<fieldLength>15</fieldLength>
<fieldType>Alpha</fieldType>
<fieldRequired>Y</fieldRequired>
<fieldDefaultValue />
</recordTypeId_x0020_01>
<recordTypeId_x0020_02>
<fieldNID>userID</fieldNID>
<fieldID>1</fieldID>
<fieldName>User ID</fieldName>
<fieldStartPos>1</fieldStartPos>
<fieldEndPos>6</fieldEndPos>
<fieldLength>6</fieldLength>
<fieldType>Alpha</fieldType>
<fieldRequired>Y</fieldRequired>
<fieldDefaultValue />
</recordTypeId_x0020_02>
<recordTypeId_x0020_02>
<fieldNID>reserved0201</fieldNID>
<fieldID>2</fieldID>
<fieldName>Reserved</fieldName>
<fieldStartPos>7</fieldStartPos>
<fieldEndPos>8</fieldEndPos>
<fieldLength>2</fieldLength>
<fieldType>Alpha</fieldType>
<fieldRequired>Y</fieldRequired>
<fieldDefaultValue />
</recordTypeId_x0020_02>
<recordTypeId_x0020_02>
<fieldNID>testField</fieldNID>
<fieldID>3</fieldID>
<fieldName>Test Sequence</fieldName>
<fieldStartPos>9</fieldStartPos>
<fieldEndPos>23</fieldEndPos>
<fieldLength>15</fieldLength>
<fieldType>Alpha</fieldType>
<fieldRequired>Y</fieldRequired>
<fieldDefaultValue />
</recordTypeId_x0020_02>
</records>
Your code is reading to the next fields entry with each iteration using
xrMeta.ReadToFollowing("fields");
You're then renaming the base table from fields to recordTypeId XX with
dt.TableName = "recordTypeId " + iTableNumber.ToString().PadLeft(2, '0');`
and the space is being encoded to _x0020_ to avoid breaking up the tag.
You then add this renamed instance of fields back to the root with
MetaXML.Tables.Add(dt);
The output is a result of this.
What different result were you trying to achieve?
> The XML I read looks very nice and humanly readable (see fig. 2 below), however,
> the written XML is nowhere near the original (see fig. 3 below).
> What am I doing wrong?
the dotnet dataset can only write the xml-format of its internal representation.
this representation is similar to
<datasetName>
<dataTableName OtherFieldName='value'>
<FieldName>value</FieldName>
</dataTableName>
</datasetName>
So fields are elements or attributs. Your xml structre is more complex.
The dataset tries to interprete your data and puts the data into its internal structure, if possible. In your example the information recordTypeId is lost.
I had a similar problem and created a my own xml-post-processer that reformats the xml-output to my own xml-format that dataset can read but not write.
Ended up going with k3b's approach (sorry, can't upvote - need more reputation).
Here's the updated code to read the XML into DataSet (keep in mind, it's just a mock code to make things work for the first time. You should revise it to be more efficient and ultimately make more sense):
int iTableNumber = 1;
// Read input XML
using (XmlReader xrMeta = XmlReader.Create(new StreamReader(ofdOpenXML.FileName)))
{
while (!xrMeta.EOF)
{
// Advance to next <record>
xrMeta.ReadToFollowing("record");
if (xrMeta.NodeType == XmlNodeType.Element)
{
// Advance to the next <fields>
xrMeta.ReadToFollowing("fields");
// Read underlying XML - it will be a set of flat tables
xrSub = xrMeta.ReadSubtree();
dt = new DataTable();
ds = new DataSet("fields");
ds.ReadXml(xrSub);
dt = ds.Tables[0].Copy();
dt.TableName = "field_" + iTableNumber.ToString().PadLeft(2, '0');
MetaXML.Tables.Add(dt);
iTableNumber++;
}
}
}
// Populate comboBox to switch between tables in DataSet
for (int i = 0; i < MetaXML.Tables.Count; i++)
{
cbShowTable.Items.Add(MetaXML.Tables[i].TableName);
}
// Populate DataGridView with first read table
dataGridViewMetaXML.DataSource = MetaXML.Tables[0];
Saving XML now looks like this:
// This is our output XML file
// Technically, it should have been the same name as the input one
// but for the purposes of testing it isn't
StreamWriter srFile = new StreamWriter((#"testingOutputXML.xml"));
StringWriter stWriter;
StringBuilder sbXML = new StringBuilder();
// Headers to play nice
sbXML.AppendLine("<?xml version='1.0'?>");
sbXML.AppendLine("<records>");
DataTable dt;
for (int i = 0; i < MetaXML.Tables.Count; i++)
{
// This is where we have to recreate the structure manually
sbXML.AppendLine("<record>");
sbXML.Append("<recordTypeId>");
sbXML.Append((1+ i).ToString().PadLeft(2,'0'));
sbXML.AppendLine("</recordTypeId>");
dt = new DataTable();
dt = MetaXML.Tables[i].Copy();
dt.TableName = "field";
stWriter = new StringWriter();
dt.WriteXml(stWriter, false);
stWriter.WriteLine();
sbXML.Append(stWriter.GetStringBuilder());
// Need to clean up because DataTable's WriteXML() method
// wraps the data in <DocumentElement> and </DocumentElement> tags
sbXML.Replace("DocumentElement", "fields");
sbXML.AppendLine("</record>");
}
sbXML.AppendLine("</records>");
srFile.Write(sbXML.ToString());
srFile.Flush();
srFile.Close();
MessageBox.Show("Done!");
Thanks everyone who chipped in with the answers, it steered me to the right track.
In this xml file (http://www.studiovincent.net/list.xml):
<list version="1.0">
<meta>
<type>resource-list</type>
</meta>
<resources start="0" count="4">
<resource classname="Quote">
<field name="name">Vincent</field>
<field name="username">Hill</field>
<field name="age">31</field>
<field name="hair">black</field>
</resource>
<resource classname="Quote">
<field name="name">John</field>
<field name="username">Tedelon</field>
<field name="age">27</field>
<field name="hair">brown</field>
</resource>
<resource classname="Quote">
<field name="name">Michael</field>
<field name="username">Lopez</field>
<field name="age">20</field>
<field name="hair">red</field>
</resource>
<resource classname="Quote">
<field name="name">Frank</field>
<field name="username">Lopez</field>
<field name="age">25</field>
<field name="hair">black</field>
</resource>
</resources>
</list>
using this code:
using System.Xml;
using.System.Xml.Linq;
XmlReader reader = XmlReader.Create("http://www.studiovincent.net/list.xml");
XElement el = XElement.Load(reader);
reader.Close();
var items = el.Elements("resources").Elements("resource").Descendants().DescendantNodes();
var items = from item in el.Elements("resources").Elements("resource").Descendants()
where item.Attribute("name").Value == "name" select item.FirstNode;
foreach (XNode node in items)
{
Console.WriteLine(node.ToString());
}
I have this OUTPUT:
Vincent
John
Michael
Frank
Code working very good, but I need get value 31 which corresponds field name="age" where field name="name" is Vincent. How Can I get this result?
I recommend you do as you would when reading XML naturally.
In your code, try to find all the fields with the name attribute set to "name".
This process cannot be used to associate a name with an age. It is more natural to read the XML and check all resource elements. Then add to this element some information described in the field elements.
// Using LINQ to SQL
XDocument document = XDocument.Load("http://www.studiovincent.net/list.xml"); // Loads the XML document.
XElement resourcesElement = document.Root.Element("resources"); // Gets the "resources" element that is in the root "list" of the document.
XElement resourceElementVincent = (from resourceElement in resourcesElement.Elements("resource")// Gets all the "resource" elements in the "resources" element
let fieldNameElement = resourceElement.Elements("field").Single(fieldElement => fieldElement.Attribute("name").Value == "name") // Gets the field that contains the name (there one and only one "name" field in the "resource" element -> use of Single())
where fieldNameElement.Value == "Vincent" // To get only resources called "Vincent"
select resourceElement).Single(); // We suppose there is one and only 1 resource called "Vincent" -> Use of Single()
XElement fieldAgeElement = resourceElementVincent.Elements("field").Single(fieldElement => fieldElement.Attribute("name").Value == "age"); // Gets the corresponding "age" field
int age = int.Parse(fieldAgeElement.Value, CultureInfo.InvariantCulture); // Gets the age by Parse it as an integer
Console.WriteLine(age);
Does it do what you want?
Consider this below XML is there as one of the SQL table's column.
<Root>
<Name>Dinesh</Name>
<Id>2</Id>
</Root>
The objective of the query is to fetch the Name from the XML. In this example we will fetch the 'Dinesh' as the value.
var Query = (from t in dbContext.Employee.AsEnumerable()
where t.active == true
select new Employee
{
Id = t.AtpEventId,
Name = XDocument.Parse(t.Content).Descendants("Root").Descendants("Name").ToList().
Select(node => node.Value.ToString()).FirstOrDefault()
});
Note the following:
t.active == true is just an example to make some condition if needed.
Please note, in the above LINQ query, always use the AsEnumerable, as I did in the first line.
Descendants("Root").Descendants("Name") - here "Root" should be the element matching with the XML, and under Root we have Name element.
I am currently working with a webservice to pull a report about users in a remote support system.
After pulling my report and receiving the result, I am given the following string back by the method:
<report>
<header>
<field id="0">Source</field>
<field id="1">Session ID</field>
<field id="2">Date</field>
<field id="3">Name</field>
<field id="24">Technician Name</field>
<field id="25">Technician ID</field>
</header>
<data>
<row>
<field id="0">Email</field>
<field id="1">55037806</field>
<field id="2">4/13/2010 2:28:06 AM</field>
<field id="3">Bill Gates</field>
<field id="24">John</field>
<field id="25">1821852</field>
</row>
<row>
<field id="0">Telephone</field>
<field id="1">55034548</field>
<field id="2">4/13/2010 12:59:44 AM</field>
<field id="3">Steve Jobs</field>
<field id="24">John</field>
<field id="25">1821852</field>
</row>
</data>
</report>
After receiving this string, I need to take it and display the actual data in a datagridview. I've tried putting it into an XMLDocument then reading that, but it seems to keep failing. Just interested in another set of eyes :) Application is written in C# in VS2010.
You could use Linq to XML to build a DataTable with the column names from the XML file, then set that table as the DataSource of the DataGridView :
string xml = theWebService.TheMethod();
XDocument doc = XDocument.Parse(xml);
var queryHeaders =
from field in doc.Root.Element("header").Elements("field")
select new
{
Id = field.Attribute("id").Value,
Name = field.Value
};
var headers = queryHeaders.ToDictionary(f => f.Id, f => f.Name);
DataTable table = new DataTable();
foreach (var kvp in headers)
{
table.Columns.Add(kvp.Value);
}
Func<XElement, DataRow> rowGenerator =
element =>
{
var row = table.NewRow();
foreach (var field in element.Elements("field"))
{
string fieldId = field.Attribute("id").Value;
string columnName = headers[fieldId];
string value = field.Value;
row[columnName] = value;
}
return row;
};
var rows =
from rowElement in doc.Root.Element("data").Elements("row")
select rowGenerator(rowElement);
foreach (var row in rows)
{
table.Rows.Add(row);
}
dataGridView.AutoGenerateColumns = true;
dataGridView.DataSource = table;
Note that the code above only works if all fields have distinct names. If that's not the case, you might want to use the field ID as the DataColumn name, and change the headers of the DGV with the actual field names