简体   繁体   中英

write to xml file using linq

Writing to XML using XDocument, but knowing where to write similar to this Qn.

Hope you can help me a bit. I'm trying to update a XML file, but am struggling to write the method which writes to the XML file. This is the XML file

  <software>
    <software_entry name="Adobe Acrobat X Standard" path="Applications\Acrobat\Acrobat XStandard\AcroStan.msi" />
    ..
  </software>
  <ApplicationConfigurations>
     <Config name ="someName" value ="someValue"/>
      ..
  </ApplicationConfigurations>

Here a user can change/add tags, ie, name or path under software_entry are unknown, since user can edit/add values. Except the format, everything is unknown.

I am able to read the file, store it in a DataTable , populate to grid, etc.. using LINQ

However, after editing the table while writing to xml I am stuck..

Can anyone please help me to write this kind of data into xml?

I have tried this so far:

for (int i = 0; i < dt.Length; i++) //for every table 
{
      XName TableName = dt[i].TableName; //table name.

      XElement[] xInnerElt = new XElement[dt[i].Rows.Count]; //for n rows inside one table
      for (int j = 0; j < dt[i].Rows.Count; j++) //loop each tag inside the table
      {
         XName InnerTagName = htAtributNameForTable[dt[i].TableName].ToString(); //tag name form hash table. i.e, software_entry
         //I am unable to write the next line
         xInnerElt[j] = new XElement(InnerTagName,new XAttribute((XName)ColumnName,rows Item arry)),
         //loop till all column added
      }

      XElement xElt = new XElement(TableName, xInnerElt); //one table aded to tag.
}

With this extension method you can convert any DataTable to XElement :

public static class Extensions
{
    public static XElement ToXml(this DataTable table, string rowElementName)
    {
        // check if table has name and rowElementName is not empty           

        return new XElement(
            new XElement(table.TableName,
                from row in table.AsEnumerable()
                select new XElement(rowElementName,
                    from column in table.Columns.Cast<DataColumn>()
                    let value = row.Field<string>(column)
                    where value != null
                    select new XAttribute(column.Caption, value)
                    )
                )
            );
    }
}

Usage:

DataTable table = new DataTable("software");
table.Columns.Add("name");
table.Columns.Add("path");
// add rows to table

XElement software = table.ToXml("software_element");

The problem:

You state in your comments that you are unsure how to add the inner elements which will create the attributes in your resulting XML. The rest of your code works perfectly, except this bit:

  //I am unable to write the next line xInnerElt[j] = new XElement(InnerTagName,new XAttribute((XName)ColumnName,rows Item arry)), 

The Solution

The missing part you want is to loop through the columns which are on the DataTable , and then access the values of the columns per row. This is because the DataRow object doesn't have information about the column names.

Therefore you need another inner loop through the DataTable.Columns collection, and then use the DataRow column accessor dt.Rows[j][column] to get the value from the current column.

It looks like this:

              foreach (var column in dt[i].Columns)
              {
                  xInnerElt[j].Add(
                      new XAttribute(
                          (column as DataColumn).ColumnName,
                          dt[i].Rows[j][(column as DataColumn)].ToString()
                      )
                  );
              }

My test with your code:

using System;
using System.Collections.Generic;
using System.Data;
using System.Xml.Linq;

namespace XElemFromDT
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, string> htAtributNameForTable = new Dictionary<string, string>()
            {
                { "Software", "software_entry" },
                { "ApplicationConfigurations", "config" }
            };

            DataTable dt1 = new DataTable();
            dt1.TableName = "Software";
            dt1.Columns.Add("name", typeof(string));
            dt1.Columns.Add("path", typeof(string));
            dt1.Rows.Add("Adobe Acrobat X Standard", @"Applications\Acrobat\Acrobat XStandard\AcroStan.msi");
            dt1.Rows.Add("Adobe Photoshop", @"Applications\Photoshop\Photoshop.msi");

            DataTable dt2 = new DataTable();
            dt2.TableName = "ApplicationConfigurations";
            dt2.Columns.Add("name", typeof(string));
            dt2.Columns.Add("value", typeof(string));
            dt2.Rows.Add("someName", "someValue");
            dt2.Rows.Add("someOtherName", "someOtherValue");

            DataTable[] dt = new DataTable[] { dt1, dt2 };

            XDocument xDoc = new XDocument(new XElement("Root"));

            for (int i = 0; i < dt.Length; i++) //for every table 
            {

                  XName TableName = dt[i].TableName; //table name.

                  XElement[] xInnerElt = new XElement[dt[i].Rows.Count]; //for n rows inside one table
                  for (int j = 0; j < dt[i].Rows.Count; j++) //loop each tag inside the table
                  {
                     XName InnerTagName = htAtributNameForTable[dt[i].TableName].ToString(); //tag name form hash table. i.e, software_entry
                     //I am unable to write the next line
                     xInnerElt[j] = new XElement(InnerTagName);

                      foreach (var column in dt[i].Columns)
                      {
                          xInnerElt[j].Add(
                              new XAttribute(
                                  (column as DataColumn).ColumnName,
                                  dt[i].Rows[j][(column as DataColumn)].ToString()
                              )
                          );
                      }
                  }

                  XElement xElt = new XElement(TableName, xInnerElt); //one table aded to tag.
                  xDoc.Root.Add(xElt);
            }

            Console.WriteLine(xDoc.ToString());
            Console.ReadKey();
        }
    }
}

Bonus -- because I had to try this

Here's a way to do it using LINQ completely: (using same test data as my previous example)

    DataTable[] dt = new DataTable[] { dt1, dt2 };

    XDocument xDoc = new XDocument(new XElement("Root"));

    Func<DataTable, DataRow, IEnumerable<XAttribute>> getAttributes = (t, r) =>
        t.Columns.OfType<DataColumn>().Select(c => new XAttribute(c.ColumnName, r[c].ToString()));

    Func<DataTable, IEnumerable<XElement>> getElements = t =>
        t.Rows.OfType<DataRow>().Select(r => new XElement(htAtributNameForTable[t.TableName], getAttributes(t, r)));

    Func<DataTable[], IEnumerable<XElement>> getTables = dtc =>
        dtc.AsEnumerable().Select(t => new XElement(t.TableName, getElements(t)));

    xDoc.Root.Add(getTables(dt));

If you're just looking for a way to stream your XElements to a file, that's pretty easy:

        using (XmlWriter writer = XmlWriter.Create("filename.xml"))
        {
            writer.WriteStartElement("DocumentElementName");
            // Your code goes here
            xElt.WriteTo(writer);
            writer.WriteEndElement();
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM