【问题标题】:Template based XML to CSV conversion基于模板的 XML 到 CSV 的转换
【发布时间】:2016-10-23 06:03:30
【问题描述】:

我是 C# 新手,我知道这对每个人来说都是一项非常艰巨和具有挑战性的任务。我有一个需要将基于模板的 XML 转换为 CSV 的案例。我在下面列出了模板、示例 XML 和预期的 CSV

我的模板将有一些列,这些列应该是输出 CSV 中的 标题。我们希望将模板中的每一列与 XML 匹配,并检查它是否存在。如果 XML 中存在模板列值,则将其 添加 到 CSV 中,如果不存在,则 将其添加为 null,如下例所示。

同样在给定的 XML 示例中,它具有三种类型的行

  1. TRX
  2. TRXR
  3. TRXC

我们只需要考虑前两种类型的行。我们必须忽略 XML 中的 TRXC 类型行。

给定模板

var templeteList = new List<string> //Consider this as my template
        {
           "ID",
           "Name",
           "Address",
           "Phone",
           "Email",
           "Gender"     
        };

示例 XML

<?xml version="1.0" encoding="utf-8"?>
<StudentXML>
    <TRX ID="2" Name="Smita" Address="Pune" Gender="F" Phone="987654321"/>
    <TRX ID="2" Name="Ram"  Phone="3554321" Email="ram@mail.com" />
    <TRX ID="1" Name="John" Address="Mumbai" Phone="NULL" Email="John@mail.com" Gender="M" />
    <TRXR ID="3" Name="NULL" Address="Mumbai" Phone="121212" Email="Don@mail.com" Gender="M" />
    <TRXC ID="3" Name="Prem" Address="Mumbai" Phone="121212" Email="Prem@mail.com" Gender="M"/>
</StudentXML>

预期输出

"ID", "Name",   "Address",    "Phone",           "Email",          "Gender" 
"2",   "Smita",  "Pune",      "987654321",       "NULL",            "F"
"2",   "Ram",    "NULL",      "3554321",         "ram@mail.com",    "NULL"
"1",   "John",   "Mumbai",    "NULL",            "ohn@mail.com",    "M"
"3",   "NULL",   "Mumbai",    "121212",          "Don@mail.com",    "M"

我尝试使用 XML Reader,但将 60 万 XML 行的 XML 转换为 CSV 需要更长的时间。

 var dataSet = new DataSet();
 dataSet.ReadXml("XML File Name");//This line takes to much longer

如果有人能从这里帮助我,我将非常感激。

【问题讨论】:

  • 如果结构不变我推荐XmlSerializerjonasjohn.de/snippets/csharp/xmlserializer-example.htm
  • @SebastianSchulz 结构正在发生变化,每行都不相同
  • 您写道:我们必须忽略 TRXC。接下来,您编写 Expected Output,我们可以看到来自 TRXC 的数据。哪里有差异?

标签: c# xml linq csv xml-parsing


【解决方案1】:

您可以使用 MemoryStream 从 StreamWriter 写入数据。然后使用 StreamReader 读取 MemoryStream,如下所示,

            var templateList = new List<string> 
            {
               "ID",
               "Name",
               "Address",
               "Phone",
               "Email",
               "Gender"     
            };

            using (XmlReader xmlReader = XmlReader.Create("MyXMLFile.xml"))
            {
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    using (StreamWriter csvWriter = new StreamWriter(memoryStream))
                    {
                        csvWriter.WriteLine("\"" + string.Join("\",\"", templateList) + "\"");
                        xmlReader.MoveToContent();
                        while (xmlReader.Read())
                        {
                            if (xmlReader.NodeType == XmlNodeType.Element &&
                                (xmlReader.Name == "TRX" || xmlReader.Name == "TRXR"))
                            {
                                csvWriter.Write('"');

                                for (int i = 0; i < templateList.Count; i++)
                                {
                                    csvWriter.Write(xmlReader.MoveToAttribute(templateList[i]) ? xmlReader.Value.Replace(",", string.Empty) : null);

                                    if (i < templateList.Count - 1)
                                        csvWriter.Write("\",\"");
                                }
                                csvWriter.WriteLine('"');
                            }
                        }
                        csvWriter.Flush();
                        memoryStream.Position = 0;
                        using (StreamReader streamReader = new StreamReader(memoryStream))
                        {
                            while ((streamReader.ReadLine()) != null)
                            {
                                //Parse your csv line by line
                            }
                        }
                    }
                }
            }

