在我的应用程序中,我接收JSON数据,但从不知道它将包含哪些字段。我想做的就是将JSON字符串列表转换成.csv文件,并将其作为文件结果返回。棘手的事情是我永远不知道JSON将包含哪些字段或多少字段。每个对象可能是一个字段,也可能是几个字段,其名称我无法预测。我发现的所有解决方案都是针对集合对象结构的,您可以在其中将JSON解析为与JSON结构匹配的c#类。
是否可以轻松地将JSON解析为动态对象,然后将其序列化为CSV?任何帮助表示赞赏。
预先感谢
我发现this个简单的导出工具可以使用PropertyInfo
对象的dynamic
工作。有建议吗?
好的,所以我不再使用dynamic
对象,这只会使事情变得更复杂。我将JSON解析为Dictionary<string, string>
,因为我意识到JSON仅包含键值对。完美无瑕。现在,我需要一种将其序列化为CSV的方法,并且我需要标头。我之前提到的CSV导出工具无法按我想要的方式工作,它不支持标头,并且由于某种原因,它在第一行中添加了sep=
。我还没有找到一个没有对象就可以工作的CSV序列化程序。为什么这么复杂?
答案 0 :(得分:1)
由于您假设该属性是简单属性,因此我们可以简单地将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();
}
}
答案 1 :(得分:1)
我接受了itminus的回答,因为他找到了合适的解决方案,并将大量工作投入其中。虽然,我事先想通了。这是我自己的解决方案:
要解析,我使用了很好的'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");
}
}
我想从MVC控制器将其作为文件返回,因此返回类型为FileResult
,并且我正在返回File()
方法的输出。