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

使用CsvHelper解析CSV时单独提取id、其余字段转JSON为空如何解决

问题原因

你的代码有3个核心错误:

  • 映射逻辑错误:你给Json属性配置类型转换器时,CsvHelper默认会去CSV中查找名为Json的列,但你的CSV里不存在这个列,转换器根本拿不到输入值,自然返回空。
  • 实体定义错误:你定义的Json类只有GeoFeatures一个字符串属性,和CSV中除id外的其余字段(_npi_link_id/dtime/_dow等)完全不匹配,就算拿到值也无法正确序列化/反序列化。
  • 逻辑理解错误:你需要的是把当前行除id外的所有列内容拼接为JSON,不是把某一个单元格的JSON字符串反序列化为对象,默认的列映射+类型转换器模式不适用这个场景。
正确实现方案

方案1:基于ClassMap的实现(推荐)

直接在映射规则里手动组装除id外的字段,不需要自定义类型转换器。

  1. 简化实体定义,不需要额外嵌套JSON类:
public class GeoFeatures
{
    public string id { get; set; }
    // 存储其余字段序列化后的JSON字符串
    public string ExtFieldsJson { get; set; }
}
  1. 编写类映射规则,手动组装非id字段为JSON:
using CsvHelper.Configuration;
using Newtonsoft.Json;

public class FooMap : ClassMap<GeoFeatures>
{
    public FooMap()
    {
        // 单独映射id字段
        Map(m => m.id).Name("id");
        // 手动处理剩余字段转JSON
        Map(m => m.ExtFieldsJson).Convert(row =>
        {
            var extFields = new Dictionary<string, object>();
            // 遍历所有表头,跳过id列
            foreach (var header in row.Row.HeaderRecord)
            {
                if (header.Equals("id", StringComparison.OrdinalIgnoreCase))
                    continue;
                extFields[header] = row.Row.GetField(header);
            }
            // 序列化为JSON字符串返回
            return JsonConvert.SerializeObject(extFields);
        });
    }
}
  1. 读取时注册映射即可:
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
    IgnoreBlankLines = true,
    HasHeaderRecord = true,  
    HeaderValidated = null,
    MissingFieldFound = null,
    ShouldSkipRecord = record => string.IsNullOrEmpty(record.ToString()),
};

using (var reader = new StreamReader(@"Z:\tmp\test3.csv"))
using (var csv = new CsvReader(reader, config))
{
    // 注册自定义映射
    csv.Context.RegisterClassMap<FooMap>();
    var list = csv.GetRecords<GeoFeatures>().ToList();
    
    // 测试输出
    Console.WriteLine($"总记录数:{list.Count}");
    Console.WriteLine($"第一条记录id:{list[0].id}");
    Console.WriteLine($"第一条记录扩展字段JSON:{list[0].ExtFieldsJson}");
}

方案2:动态读取实现

如果不想写ClassMap,直接在循环读取时手动组装字段即可:

var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
    IgnoreBlankLines = true,
    HasHeaderRecord = true,  
    HeaderValidated = null,
    MissingFieldFound = null,
    ShouldSkipRecord = record => string.IsNullOrEmpty(record.ToString()),
};

using (var reader = new StreamReader(@"Z:\tmp\test3.csv"))
using (var csv = new CsvReader(reader, config))
{
    csv.Read();
    csv.ReadHeader();
    var headers = csv.HeaderRecord;
    var result = new List<GeoFeatures>();
    
    while (csv.Read())
    {
        var item = new GeoFeatures
        {
            id = csv.GetField<string>("id")
        };
        var extFields = new Dictionary<string, object>();
        foreach (var header in headers)
        {
            if (header == "id") continue;
            extFields[header] = csv.GetField(header);
        }
        item.ExtFieldsJson = JsonConvert.SerializeObject(extFields);
        result.Add(item);
    }
}

运行后得到的ExtFieldsJson就是除id外所有字段的JSON结构,示例输出:

{"_npi_link_id":"3372355","dtime":"1","_dow":"2","congestion_cost":"0","vkt":"2.08","_delay_time":"0","vol":"11","_type":"Council Arterial","anes":"3","length":"189","eed_lim":"50","v_speed":"45.44"}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 16:21:31