【讨论】:

    【解决方案2】:

    您可以先使用 Linq to XML 获取信息,然后对其进行处理以将其转换为 csv,如下所示:

    XElement students= XElement.Load("YourXml.xml");
    string csv =
        (from el in students.Elements()
         where el.Name!="TRXC"
         select
            String.Format("{0},{1},{2},{3},{4},{5},{6}",
                (string)el.Attribute("ID"),
                (string)el.Attribute("Name"),
                (string)el.Attribute("Address"),
                (string)el.Attribute("Phone"),
                (string)el.Attribute("Email"),
                (string)el.Attribute("Gender"),
                Environment.NewLine
            )
        )
        .Aggregate(
            new StringBuilder(),
            (sb, s) => sb.Append(s),
            sb => sb.ToString()
        );
     string header="ID,"+""+"Name,"+"Address,"+"Phone,"+"Email,"+"Gender,"+ Environment.NewLine;
     File.WriteAllText("yourCSV.csv", header+csv);
    

    StringBuilder 将帮助您有效地构造结果。如果您尝试逐个元素连接,您将在每次应用该操作时创建一个新的string,这可能会由于您的元素数量而影响您的性能。我建议您阅读此Jon Skeet's article 了解更多详情

    更新 1

    您也可以这样做来创建您的标题:

    var templeteList = new List<string> //Consider this as my template
            {
               "ID",
               "Name",
               "Address",
               "Phone",
               "Email",
               "Gender",
               Environment.NewLine    
            };
    var header=String.Joing(",",templateList);
    

    更新 2

    要动态地做同样的事情,你需要稍微改变一下我之前的查询:

    var templeteList = new List<string> //Consider this as my template
            {
               "ID",
               "Name",
               "Address",
               "Phone",
               "Email",
               "Gender"     
            };
    string csv =students.Elements()
                        .Where(el=>el.Name!="TRXC")
                        .Select(el=>{
                                      string result="";
                                      for(int i=0; i<templeteList.Count;i++)
                                      {
                                        result+=((string)el.Attribute(templateList[i])) +",";
                                      }
                                      result+= Environment.NewLine;
                                      return result;
                                    })
                        .Aggregate(new StringBuilder(),
                                   (sb, s) => sb.Append(s),
                                    sb => sb.ToString()
                                   );
    

    【讨论】:

    • 感谢您的回答,但不幸的是,输出不包含标题,并且它仅返回“ID”值,并且所有剩余列为空白2,,,,,, 2,,,,,, 1,,,,,, 3,,,,,,
    • 你快到了,但在上面的例子中,你用"{0},{1},{2},{3},{4},{5},{6}"硬编码了String.Format,并且还硬编码了它的属性(string)el.Attribute("ID"), (string)el.Attribute("Name"),。这应该来自于 templateList 动态。它可以是运行时的列数。
    • 我的朋友,我想我教你如何钓鱼,通过我的解决方案,你应该能够创造出你需要的东西。不管怎样,我稍后会尝试;)
    • 正如我在问题的第一行中提到的那样我是 C# 新手,这是我的学习曲线。
    • @SmitaKop​​arkar,给你
    【解决方案3】:

    XmlReader 是解析 XML 的最快方式。您没有使用XmlReader 显示您的代码,但您可能做错了什么。不过,我敢打赌,这种方法是最快的。

    试试这个代码:

    var templateList = new List<string> { "ID", "Name", "Address", "Phone", "Email", "Gender" };
    
    using (var xmlReader = XmlReader.Create("test.xml"))
    using (var csvWriter = new StreamWriter("test.csv"))
    {
        csvWriter.WriteLine("\"" + string.Join("\",\"", templateList) + "\"");
        xmlReader.MoveToContent();
    
        while (xmlReader.Read())
        {
            if (xmlReader.NodeType == XmlNodeType.Element
                && xmlReader.Name != "TRXC")
            {
                csvWriter.Write('"');
    
                for (int i = 0; i < templateList.Count; i++)
                {
                    if (xmlReader.MoveToAttribute(templateList[i]))
                        csvWriter.Write(xmlReader.Value);
                    else
                        csvWriter.Write("NULL");
    
                    if (i < templateList.Count - 1)
                        csvWriter.Write("\",\"");
                }
                csvWriter.WriteLine('"');
            }
        }
    }
    

    测试代码并报告结果。如果它的性能会很慢(确定吗?),我们可以尝试使用NameTable 来加速它。


    你的最终目标是什么?这很奇怪:首先解析 xml,创建一个 csv,然后立即解析 csv,创建其他内容。

    好的,看看这段代码:

    IEnumerable<string[]> ParseXml()
    {
        var templateList = new List<string> { "ID", "Name", "Address", "Phone", "Email", "Gender" };
    
        using (var xmlReader = XmlReader.Create("test.xml"))
        {
            yield return templateList.ToArray(); // header
            xmlReader.MoveToContent();
    
            while (xmlReader.Read())
            {
                if (xmlReader.NodeType == XmlNodeType.Element
                    && xmlReader.Name != "TRXC") // exclude TRXC
                {
                    string[] result = new string[templateList.Count];
    
                    for (int i = 0; i < templateList.Count; i++)
                    {
                        if (xmlReader.MoveToAttribute(templateList[i]))
                            result[i] = xmlReader.Value;
                        else
                            result[i] = "NULL";
                    }
                    yield return result; // each row
                }
            }
        }
    }
    

    用途:

    foreach (string[] row in ParseXml())
    {
        // process row
        // or
        foreach (string value in row)
        {
            // process value
        }
    }
    

    我们不创建中间 csv。我们在 xml 解析时立即处理每个字符串。

    【讨论】:

    • 我们可以将数据放入StreamReader而不是写入文件新文件。我不想创建单独的 CSV 文件,我只想转换它并使用 StreamReader 的 Readline 方法逐行读取?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-25
    • 2021-08-12
    • 1970-01-01
    • 2021-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多