【问题标题】:For Loop in Decendents (Sequence contains no Element ) c#后裔中的 For 循环(序列不包含元素)c#
【发布时间】:2017-12-13 18:27:53
【问题描述】:

我有一个复杂的 XML 文件,我想从特定的事务标签中检索值。最后,我需要将这些值保存到 CSV 文件中。

我正在尝试在它所在的每个事务标签中检索下面的值。

<szCustomerID>2988880562567</szCustomerID>

   <szCustomerName>130808125028SHM </szCustomerName>

   <szExternalID>3177@06/07/17</szExternalID>

注意所有的交易标签都不一样
不幸的是,它正在循环所有事务标签,我得到错误序列不包含元素

有没有一种方法可以让我循环使用后代并总是跳过第二个交易标签,因为我不需要它

  try
        {
            string strSource_voucher = @"C:\\TempFid";

                  string[] files = Directory.GetFiles(strSource_voucher, "*.xml", SearchOption.AllDirectories);

            if (files.Length > 0) // Check if files has values
            {
                StringBuilder dataToBeWritten = new StringBuilder();


                foreach (string file in files)
                {

                    dataToBeWritten.Append("szCustomerID");
                    dataToBeWritten.Append(",");
                    dataToBeWritten.Append("szCustomerName");
                    dataToBeWritten.Append(",");
                    dataToBeWritten.Append("szExternalID");
                    dataToBeWritten.Append(",");

                    dataToBeWritten.Append(Environment.NewLine);

                    XDocument xDocument = XDocument.Load(file);


                    int results = xDocument.Descendants("Transaction").Count();

                    foreach (var trans in xDocument.Descendants("Transaction"))

                    {
                           //var trans = xDocument.Descendants("Transaction");

                           var val1 = (string)trans.Descendants("Set").Elements("szCustomerID").First();

                            var val2 = (string)trans.Descendants("Set").Elements("szCustomerName").First();


                            var val3 = (string)trans.Descendants("Set").Elements("szExternalID").First();

                            dataToBeWritten.Append(val1);
                            dataToBeWritten.Append(",");

                            dataToBeWritten.Append(val2);
                            dataToBeWritten.Append(",");

                            dataToBeWritten.Append(val3);
                            dataToBeWritten.Append(",");

                            dataToBeWritten.Append(Environment.NewLine);


                    } // End of For each var trans


                    Console.WriteLine(dataToBeWritten.ToString());

                    Console.ReadLine();

【问题讨论】:

    标签: c# xml csv xml-parsing


    【解决方案1】:

    您可以使用Element 代替Elements 并检查该值是否不是null

    foreach (var trans in xDocument.Descendants("Transaction"))
    {
        XElement setElement = trans.Descendants("Set").FirstOrDefault();
        if (setElement != null)
        {
            var val1 = (string)setElement.Element("szCustomerID");
            var val2 = (string)setElement.Element("szCustomerName");
            var val3 = (string)setElement.Element("szExternalID");
    
            if (val1 != null && val3 != null && val3 != null)
            {
                dataToBeWritten.Append(val1);
                dataToBeWritten.Append(",");
    
                dataToBeWritten.Append(val2);
                dataToBeWritten.Append(",");
    
                dataToBeWritten.Append(val3);
                dataToBeWritten.Append(",");
    
                dataToBeWritten.Append(Environment.NewLine);
            }
        }
    }
    

    【讨论】:

    • 他在每个Transaction 中都有Set 元素和其他一些元素。我认为这个 If 将永远返回 true
    • @SamvelPetrosov 此代码中没有任何内容返回 true。如果存在则返回元素,如果不存在则返回null
    • if (setElement != null) 这将永远是true
    • @SamvelPetrosov 好的,我明白你的意思。在 xml 中,OP 提供的每个 Transaction 中都有 Set 标签,但是 OP 确实说其中一些有不同的结构。如果Set 在没有检查的情况下在某处丢失,他将获得异常。
    • @SamvelPetrosov 有两个检查,Set 中的 setElement != nullif (val1 != null &amp;&amp; val3 != null &amp;&amp; val3 != null) 中的值。 setElement.Element 将返回节点(如果找到)或 null
    【解决方案2】:

    只需使用for 循环并将i 增加2,如下所示:

    var transactions = xDocument.Descendants("Transaction").ToList();
    for (int i = 0; i < transactions.Count(); i += 2)
    {
    
        //var trans = xDocument.Descendants("Transaction");
        var val1 = (string)transactions[i].Descendants("Set").Elements("szCustomerID").First();
        var val2 = (string)transactions[i].Descendants("Set").Elements("szCustomerName").First();
        var val3 = (string)transactions.Descendants("Set").Elements("szExternalID").First();
    
        dataToBeWritten.Append(val1);
        dataToBeWritten.Append(",");
    
        dataToBeWritten.Append(val2);
        dataToBeWritten.Append(",");
    
        dataToBeWritten.Append(val3);
        dataToBeWritten.Append(",");
    
        dataToBeWritten.Append(Environment.NewLine);
    } // End of For each var trans
    

    【讨论】:

    • 有没有办法可以在 csv 文件中分离 szExternalID 的内容?例如我需要 3177 和 06/07/17 在单独的列中?
    • @LBoodia 如果你用'@'分割 val3,你会得到包含 3177 作为第一个元素和 06/07/17 作为第二个元素的数组,那么你只需要单独编写它们
    【解决方案3】:

    如果您愿意使用库(Cinchoo ETL - 一个开源 ETL 库),还有另一种方法可以进行这种类型的转换

    基于流,只需几行代码即可快速将任何 xml 转换为 csv 格式。

    我相信您想从您的 xml 中生成仅包含 CUSTOMER 表元素的 CSV 文件。

    这里是你可以如何使用这个库

    using (var parser = new ChoXmlReader("sample.xml").WithXPath("/UpdateDB/Transaction")
        .WithField("Table", xPath: "/Insert/Table")
        .WithField("szCustomerID", xPath: "/Insert/Set/szCustomerID")
        .WithField("szCustomerName", xPath: "/Insert/Set/szCustomerName")
        .WithField("szExternalID", xPath: "/Insert/Set/szExternalID")
        )
    {
        using (var writer = new ChoCSVWriter("sample.csv").WithFirstLineHeader())
            writer.Write(parser.Where(r => r.Table == "CUSTOMER").Select(r => new { szCustomerID = r.szCustomerID, szCustomerName = r.szCustomerName, szExternalID = r.szExternalID }));
    }
    

    输出如下所示

    szCustomerID,szCustomerName,szExternalID
    2988880562567,130808125028SHM,3177@06/07/17
    12345,130808125028SHM,3177@06/07/17
    

    更新:

    为了将 szExternalID 拆分为 2 列(szExternalID、szExternalDate),您必须使用 valueconverter 来解析和加载它们。示例展示了如何

    using (var parser = new ChoXmlReader("sample.xml").WithXPath("/UpdateDB/Transaction")
        .WithField("Table", xPath: "/Insert/Table")
        .WithField("szCustomerID", xPath: "/Insert/Set/szCustomerID")
        .WithField("szCustomerName", xPath: "/Insert/Set/szCustomerName")
        .WithField("szExternalID", xPath: "/Insert/Set/szExternalID", valueConverter: (v) => ((string)v).Split('@')[0])
        .WithField("szExternalDate", xPath: "/Insert/Set/szExternalID", valueConverter: (v) => ((string)v).Split('@')[1])
        )
    {
        using (var writer = new ChoCSVWriter("sample.csv").WithFirstLineHeader())
            writer.Write(parser.Where(r => r.Table == "CUSTOMER").Select(r => new { szCustomerID = r.szCustomerID, szCustomerName = r.szCustomerName, szExternalID = r.szExternalID, szExternalDate = r.szExternalDate }));
    }
    

    新的输出如下所示

    szCustomerID,szCustomerName,szExternalID,szExternalDate
    2988880562567,130808125028SHM,3177,06/07/17
    12345,130808125028SHM,3177,06/07/17
    

    披露:我是这个库的作者。

    【讨论】:

    • 有没有办法可以在 csv 文件中分离 szExternalID 的内容?例如我需要 3177 和 06/07/17 在单独的列中
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-19
    • 1970-01-01
    • 2010-09-21
    相关资源
    最近更新 更多