【问题标题】:Creating csv file using CsvHelper when number of headers are not fixed当标题数量不固定时使用 CsvHelper 创建 csv 文件
【发布时间】:2019-07-16 19:39:24
【问题描述】:

我有 json 对象的列表。每个 json 对象都具有公共属性和该 json 对象独有的一些属性。喜欢下面的示例

{"FirstName":"foo","LastName":"bar"}
{"FirstName":"jhon","LastName":"dow"}
{"FirstName":"james","LastName":"smith","Age":26}
{"LastName":"jones","Age":30, "Address":"1234 Test Drive"}

我想使用 CsvHelper 创建 CSV 报告,其中每个属性都是标题。当属性不存在时,该列的值应该为空

这是我目前的实现,当然没有考虑额外的属性

var records = new List<dynamic>();
foreach (var jObj in result)
{
   var record = new ExpandoObject();
   foreach (var property in jObj)
   {
      record.TryAdd(property.Key, property.Value.ToString());
   }

   records.Add(record);
}

using (var writer = new StreamWriter(filePath))
{
    using (var csv = new CsvWriter(writer))
    {
        csv.WriteRecords(records);
    }
}

所以在上面的例子中,生成的 csv 应该有 4 个标题 FirstName,LastName,AgeAddress

请注意,记录数可能以千为单位

更新 1
以下是我的临时解决方案,直到我找到更好的方法来做到这一点而无需循环两次

    [Fact]
    public async Task CreateCSVFromJObjects()
    {
        // arrange
        var list = new JObject[]
        {
            JObject.FromObject(new { FirstName = "foo",LastName = "bar" }),
            JObject.FromObject(new { FirstName = "john",LastName = "doe" }),
            JObject.FromObject(new { FirstName = "james",LastName = "smith", Age = 26 }),
            JObject.FromObject(new { LastName = "bar", Address = "123 Test Drive" })
        }.ToList();


        //act
        var headers = new HashSet<string>();
        foreach (var j in list)
        {
            foreach (var p in j)
            {
                if (!headers.Contains(p.Key))
                {
                    headers.Add(p.Key);
                }
            }
        }


        using (var writer = new StreamWriter("C:\\temp\\test.csv"))
        {
            using (var csv = new CsvWriter(writer))
            {
                foreach (var header in headers)
                {
                    csv.WriteField(header);
                }
                await csv.NextRecordAsync();

                foreach (var jObj in list)
                {
                    foreach (var header in headers)
                    {
                        var token = jObj[header];
                        if (token != null)
                        {
                            csv.WriteField(token.ToString());
                        }
                        else
                        {
                            csv.WriteField("");
                        }
                    }

                    await csv.NextRecordAsync();
                }
            }
        }
    }

【问题讨论】:

  • 你知道所有可能的标题吗?
  • 未知.. 除非我先循环所有 JObjects。这将导致循环两次
  • 你能将 json 反序列化为 JObject 以外的类型吗?编写对象集合是可行的,因为属性名称成为标题。
  • 对象未知。我们收集了我转换为 JObjects 的 json 字符串。每个 JObject 中的属性数量不会超过 10 个,但 JObject 的数量可能以数千为单位
  • @LP13 显示的示例是否准确地提供数据?

标签: c# .net .net-core csvhelper


【解决方案1】:

如果结果不必通过处理实时流式传输,则在与流写入相同的循环内创建和维护标头集和标头列表可能更有效,跳过标头- 首先写入流。然后你可以建立一个新的流,写入标题,然后将原始流复制到其中。

是否可以使用内存流进行中间写入取决于您的内存需求。

每个请求的伪代码。这实际上只是对现有代码的一个小重新排列。它需要更少的循环,但需要更多的内存或磁盘。问题的性质需要权衡。

Stream intermediate_stream // memory or file
List headers // only add a set if List is a bottleneck (1000s of properties); list required to maintain ordering
foreach ( obj in list ) :
  foreach ( prop_name in obj.props )
    headers.add_if_unique(prop_name)
  foreach ( name in headers ) // to preserve ordering in output
   intermediate_stream.write(obj.prop_value(name))

Stream final_stream
final_stream.write(headers)
intermediate_stream.copy_to(final_stream)

【讨论】:

  • 你能给我一个例子,不确定我明白了吗..paedo 代码可以工作
猜你喜欢
  • 1970-01-01
  • 2017-10-05
  • 2021-10-02
  • 1970-01-01
  • 2021-09-11
  • 1970-01-01
  • 2018-11-18
  • 2017-07-28
  • 1970-01-01
相关资源
最近更新 更多