You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#(.NET Framework)中从JSON生成DataTable并导出CSV

问题:如何将含嵌套数组的JSON转换为扁平化DataTable并导出为CSV?

背景

我接手了一个基于Visual Studio的项目(使用.NET Framework 4.7.2),从API接口获取到包含嵌套数组的JSON响应。需要从中选取指定键值对生成DataTable,并将嵌套数组(如order_line_list)扁平化为多行数据,最终生成CSV文件发送至其他服务器。目前尝试的代码无法成功创建并验证DataTable,仅能遍历JSON获取单个键值对。

JSON响应示例

[
    {
        "transaction_id": "00000352",
        "transaction_type": "New",
        "transaction_date": "2018-08-23T00:00:00",
        "sold_to_id": "00026",
        "customer_po_number": "34567",
        "po_date": "2018-08-23T00:00:00",
        "notes": null,
        "batch_code": "######",
        "location_id": "MN",
        "req_ship_date": "2018-08-28T00:00:00",
        "fiscal_period": 8,
        "fiscal_year": 2018,
        "currency_id": "USD",
        "original_invoice_number": null,
        "bill_to_id": "00026",
        "customer_level": null,
        "terms_code": "30-2",
        "distribution_code": "01",
        "invoice_number": null,
        "invoice_date": "2018-08-23T00:00:00",
        "sales_rep_id_1": null,
        "sales_rep_id_1_percent": 100,
        "sales_rep_id_1_rate": 0,
        "sales_rep_id_2_id": "SA",
        "sales_rep_id_2_percent": 100,
        "sales_rep_id_2_rate": 10,
        "ship_to_id": null,
        "ship_via": null,
        "ship_method": null,
        "ship_number": null,
        "ship_to_name": "",
        "ship_to_attention": null,
        "ship_to_address_1": null,
        "ship_to_address_2": null,
        "ship_to_city": null,
        "ship_to_region": null,
        "ship_to_country": "USA",
        "ship_to_postal_code": null,
        "actual_ship_date": null,
        "order_line_list": [
            {
                "entry_number": 1,
                "item_id": "5' Glass/Wood Table",
                "description": "Glass/ Wood Combo Coffee Table",
                "customer_part_no": null,
                "additional_description": null,
                "location_id": "MN",
                "quantity_ordered": 11,
                "units": "EA",
                "quantity_shipped": 0,
                "quantity_backordered": 0,
                "req_ship_date": null,
                "unit_price": 599.99,
                "extended_price": 6599.89,
                "reason_code": null,
                "account_code": null,
                "inventory_gl_account": "120000",
                "sales_gl_account": "400000",
                "cogs_gl_account": "500000",
                "sales_category": null,
                "tax_class": 0,
                "promo_id": null,
                "price_id": null,
                "discount_type": 0,
                "discount_percentage": 0,
                "discount_amount": 0,
                "sales_rep_id_1": null,
                "sales_rep1_percent": null,
                "sales_rep1_rate": null,
                "sales_rep_id_2": "SA",
                "sales_rep2_percent": 100,
                "sales_rep2_rate": 5,
                "unit_cost": 241.4467,
                "extended_cost": 2655.91,
                "status": "Open",
                "extended_list": [],
                "serial_list": []
            }
        ],
        "tax_group_id": "ATL",
        "taxable": false,
        "tax_class_adjustment": 0,
        "freight": 0,
        "tax_class_freight": 0,
        "tax_location_adjustment": null,
        "misc": 0,
        "tax_class_misc": 0,
        "sales_tax": 0,
        "taxable_sales": 0,
        "non_taxable_sales": 6599.89,
        "payment_list": []
    }
]

尝试过的代码

创建DataTable的代码

var doc = JsonDocument.Parse(response.Content);
JsonElement root = doc.RootElement;

Console.WriteLine(String.Format("Root Elem: {0}", doc.RootElement));

// 创建DataTable
var jsonObject = JObject.Parse(response.Content);
Console.WriteLine(String.Format("jsonObject col: {0}", jsonObject));
DataTable jsdt = jsonObject[doc.RootElement].ToObject<DataTable>();

int totalRows = jsdt.Rows.Count;

Console.WriteLine(String.Format("jsonObject col: {0}", jsonObject));
Console.WriteLine(String.Format("totalRows col: {0}", totalRows));
Console.WriteLine(String.Format("jsdt col: {0}", jsdt.Rows.Count));

遍历JSON的代码

var users = root.EnumerateArray();

while (users.MoveNext())
{
    var user = users.Current;

    var props = user.EnumerateObject();

    while (props.MoveNext())
    {
        var prop = props.Current;
        if (prop.Name == "transaction_date") {
            // Console.WriteLine(String.Format("key: {0}", $"{prop.Name}: {prop.Value}"));
        }
    }
}

解决方案

