使用ServiceStack.Text将动态对象列表序列化为CSV

时间:2015-01-01 20:06:16

标签: c# csv servicestack servicestack-text

我的所有EF类都有一个Projection()方法,可以帮助我选择我想要从类投影到SQL查询的内容:

示例:

    public static Expression<Func<Something, dynamic>> Projection()
    {
        return e => new
        {
            something = e.SomethingId,
            name = e.Name,
            requiredThingId = e.RequiredThingId,
            requiredThing = new
            {
                requiredThingId = e.RequiredThing.RequiredThingId,
                name = e.RequiredThing.Name,
                ...
            },
            optionalThingId = e.OptionalThingId,
            optionalThing = e.OptionalThingId == null ? null : new 
            {
                optionalThingId = e.OptionalThing.OptionalThingId,
                name = e.OptionalThing.Name
            }
            ...
        };
    }

我使用这样的投影方法:<​​/ p>

  List<dynamic> projected = dbContext.Something.Select(Something.Projection()).ToList();

通过这种方式,我可以在项目周围重复使用我的投影。

我想使用 ServiceStack.Text 将这些列表序列化为CSV。

我这样做:

 string csvString = CsvSerializer.SerializeToCsv<dynamic>(Something.Select(Something.Projection()).ToList());

 byte[] csvBytes = System.Text.Encoding.Unicode.GetBytes(csvString);

 return File(csvBytes, "text/csv", "foo.csv");

但是结果是一个空的csv(csvString充满了“\ r \ n”,而且仅此而已)

问题:

  • 我是否正确使用了SerializeToCsv()方法?
  • 我可以在此库中使用<dynamic>吗?
  • 达到目的的建议?

对于上面的示例,所需的CSV将展平所有属性,例如:

  "somethingId";"name";"requiredThingId";"requiredThing.requiredThingId";"requiredThing.name";"optionalThingId";"optionalThing.optionalThingId";"optionalThing.name"

3 个答案:

答案 0 :(得分:2)

我会回答我自己的问题,但不会被接受为希望得到更大的答案。

  

我是否正确使用了SerializeToCsv()方法?我可以使用动态   在这个图书馆?

答案:两者都是,但需要ServiceStack.Text v4.0.36(可在MyGet上找到,而不是Nuget)

  

实现目标的建议?

答案:使用ServiceStack.Text,可以将List<dynamic>序列化为CSV,但所有嵌套属性都将呈现为JSON,并且不会被展平,例如:

   List<dynamic> list = new List<dynamic>();
   list.Add(new
   {
         name = "john", 
         pet = new 
         { 
              name = "doggy"
         }
   });

   string csv = CsvSerializer.SerializeToCsv(list);

此列表将序列化为此csv:

  

姓名,宠物
  &#34; john&#34;,{name =&#34; doggy&#34; }

而不是像我期待的那样对csv:

  

name,pet_name
  &#34; john&#34;,&#34;狗狗&#34;

所以......我最终写完了这段代码:

public class CsvHelper
{
    public static string GetCSVString(List<dynamic> inputList)
    {
        var outputList = new List<Dictionary<string, object>>();

        foreach (var item in inputList)
        {
            Dictionary<string, object> outputItem = new Dictionary<string, object>();
            flatten(item, outputItem, "");

            outputList.Add(outputItem);
        }

        List<string> headers = outputList.SelectMany(t => t.Keys).Distinct().ToList();

        string csvString = ";" + string.Join(";", headers.ToArray()) + "\r\n";

        foreach (var item in outputList)
        {
            foreach (string header in headers)
            {
                if (item.ContainsKey(header) && item[header] != null)
                    csvString = csvString + ";" + item[header].ToString();
                else
                    csvString = csvString + ";";
            }

            csvString = csvString + "\r\n";
        }

        return csvString;
    }

    private static void flatten(dynamic item, Dictionary<string, object> outputItem, string prefix)
    {
        if (item == null)
            return;

        foreach (PropertyInfo propertyInfo in item.GetType().GetProperties())
        {
            if (!propertyInfo.PropertyType.Name.Contains("AnonymousType"))
                outputItem.Add(prefix + "__" + propertyInfo.Name, propertyInfo.GetValue(item));
            else
                flatten(propertyInfo.GetValue(item), outputItem, (prefix.Equals("") ? propertyInfo.Name : prefix + "__" + propertyInfo.Name));
        }
    }
}

这是做什么的:

  1. 它使List变平,因此列表中对象的所有属性都是基元(例如:没有嵌套属性)

  2. 它会从该展平列表中创建一个CSV。

  3. 该算法为O(n * m),为
    n:列表中的项目数
    m:每个项目内的属性数量(包括嵌套属性)

答案 1 :(得分:0)

虽然建议使用干净的POCO进行序列化,但您可以序列化内联匿名类型,例如:

var somethings =  dbContext.Something.Select(e => new { 
    something = e.SomethingId, 
    name = e.Name 
});
somethings.ToCsv().Print();

否则,我刚刚添加了对序列化IEnumerable<object>IEnumerable<dynamic> in this commit的支持。

此更改可从 v4.0.36 + 获得,现在为available on MyGet

答案 2 :(得分:0)

我认为正在发生的一部分是库正在使用对象中的属性来确定序列化的内容。我认为动态对象构造属性,就像它是POCO一样。如果您未在对象上设置gettersetter,则肯定无法正常工作。仅供参考,我使用的是该库的4.5.6.0版本。