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