1. 定义实体类(强类型处理)

对应JSON结构定义实体类,方便反序列化和数据操作:

using System.Collections.Generic;

public class Transaction
{
    public string transaction_id { get; set; }
    public string transaction_type { get; set; }
    public string transaction_date { get; set; }
    public string sold_to_id { get; set; }
    public string customer_po_number { get; set; }
    // 按需添加其他需要的主表字段
    public List<OrderLine> order_line_list { get; set; }
}

public class OrderLine
{
    public int entry_number { get; set; }
    public string item_id { get; set; }
    public string description { get; set; }
    public decimal quantity_ordered { get; set; }
    public decimal unit_price { get; set; }
    public decimal extended_price { get; set; }
    // 按需添加其他需要的子表字段
}

2. 反序列化JSON到实体集合

使用Newtonsoft.Json(需通过NuGet安装Newtonsoft.Json包)进行反序列化:

using Newtonsoft.Json;

// 反序列化JSON数组到Transaction集合
var transactions = JsonConvert.DeserializeObject<List<Transaction>>(response.Content);

3. 扁平化数据生成DataTable

遍历每个交易及其订单行,将主表字段与子表字段合并为一行,添加到DataTable中:

using System.Data;

// 创建DataTable并定义列
DataTable flatTable = new DataTable();
// 添加主表列
flatTable.Columns.Add("transaction_id", typeof(string));
flatTable.Columns.Add("transaction_type", typeof(string));
flatTable.Columns.Add("transaction_date", typeof(string));
// 按需添加其他主表列
// 添加子表列
flatTable.Columns.Add("entry_number", typeof(int));
flatTable.Columns.Add("item_id", typeof(string));
flatTable.Columns.Add("description", typeof(string));
flatTable.Columns.Add("quantity_ordered", typeof(decimal));
// 按需添加其他子表列

// 填充数据
foreach (var trans in transactions)
{
    // 如果订单行为空,也可以生成一行(根据需求调整)
    if (trans.order_line_list == null || trans.order_line_list.Count == 0)
    {
        DataRow row = flatTable.NewRow();
        row["transaction_id"] = trans.transaction_id;
        row["transaction_type"] = trans.transaction_type;
        // 其他主表字段赋值
        flatTable.Rows.Add(row);
        continue;
    }

    foreach (var line in trans.order_line_list)
    {
        DataRow row = flatTable.NewRow();
        // 赋值主表字段
        row["transaction_id"] = trans.transaction_id;
        row["transaction_type"] = trans.transaction_type;
        row["transaction_date"] = trans.transaction_date;
        // 其他主表字段
        // 赋值子表字段
        row["entry_number"] = line.entry_number;
        row["item_id"] = line.item_id;
        row["description"] = line.description;
        row["quantity_ordered"] = line.quantity_ordered;
        // 其他子表字段
        flatTable.Rows.Add(row);
    }
}

4. 将DataTable导出为CSV文件

实现CSV导出逻辑,处理字段中的逗号、引号等特殊字符:

using System.IO;

public void DataTableToCsv(DataTable table, string filePath)
{
    using (StreamWriter writer = new StreamWriter(filePath))
    {
        // 写入表头
        string header = string.Join(",", table.Columns.Cast<DataColumn>().Select(col => EscapeCsvValue(col.ColumnName)));
        writer.WriteLine(header);

        // 写入行数据
        foreach (DataRow row in table.Rows)
        {
            string line = string.Join(",", row.ItemArray.Select(item => EscapeCsvValue(item?.ToString() ?? "")));
            writer.WriteLine(line);
        }
    }
}

private string EscapeCsvValue(string value)
{
    // 如果包含逗号、引号或换行,需要用引号包裹,内部引号转义
    if (value.Contains(",") || value.Contains("\"") || value.Contains("\n") || value.Contains("\r"))
    {
        return $"\"{value.Replace("\"", "\"\"")}\"";
    }
    return value;
}

// 调用示例
DataTableToCsv(flatTable, @"C:\temp\output.csv");

5. 发送CSV文件到其他服务器

使用HttpClient发送文件:

using System.Net.Http;
using System.Threading.Tasks;

public async Task SendCsvToServer(string csvFilePath, string apiUrl)
{
    using (HttpClient client = new HttpClient())
    using (MultipartFormDataContent form = new MultipartFormDataContent())
    using (FileStream fs = File.OpenRead(csvFilePath))
    {
        form.Add(new StreamContent(fs), "file", Path.GetFileName(csvFilePath));
        HttpResponseMessage response = await client.PostAsync(apiUrl, form);
        response.EnsureSuccessStatusCode();
    }
}

// 调用示例(注意异步方法调用)
// await SendCsvToServer(@"C:\temp\output.csv", "https://target-server/api/upload");

内容的提问来源于stack exchange,提问作者user2008685

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 20:24:23