MVC .Net Core - Экспорт содержимого БД в виде файла .csv - PullRequest
0 голосов
/ 23 ноября 2018

В моем приложении я получаю данные JSON, но никогда не знаю, какие поля они будут содержать.Я хотел бы преобразовать список строк JSON в файл .csv и вернуть его как результат файла.Хитрость в том, что я никогда не знаю, что или сколько полей будет содержать JSON.Это может быть одно поле на объект или несколько, с именами, которые я не могу предсказать.Все решения, которые я нашел, предназначены для заданных структур объектов, где вы можете анализировать JSON в класс ac #, который соответствует структуре JSON.

Есть ли способ легко проанализировать JSON в динамический объект и затем сериализовать его вCSV?Любая помощь приветствуется.

Заранее спасибо

Редактировать

Я нашел этот простой инструмент экспорта, который может работать с использованием PropertyInfo из dynamic объект.Предложения?

Редактировать 2

Хорошо, я больше не использую dynamic объекты, это только усложняет вещи.Я анализирую свой JSON в Dictionary<string, string>, так как я понял, что мой JSON состоит только из пар ключ-значение.Это работает безупречно.Теперь мне нужен способ сериализации в CSV, и мне нужны заголовки.Инструмент экспорта CSV, о котором я упоминал ранее, работает не так, как я хочу, он не поддерживает заголовки и по какой-то причине добавляет sep= в первую строку.Я не нашел сериализатор CSV, который работает без объекта, чтобы уйти.Почему это так сложно?

Ответы [ 2 ]

0 голосов
/ 27 ноября 2018

Я принял ответ itminus, потому что он нашел подходящее решение и вложил в него кучу работы.Хотя, я понял это заранее.Вот мое собственное решение:

Для анализа я использую хороший 'Newtonsoft.Json' и для сериализации в CSV я использую CsvHelper из jitbit, как упоминалось в вопросе.Мое решение использует 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");
    } 
}

Я хочу вернуть его как файл из контроллера MVCтип возвращаемого значения FileResult, и я возвращаю вывод метода File().

0 голосов
/ 26 ноября 2018

Поскольку вы предполагаете, что свойство является простым, мы можем просто обработать свойство 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

...