简体   繁体   English

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

[英]Serializing a list of dynamic objects to a CSV with ServiceStack.Text

All of my EF classes have a Projection() method that helps me choose what I want to project from the class to the SQL queries: 我的所有EF类都有一个Projection()方法,可以帮助我选择从项目到SQL查询的项目:

Example: 例:

    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
            }
            ...
        };
    }

I use these projection methods like this: 我使用这样的投影方法:

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

This way lets me reuse my projections all around my project. 通过这种方式,我可以在项目周围重复使用我的投影。

I want to use ServiceStack.Text to serialize these lists to CSV. 我想使用ServiceStack.Text将这些列表序列化为CSV。

I'm doing this: 我这样做:

 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");

But the result is an empty csv (csvString is full of "\\r\\n"'s and nothing more) 但结果是一个空的csv(csvString充满了“\\ r \\ n”而且仅此而已)

Questions: 问题:

  • Am I using correctly the SerializeToCsv() method? 我是否正确使用了SerializeToCsv()方法?
  • Can I use <dynamic> in this library? 我可以在这个库中使用<dynamic>吗?
  • Suggestions for achieving my purpose? 为实现我的目的的建议?

For the example above the desired CSV would flatten all the properties, something like: 对于上面的示例,所需的CSV将展平所有属性,例如:

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

I will answer my own questions, but will not mark as accepted in hope of a new greater answer.. 我会回答我自己的问题,但不会被接受,因为希望有更大的答案。

Am I using correctly the SerializeToCsv() method? 我是否正确使用了SerializeToCsv()方法? Can I use dynamic in this library? 我可以在这个库中使用dynamic吗?

Answer: Yes to both but ServiceStack.Text v4.0.36 is needed (which is available at MyGet, not Nuget) 答:两者都是,但需要ServiceStack.Text v4.0.36(可在MyGet上找到,而不是Nuget)

Suggestions for achieving my purpose? 为实现我的目的的建议?

Answer: With ServiceStack.Text it is possible to serialize to CSV a List<dynamic> , but all the nested properties will be rendered as JSON and they will not be flattened out, eg: 答:使用ServiceStack.Text ,可以将List<dynamic>序列化为CSV List<dynamic> ,但是所有嵌套属性都将呈现为JSON,并且它们不会被展平,例如:

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

   string csv = CsvSerializer.SerializeToCsv(list);

This list will be serialized to this csv: 此列表将序列化为此csv:

name, pet 名字,宠物
"john", { name = "doggy" } “john”,{name =“doggy”}

And not to this csv, as I was expecting: 而不是这个csv,正如我所料:

name, pet_name name,pet_name
"john", "doggy" “约翰”,“狗狗”

So... I finally ended up writing this code: 所以...我终于写完了这段代码:

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));
        }
    }
}

What this does is: 这样做是:

  1. It flattens the List, so that all the properties of the objects in the list are primitives (eg: no nested properties) 它使List变平,因此列表中对象的所有属性都是基元(例如:没有嵌套属性)

  2. It creates a CSV from that flattened list. 它从该展平列表中创建CSV。

This algorithm is O(n*m), being 该算法为O(n * m)
n: number of items in the list n:列表中的项目数
m: number of properties inside each item (including nested properties) m:每个项目内的属性数量(包括嵌套属性)

Whilst the recommendation is to use clean POCO's for Serialization, you can serialize in-line anonymous types, eg: 虽然建议使用干净的POCO进行序列化,但您可以序列化内联匿名类型,例如:

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

Otherwise I've just added support for serializing IEnumerable<object> and IEnumerable<dynamic> in this commit . 否则我刚刚添加了对在此提交中序列化IEnumerable<object>IEnumerable<dynamic>

This change is available from v4.0.36+ that's now available on MyGet . 此更改可从v4.0.36 +获得 ,现在可在MyGet上使用

I believe that part of what is going on is that the library is using properties in the object to determine what to serialize . 我相信正在发生的部分是库正在使用对象中的属性来确定要序列化的内容 I believe that a dynamic object does not construct properties like it's a POCO. 我相信动态对象构造像POCO这样的属性。 If you don't setup a getter and setter on your object, it certainly won't work. 如果你没有在你的对象上设置gettersetter ,那肯定是行不通的。 Just for reference, I using version 4.5.6.0 of this library. 仅供参考,我使用的是此库的4.5.6.0版。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM