【问题标题】:XML like data to CSV Conversion类似 XML 的数据到 CSV 的转换
【发布时间】:2015-06-05 14:18:45
【问题描述】:

所以我有一个设备,它有一个内置的记录程序,它会生成有关设备的状态消息并不断将它们推送到.txt 文件。这些消息包括有关设备状态、网络状态等许多信息。文件中的数据如下所示:

 <XML><DSTATUS>1,4,7,,5</DSTATUS><EVENT> hello,there,my,name,is,jack,</EVENT>
     last,name,missing,above <ANOTHERTAG>3,6,7,,8,4</ANOTHERTAG> </XML>

 <XML><DSTATUS>1,5,7,,3</DSTATUS><EVENT>hello,there,my,name,is,mary,jane</EVENT>
     last,name,not,missing,above<ANOTHERTAG>3,6,7,,8,4</ANOTHERTAG></XML>

    ... goes on

请注意,它不是格式良好的 XML。此外,一个元素可以有多个参数,也可以有空格...例如:&lt;NETWORKSTAT&gt;1,456,3,6,,7&lt;/NETWORKSTAT&gt; 我的目标是在 C# WPF 中编写一些东西,它将获取这个文本文件,处理其中的数据并创建一个 .csv 文件,每行包含每个事件。 例如,对于上面给出的简短示例,csv 文件中的第一行将是:

1,4,7,,5,hello,there,my,name,is,jack,,last,name,missing,above,3,6,7,,8,4

另外,我不需要帮助使用基本的 C#。我知道如何读取文件等。但我不知道如何在解析、处理和转换方面解决这个问题。我对 C# 还很陌生,所以我不确定该往哪个方向发展。任何帮助将不胜感激!

【问题讨论】:

标签: c# xml parsing csv xml-parsing


【解决方案1】:

由于文件中的每个顶级 XML 节点都是格式正确的,因此您可以使用 XmlReaderXmlReaderSettings.ConformanceLevel = ConformanceLevel.Fragment 来遍历文件中的每个顶级节点并使用 Linq-to-XML 读取它:

    public static IEnumerable<string> XmlFragmentsToCSV(string path)
    {
        using (var textReader = new StreamReader(path, Encoding.UTF8))
            foreach (var line in XmlFragmentsToCSV(textReader))
                yield return line;
    }

    public static IEnumerable<string> XmlFragmentsToCSV(TextReader textReader)
    {
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.ConformanceLevel = ConformanceLevel.Fragment;

        using (XmlReader reader = XmlReader.Create(textReader, settings))
        {
            while (reader.Read())
            {   // Skip whitespace
                if (reader.NodeType == XmlNodeType.Element) 
                {
                    using (var subReader = reader.ReadSubtree())
                    {
                        var element = XElement.Load(subReader);
                        yield return string.Join(",", element.DescendantNodes().OfType<XText>().Select(n => n.Value.Trim()).Where(t => !string.IsNullOrEmpty(t)).ToArray());
                    }
                }
            }
        }
    }

为了精确匹配您想要的输出,我必须在每个文本节点值的开头和结尾修剪空格。

另外,Where(t =&gt; !string.IsNullOrEmpty(t)) 子句是跳过此处空格对应的空白节点:&lt;/ANOTHERTAG&gt; &lt;/XML&gt;。如果实际文件中不存在该空间,则可以省略该子句。

【讨论】:

    【解决方案2】:

    由于非标准格式,不得不从 XML Linq 解决方案切换到标准 XML 解决方案。 Linq 不支持不在标签中的 TEXT 字符串。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;
    using System.Xml;
    using System.Xml.Linq;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            const string FILENAME = @"c:\temp\test.csv";
            static void Main(string[] args)
            {
                string input =
                    "<XML><DSTATUS>1,4,7,,5</DSTATUS><EVENT> hello,there,my,name,is,jack,</EVENT>" +
                       "last,name,missing,above <ANOTHERTAG>3,6,7,,8,4</ANOTHERTAG> </XML>" +
    
                    "<XML><DSTATUS>1,5,7,,3</DSTATUS><EVENT>hello,there,my,name,is,mary,jane</EVENT>" +
                       "last,name,not,missing,above<ANOTHERTAG>3,6,7,,8,4</ANOTHERTAG></XML>";
    
                input = "<Root>" + input + "</Root>";
    
                XmlDocument  doc = new XmlDocument();
                doc.LoadXml(input);
    
                StreamWriter writer = new StreamWriter(FILENAME);
    
                XmlNodeList rows = doc.GetElementsByTagName("XML");
    
                foreach (XmlNode row in rows)
                {
                    List<string> children = new List<string>();
                    foreach (XmlNode child in row.ChildNodes)
                    {
                        children.Add(child.InnerText.Trim());
                    }
    
                    writer.WriteLine(string.Join(",", children.ToArray()));
                }
    
                writer.Flush();
                writer.Close();
    
            }
        }
    }
    ​
    

    【讨论】:

    • 完美运行。非常感谢!
    【解决方案3】:

    这是我使用 XML Linq 的解决方案。我通过使用 Root 标记包装片段来创建 XDocument。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;
    using System.Xml;
    using System.Xml.Linq;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            const string FILENAME = @"c:\temp\test.csv";
            static void Main(string[] args)
            {
                string input =
                    "<XML><DSTATUS>1,4,7,,5</DSTATUS><EVENT> hello,there,my,name,is,jack,</EVENT>" +
                       "last,name,missing,above <ANOTHERTAG>3,6,7,,8,4</ANOTHERTAG> </XML>" +
    
                    "<XML><DSTATUS>1,5,7,,3</DSTATUS><EVENT>hello,there,my,name,is,mary,jane</EVENT>" +
                       "last,name,not,missing,above<ANOTHERTAG>3,6,7,,8,4</ANOTHERTAG></XML>";
    
                input = "<Root>" + input + "</Root>";
    
                XDocument doc = XDocument.Parse(input);
    
                StreamWriter writer = new StreamWriter(FILENAME);
    
                List<XElement> rows = doc.Descendants("XML").ToList();
    
                foreach (XElement row in rows)
                {
                    string[] elements = row.Elements().Select(x => x.Value).ToArray();
                    writer.WriteLine(string.Join(",", elements));
                }
    
                writer.Flush();
                writer.Close();
    
            }
        }
    }
    ​
    

    【讨论】:

    • jdweng,谢谢。你的方法完美无缺。但是有一个问题我没有在我的帖子中明确讨论...如果您查看我的示例日志数据,“last,name,missing,above”以下数据未嵌入任何标签中,因此目前您的方法只是忽略这 4 个单词,但是我需要将它们也包含在我的 csv 数据中。
    • 非常感谢您的帮助
    • 上述解决方案使用了不支持 Text not inside 标签的 XML Linq。发布在下面的新解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-15
    • 2018-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多