将CSV文件转换为XML

14

我需要将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 文件每行有多少单词(列)来添加循环呢?

谢谢

5个回答

34
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");

输入:

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>

很棒的帖子...我用了一些片段,我的程序完美运行。 - afreeland
它为我节省了很多时间。感谢这个伟大的解决方案! - Gaurang Jadia
2
回复有点晚了,但如果您的列中包含带引号的分隔符,请注意字符串.split()不会关注带引号的字符串。例如:var x = ""Quoted;string""; var words = x.Split(';');words[0]; // "Quoted words[1]; // "string"因此,如果您的分隔符是常见字符,则会有额外的数据列。 - Oblivion2000
@dtb 如果我需要将"A;B"作为单个元素 - Meer
嗨@dtb,非常好的解决方案。但我想知道如何将列添加为自闭合标签元素,以便它们将是<Column0 D =d />而不是<Column0>D</Column0>。非常感谢您的帮助。尝试查找并发现这似乎是最佳解决方案,但需要弄清楚如何使其自闭合。 - Jay
显示剩余3条评论

12

如果您希望将标题用作元素名称:

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");

有没有办法将这个例子转换成JSON格式? - Alao

4
我编写了一个从Vlax代码片段派生的类。此外,我提供了一个单元测试来记录工作流程。 单元测试:
[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;
    }
}

注意:

我通过删除每个CSV行条目的尾随逗号来扩展了Vlax的解决方案,这导致一个运行时异常,因为与列标题相关的索引超出了范围。


1
优秀的实现! - LordTitiKaka
1
代码运行良好,但如果我的CSV中有逗号分隔的值,则无法正常工作。 - Utpal

1

Cinchoo ETL - 一种开源库,可用于使用少量代码轻松地将CSV转换为Xml。

对于一个示例CSV:

string csv = @"Id, Name, City
1, Tom, NY
2, Mark, NJ
3, Lou, FL
4, Smith, PA
5, Raj, DC
";

StringBuilder sb = new StringBuilder();

using (var p = ChoCSVReader.LoadText(csv)
    .WithFirstLineHeader()
    )
{
    using (var w = new ChoXmlWriter(sb)
        .Configure(c => c.RootName = "Employees")
        .Configure(c => c.NodeName = "Employee")
        )
        w.Write(p);
}

Console.WriteLine(sb.ToString());

输出Xml:

<Employees>
  <Employee>
    <Id>1</Id>
    <Name>Tom</Name>
    <City>NY</City>
  </Employee>
  <Employee>
    <Id>2</Id>
    <Name>Mark</Name>
    <City>NJ</City>
  </Employee>
  <Employee>
    <Id>3</Id>
    <Name>Lou</Name>
    <City>FL</City>
  </Employee>
  <Employee>
    <Id>4</Id>
    <Name>Smith</Name>
    <City>PA</City>
  </Employee>
  <Employee>
    <Id>5</Id>
    <Name>Raj</Name>
    <City>DC</City>
  </Employee>
</Employees>

请查看CodeProject文章以获取更多帮助。

免责声明:本库的作者是我自己。


我的CSV数据被引号包围,例如:"Value 1","Value 2"等等。而Cinchoo输出保留了这些引号,例如:<Field1>"Value 1"</Field1><Field2>"Value 2"</Field2>。我该如何让它不输出这些引号呢?我希望它输出<Field1>Value 1</Field1><Field2>Value 2</Field2>。 - Chris
使用 MayHaveQuotedFields() 函数来读取。 - Cinchoo

0
这里提供了一个不使用嵌套LINQ的解决方案,更易于理解。
  • 使用Linq to Xml。
  • 支持不同的分隔符(const)。
  • 支持每行具有不同的切片。

input.csv 的内容:

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

处理的代码:

Program.cs

using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;

namespace CSVtoXML
{
    class Program
    {
        private static void AddContentForEachLine(string line, ref XElement xmlTree)
        {
            var currentTree = new XElement("Item");
            const string delimiter = ",";     // Can be changed based on the actual situation
            string[] slices = line.Split(delimiter);

            for (int i = 0; i < slices.Count(); i++)
                currentTree.Add(new XElement($"Column{i}", slices[i].ToString()));

            xmlTree.Add(currentTree);
        }

        static void Main(string[] args)
        {
            var basePath = Environment.CurrentDirectory;
            var lines = File.ReadAllLines(Path.Combine(basePath, "../../..", @"input.csv"));

            var xmlTree = new XElement("TopElement");

            foreach (var line in lines)
            {
                AddContentForEachLine(line, ref xmlTree);
            }

            xmlTree.Save(Path.Combine(basePath, "../../..", @"output.xml"));
        }
    }
}

运行代码后,结果如下:

<?xml version="1.0" encoding="utf-8"?>
<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>


您可以在此处查看此代码的完整Visual Studio解决方案: https://github.com/yanglr/dotnetInterview/tree/master/CSVtoXML


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接