Преобразование CSV файла в XML

Мне нужно преобразовать CSV в XML-документ. Примеры, которые я видел до сих пор, показывают, как это сделать с фиксированным числом столбцов в CSV.

У меня это до сих пор, используя LINQ:

String[] File = File.ReadAllLines(@"C:\text.csv");

        String xml = "";

        XElement top = new XElement("TopElement",

        from items in File

        let fields = items.Split(';')

        select new XElement("Item",

        new XElement("Column1", fields[0]),

        new XElement("Column2", fields[1]),

        new XElement("Column3", fields[2]),

        new XElement("Column4", fields[3]),

        new XElement("Column5", fields[4])

        )

        );

        File.WriteAllText(@"C:\xmlout.xml", xml + top.ToString());

Это для фиксированного количества столбцов, но мое .CSV имеет различное количество столбцов в каждой строке.

Как бы вы поместили какой-то цикл в это, в зависимости от того, сколько слов (столбцов) есть в каждой строке .CSV?

Thnx

Ответы

Ответ 1

var lines = File.ReadAllLines(@"C:\text.csv");

var xml = new XElement("TopElement",
   lines.Select(line => new XElement("Item",
      line.Split(';')
          .Select((column, index) => new XElement("Column" + index, column)))));

xml.Save(@"C:\xmlout.xml");

Input:

A;B;C
D;E;F
G;H

Вывод:

<TopElement>
  <Item>
    <Column0>A</Column0>
    <Column1>B</Column1>
    <Column2>C</Column2>
  </Item>
  <Item>
    <Column0>D</Column0>
    <Column1>E</Column1>
    <Column2>F</Column2>
  </Item>
  <Item>
    <Column0>G</Column0>
    <Column1>H</Column1>
  </Item>
</TopElement>

Ответ 2

Если вы хотите использовать заголовки в качестве имен элементов:

var lines = File.ReadAllLines(@"C:\text.csv");
string[] headers = lines[0].Split(',').Select(x => x.Trim('\"')).ToArray();

var xml = new XElement("TopElement",
   lines.Where((line, index) => index > 0).Select(line => new XElement("Item",
      line.Split(',').Select((column, index) => new XElement(headers[index], column)))));

xml.Save(@"C:\xmlout.xml");

Ответ 3

Я написал класс, полученный из фрагмента Vlax. Кроме того, я предоставил unit test для документирования рабочего процесса.

Unit Test:

[TestMethod]
public void convert_csv_to_xml()
{
    // Setup
    var csvPath = @"Testware\vendor.csv";
    var xmlPath = @"Testware\vendor.xml";

    // Test
    var success = DocumentConverter.Instance.CsvToXml(csvPath, xmlPath);

    // Verify
    var expected = File.Exists(xmlPath) && success;
    Assert.AreEqual(true, expected);
}

CSV для XML:

public class DocumentConverter
{
    #region Singleton
    static DocumentConverter _documentConverter = null;

    private DocumentConverter() { }

    public static DocumentConverter Instance
    {
        get
        {
            if (_documentConverter == null)
            {
                _documentConverter = new DocumentConverter();
            }

            return _documentConverter;
        }
    }
    #endregion

    public bool CsvToXml(string sourcePath, string destinationPath)
    {
        var success = false;

        var fileExists = File.Exists(sourcePath);

        if (!fileExists)
        {
            return success;
        }

        var formatedLines = LoadCsv(sourcePath);
        var headers = formatedLines[0].Split(',').Select(x => x.Trim('\"').Replace(" ", string.Empty)).ToArray();

        var xml = new XElement("VendorParts",
           formatedLines.Where((line, index) => index > 0).
               Select(line => new XElement("Part",
                  line.Split(',').Select((field, index) => new XElement(headers[index], field)))));

        try
        {
            xml.Save(destinationPath);

            success = true;
        }
        catch (Exception ex)
        {
            success = false;

            var baseException = ex.GetBaseException();
            Debug.Write(baseException.Message);
        }

        return success;
    }

    private List<string> LoadCsv(string sourcePath)
    {
        var lines = File.ReadAllLines(sourcePath).ToList();

        var formatedLines = new List<string>();

        foreach (var line in lines)
        {
            var formatedLine = line.TrimEnd(',');
            formatedLines.Add(formatedLine);
        }
        return formatedLines;
    }
}

Примечание:

Я расширил решение Vlax, удалив конечную запятую для каждой из записей строки CSV, которая вызвала исключение во время выполнения, основанное на отсутствии индекса из-за заголовка столбца.