代码之家  ›  专栏  ›  技术社区  ›  lenny

MVC.Net核心-将数据库内容导出为.csv文件

  •  0
  • lenny  · 技术社区  · 7 年前

    在我的应用程序中,我接收JSON数据,但从不知道它将包含哪些字段。我要做的是将JSON字符串列表转换为.csv文件,并将其作为文件结果返回。棘手的是,我永远不知道JSON将包含什么或多少字段。它可以是每个对象一个字段,也可以是几个字段,其名称我无法预测。我找到的所有解决方案都是针对set对象结构的,您可以将JSON解析为与JSON结构匹配的c#类。

    有没有一种方法可以轻松地将JSON解析为动态对象,然后将其序列化为CSV?感谢您的帮助。

    提前谢谢

    我发现 this PropertyInfo dynamic

    编辑2

    好吧,所以我没有使用 动态 Dictionary<string, string> sep= 在第一行。我还没有发现一个CSV序列化程序在没有对象的情况下工作。为什么这么复杂?

    2 回复  |  直到 6 年前
        1
  •  2
  •   itminus    7 年前

    因为您假设属性是简单属性,所以我们可以简单地将json的属性视为csv中的字段。

    Row 作为 SortedDictionary<string,string>

    using Row =SortedDictionary<string,string>; 
    

    我还编写了一个助手类来将json导出到csv。

    public class JsonToCsvExporter{
    
        public JsonToCsvExporter(string json,string sep=","){
            this._json = json;
            this.Sep = sep;
            this.Rows = new List<Row>();
            this.Headers = new List<string>();
            this.Initialize(json);
        }
    
        private string _json ;
    
        public IList<Row> Rows{get;set;}
        public IList<string> Headers { get; set; }
        public string Sep {get;set;}=",";
    
        private void Initialize(string json){
            var o = JArray.Parse(json);
            this.BuildRows(o, null);
            this.Headers = this.Rows.FirstOrDefault().Keys.ToList();
            this.NormailizeRows();
        }
        private void BuildRows(IEnumerable<JToken> tokens, Row row){
            if(row == null){ row = new Row(); }
            foreach( var token in tokens){
                if (token.Type == JTokenType.Property)
                {
                    JProperty prop = (JProperty)token;
                    if (!prop.Value.HasValues){
                        row.Add(prop.Name,prop.Value.ToString());
                    }
                }
                // if it is not a `JProperty`, they shoud have children,
                //     that means it shoud be treated as a brand new line 
                else if (token.HasValues){
                    var _row = new Row();
                    BuildRows(token.Children(),_row);
                }
            }
            // if current row has fields, add this row
            if (row.Count>0) {
                this.Rows.Add(row);
            }
        }
    
        // add null for unspecified values
        private void NormailizeRows() {
            foreach (var row in Rows) {
                foreach (var header in Headers) {
                    if (!row.ContainsKey(header)) {
                        row.Add(header,null);
                    }
                }
            }
        }
    
        private async Task ForEach<T>(IEnumerable<T> items,Func<T,Task> funcForFirst,Func<T,Task> funcForEach ){
            if(funcForFirst== null ){ throw new ArgumentNullException(nameof(funcForFirst));}
            if(funcForEach== null ){ throw new ArgumentNullException(nameof(funcForEach));}
    
            var iter = items.GetEnumerator();
            var flag= iter?.MoveNext();
            if(flag==false){ throw new Exception("items MUST have at least one element");}
    
            await funcForFirst(iter.Current);
    
            while(iter.MoveNext()!= false){
                await funcForEach(iter.Current);
            }
        }
    
        public async Task ExportHeader(StreamWriter writer){
            await this.ForEach(this.Headers,
                async header=>{
                    await writer.WriteAsync(header);
                },
                async header=>{
                    await writer.WriteAsync(this.Sep);
                    await writer.WriteAsync(header);
                }
            );
            await writer.WriteLineAsync();
        }
    
        public async Task ExportBody(StreamWriter writer)
        {
            foreach (var row in this.Rows) {
                await this.ForEach(row,
                    async f=>{
                        await writer.WriteAsync(f.Value);
                    },
                    async f=>{
                        await writer.WriteAsync(this.Sep);
                        await writer.WriteAsync(f.Value);
                    }
                );
                await writer.WriteLineAsync();
            }
        }
    
    }
    

    static void Main(string[] args)
    {
        var json =@"[{
            'F1': 'hello1',
            'F2': 'world1',
            'F3': 'foo1',
            'F4': 'bar2',
        },{
            'F1': 'Hello2',
            'F4': 'Bar2',
        },{
            'F1': 'Hello3',
            'F2': 'World3',
            'F3': null,
            'F4': 'Bar3',
        }]";
        var fs= new FileStream("xxxx.csv",FileMode.OpenOrCreate);
        using(var writer = new StreamWriter(fs)){
            var exporter= new JsonToCsvExporter(json);
            exporter.ExportHeader(writer).Wait();
            exporter.ExportBody(writer).Wait();
            fs.Flush();
        }
    }
    

    enter image description here

        2
  •  1
  •   lenny    7 年前

    我接受了itminus的答案,因为他找到了一个合适的解决方案,并投入了大量的工作。不过,我事先就想好了。以下是我自己的解决方案:

    为了解析,我使用了good'ol Newtonsoft.Json,为了序列化为CSV,我使用了jitbit的CsvHelper,如问题中所述。我的解决方案需要一段时间 List<string> 填充了一堆JSON对象,每个对象都有相同的结构,但结构未知。唯一需要说明的是JSON是由键值对填充的,不包含数组或更多“更深层”的对象。

    [Authorize]
    public class ExportController : Controller
    {
        //Dependency-Injection of database context
        private readonly VoteDbContext c;
        public ExportController(VoteDbContext Context)
        {
            c = Context;
        }
    
        [HttpGet]
        public FileResult Feedback()
        {
            //get all feedback records
            List<string> jsonData = c.UserFeedback.Select(x => x.Data).ToList();
            //example JSON in this list:
            // {"key1":"val1", "key2":"val2", ...}
    
            CsvExport csvExport = new CsvExport();
    
            foreach (string json in jsonData)
            {
                //parse json into usable object
                Dictionary<string, string> currentData = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
    
                //add new row for each record
                csvExport.AddRow();
                //add values for row
                foreach (KeyValuePair<string, string> kvp in currentData)
                    csvExport[kvp.Key] = kvp.Value;
            }
    
            //return the generated csv file
            return File(csvExport.ExportToBytes(true)/*true -> with header*/, "text/csv", "Feedback.csv");
        } 
    }
    

    FileResult 我将返回 File()