简体   繁体   中英

What is the correct method to load an XML file and re-write it as a CSV?

Updated to be clear.

Step One: I have a XML file that I want to load into a DatGridView. (Mostly working thanks to Max, but I still have a problem with the XML rollup)

Step Two: Run some code based on user input -- (not part of this solution)

Step Three: Export the DataGridView into a CSV File. (Solved by Max! Thanks Man That was exactly what I was trying to do on that part.)

Using VS 2008 C#

(Created a new project)

Sample from the XML file cars.xml

<?xml version="1.0" encoding="utf-8" ?>
<root>
  <car>
    <year>2010</year>
    <make>Chevy</make>
    <model>Surburban</model>
    <color-e>Black</color-e>
    <color-i>Black</color-i>
    <features>
      <Engine>8 cylinder</Engine>
      <gas>Petrol</gas>
      <doors>5</doors>
      <miles>12312</miles>
    </features>
    </car>
  <car>
    <year>2001</year>
    <make>Ford</make>
    <model>Excursion</model>
    <color-e>Black</color-e>
    <color-i>Black</color-i>
    <features>
      <Engine>10 cylinder</Engine>
      <gas>Petrol</gas>
      <doors>5</doors>
      <miles>90312</miles>
    </features>
  </car>
  <car>
    <year>1999</year>
    <make>Chevy</make>
    <model>corvette</model>
    <color-e>Silver</color-e>
    <color-i>Black</color-i>
    <features>
      <Engine>8 cylinder</Engine>
      <gas>Petrol</gas>
      <doors>3</doors>
      <miles>44222</miles>
    </features>
  </car>
</root>

This is a winform application. It has two button, one textbox, and one datagridview. button one should load the XML data into the datagrid. Then button two should save the data in the datagridview to a CSV file.

Here is the code I have so far to open and load the xml into the datagridview.

namespace carsXML
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
        var cars = XDocument.Load(@"C:\cars.xml");
        var query = from c in cars.Descendants("car")
                    select new
                    {
                        Year = (string)c.Element("year").Value,
                        Make = (string)c.Element("make").Value,
                        Model = (string)c.Element("model").Value,
                        // I needed to step directly into the sub element.
                        gas = (string)c.Element("features").Element("gas").Value,
                        doors = (string)c.Element("features").Element("doors").Value,
                        miles = (string)c.Element("features").Element("miles").Value


                    };

        dataGridView1.DataSource = query.ToList();

        }

        private void button2_Click(object sender, EventArgs e)
        {
          dataGridView1.ExportToCSV(@"C:\cars-griddump.csv"); 
         //Added Class Max showed me.  This works, I have only tested it on a small
         // XML file so far but it seems to work exactly as I wanted.
        }
    }
}

This gets the elements directly below the car element. When it gets to the feature element the application crashes. (null ref etc.)

So the last part of this before I am done with this little project is to figure out the XML rollup. When the code reads the car elements it gets all of the sub elements that are directly under car. It fails when it gets to the element that has additional sub elements and does not add them to the datagridview.

//Rollup problem fixed. It took me a while though! // Thank you for all of the help!

One way is to load the XML in to a dataset and then save the dataset to a CSV. This is the easiest one. But this has some dependency the structure of the XML tree.

Update to show a sample to save DataTable to csv:

We can call this method for each DataTable in the DataSet

void SaveDTtoCSV(DataTable dt)
{
    // Save data to CSV file 
    StreamWriter writer = new StreamWriter(@"c:/dsoutput.csv");
    // First we will write the headers.
    int iColCount = dt.Columns.Count;
    for(int i = 0; i < iColCount; i++)
    {
        writer.Write(dt.Columns[i]);
        if (i < iColCount - 1)
        {
            writer.Write(",");
        }
    }
    writer.Write(writer.NewLine);
    // Now write all the rows.
    foreach (DataRow dr in dt.Rows)
    {
        for (int i = 0; i < iColCount; i++)
        {
            if (!Convert.IsDBNull(dr[i]))
            {
                writer.Write(dr[i].ToString());
            }
            if ( i < iColCount - 1)
            {
                writer.Write(",");
            }
        }
        writer.Write(writer.NewLine);
    }
    writer.Close();
}

Unless you want to use XSLT and XslCompiledTransform look at LINQ to XML. It is simple and strait forward. To load XML file you would use XDocument.Load(path) and to extract car element(s) you would use Decedents("car") method. And then simply loop over elements writing them to output.

var cars = XDocument.Load(path).Decedents("car");

LINQ to SQL

XSLT and XslCompiledTransform: look for xslcompiledtransform on MSDN

If you are using VS2008 you can extend DataGrid and create a helper class to handle all of your exporting needs.

  1. Add this [DataGridViewExtender] to your project as seen below

     namespace System.Windows.Forms { using System; using System.Collections.Generic; using System.Windows.Forms; using System.IO; 

    \n\n

    static class DataGridViewExtender { public static void ExportToCSV(this DataGridView grid, string path) { ExportToFile(grid, path, ","); }

    \n\npublic static void ExportToFile(this DataGridView grid, string path, string separator) { if (grid.Columns.Count <= 0) { return; } using (var writer = new StreamWriter(path)) { var values = new List<string>(grid.Columns.Count); foreach (DataGridViewColumn column in grid.Columns) { values.Add(column.Name); } writer.WriteLine(string.Join(separator, values.ToArray())); foreach (DataGridViewRow row in grid.Rows) { values.Clear(); foreach (DataGridViewCell cell in row.Cells) { values.Add(string.Format("{0}", cell.Value)); } writer.WriteLine(string.Join(separator, values.ToArray())); } writer.Close(); } }

    } }

  2. in button2_Click call this method private void button2_Click(object sender, EventArgs e) { dataGridView1.ExportToCSV(@"C:\\griddump.csv"); } private void button2_Click(object sender, EventArgs e) { dataGridView1.ExportToCSV(@"C:\\griddump.csv"); }

There's not much detail in your question, but the best I can do is assume that you've got an XML file that contains information related to cars, like

<cars>
  <car>
    <make>Honda</model>
    <model>Odyssey</model>
    <year>2009</year>
  </car>
  <car>
    <make>Toyota</make>
    <model>Sienna</model>
    <year>2010</year>
  </car>
</cars>

And then you want to write a CSV that looks like this:

make   model   year
Honda  Odyssey 2009
Toyota Sienna  2010

Since you have tagged linq-to-xml, I assume you want to use LINQ, though you might not know what to do next. Try using XDocument.Load to load your XML file. You'll then use Element() and Elements() to get at the nodes in the XML structure. For example, if xml = XDocument.Load(filename) , then xml.Element("cars") should give you the root node. With that, you can use xml.Element("cars").Elements() to get all of the elements... and so on and so forth.

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