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

Asp.net core mvc MVC.Net核心-将数据库内容导出为.csv文件,asp.net-core-mvc,export-to-csv,Asp.net Core Mvc,Export To Csv,在我的应用程序中,我接收JSON数据,但从不知道它将包含哪些字段。我要做的是将JSON字符串列表转换为.csv文件,并将其作为文件结果返回。棘手的是,我永远不知道JSON将包含什么或多少字段。它可以是每个对象一个字段,也可以是几个字段,其名称我无法预测。我找到的所有解决方案都是针对set对象结构的,您可以将JSON解析为与JSON结构匹配的c#类 有没有一种方法可以轻松地将JSON解析为动态对象,然后将其序列化为CSV?感谢您的帮助 提前谢谢 编辑 我发现了一个简单的导出工具,它可以使用动态对象

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

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

提前谢谢

编辑 我发现了一个简单的导出工具,它可以使用
动态对象的
属性info
。建议

编辑2
好吧,我不再使用
动态
对象了,它只会让事情变得更复杂。我正在将我的JSON解析到一个
字典
,因为我意识到我的JSON只包含键值对。那是完美的。现在我需要一种将其序列化为CSV的方法,并且我想要一个标题。我前面提到的CSV导出工具没有按照我想要的方式工作,它不支持标题,并且出于某种原因,它在第一行添加了
sep=
。我还没有发现一个CSV序列化程序在没有对象的情况下工作。为什么这么复杂?

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

为了使代码清晰明了,我将
行定义为
SortedDictionary


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

为了使代码清晰明了,我将
行定义为
SortedDictionary


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

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

[授权]
公共类导出控制器:控制器
{
//数据库上下文的依赖注入
私有只读VoteDbContext c;
公共ExportController(VoteDbContext上下文)
{
c=上下文;
}
[HttpGet]
公共文件结果反馈()
{
//获取所有反馈记录
列出jsonData=c.UserFeedback.Select(x=>x.Data.ToList();
//此列表中的JSON示例:
//{“key1”:“val1”,“key2”:“val2”,…}
CsvExport CsvExport=新的CsvExport();
foreach(jsonData中的字符串json)
{
//将json解析为可用对象
Dictionary currentData=JsonConvert.DeserializeObject(json);
//为每条记录添加新行
csvExport.AddRow();
//为行添加值
foreach(currentData中的KeyValuePair kvp)
csvExport[kvp.Key]=kvp.Value;
}
//返回生成的csv文件
返回文件(csvExport.ExportToBytes(true)/*true->带有标题*/,“text/csv”,“Feedback.csv”);
} 
}

我想从MVC控制器将其作为文件返回,因此返回类型为
FileResult
,我将返回
file()
方法的输出。

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

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

[授权]
公共类导出控制器:控制器
{
//数据库上下文的依赖注入
私有只读VoteDbContext c;
公共ExportController(VoteDbContext上下文)
{
c=上下文;
}
[HttpGet]
公共文件结果反馈()
{
//获取所有反馈记录
列出jsonData=c.UserFeedback.Select(x=>x.Data.ToList();
//此列表中的JSON示例:
//{“key1”:“val1”,“key2”:“val2”,…}
CsvExport CsvExport=新的CsvExport();
foreach(jsonData中的字符串json)
{
//将json解析为可用对象
Dictionary currentData=JsonConvert.DeserializeObject(json);
//为每条记录添加新行
csvExport.AddRow();
//为行添加值
foreach(currentData中的KeyValuePair kvp)
csvExport[kvp.Key]=kvp.Value;
}
//返回生成的csv文件
返回文件(csvExport.ExportToBytes(true)/*true->带有标题*/,“text/csv”,“Feedback.csv”);
} 
}

我想从MVC控制器将其作为文件返回,因此返回类型为
FileResult
,我返回
file()
方法的输出。

我不知道为什么有人否决了这个问题。但是Json可以被视为一个树状对象,而csv可以被视为一个表状文件。没有标准的方法将树状对象转换为表状对象。我认为最好告诉我们如何决定将
a.b.c.d
的属性映射到表中的一个字段。为了清楚起见,我要问的是如何将json字符串
{“x”:“ss”,“z”:{“c1”:“c1”,“c2”:“c2”},“a”:[1,2,3]}
转换为csv?@its幸运的是,我的json对象会这样做
using Row =SortedDictionary<string,string>; 
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();
    }
}
[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");
    } 
}