JsonConvert序列化DataTable正常但反序列化报错的解决方法
问题描述
我有一个DataTable,用JsonConvert.SerializeObject序列化JSON正常,结果如下:
[ { "String": null, "Int": 1, "Double": 1.0, "Boolean": false, "Date": "2022-09-30T00:00:00", "DateTime": "2022-09-30T00:00:00", "List of strings": [ "A", "C" ], "List of floats": [ 1.0, 2.0 ], "List of dates": "[datetime.date(2022, 9, 30)]", "Dictionary String - Flaot": { "A": 1.0 }, "Dictionary String - List of floats": { "A": [ 1.0 ] } }, { "String": null, "Int": 2, "Double": 2.0, "Boolean": true, "Date": "2021-01-01T00:00:00", "DateTime": "2021-01-01T00:00:00", "List of strings": [ "B", "D" ], "List of floats": [ 3.0, 4.0 ], "List of dates": "[datetime.date(2022, 1, 1), datetime.date(2022, 2, 2)]", "Dictionary String - Flaot": { "B": 2.0, "C": 3.0 }, "Dictionary String - List of floats": { "B": [ 2.0, 3.0 ], "C": [ 12.0 ] } } ]
但调用JsonConvert.DeserializeObject<DataTable>(上述JSON字符串)时抛出错误:
JsonSerializationException: Unexpected JSON token when reading DataTable: StartObject. Path '[0]['Dictionary String - Flaot']', line 18, position 34. at Newtonsoft.Json.Converters.DataTableConverter.GetColumnDataType(JsonReader reader) at Newtonsoft.Json.Converters.DataTableConverter.CreateRow(JsonReader reader, DataTable dt, JsonSerializer serializer) at Newtonsoft.Json.Converters.DataTableConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
原因分析
Newtonsoft.Json的默认DataTableConverter仅支持简单值类型和一维数组,无法处理嵌套对象(比如Dictionary<string, T>)或复杂嵌套数组结构。反序列化时,转换器遇到{(StartObject)标记时,无法识别该类型作为DataTable的列值,直接抛出异常。
解决办法
方案1:改用强类型实体类(推荐)
放弃DataTable,定义匹配JSON结构的强类型类,完美支持嵌套结构的序列化与反序列化:
public class DataItem { public string String { get; set; } public int Int { get; set; } public double Double { get; set; } public bool Boolean { get; set; } public DateTime Date { get; set; } public DateTime DateTime { get; set; } public List<string> ListOfStrings { get; set; } public List<double> ListOfFloats { get; set; } public string ListOfDates { get; set; } // 原JSON中为字符串格式的日期列表,直接用string接收 public Dictionary<string, double> DictionaryStringFloat { get; set; } public Dictionary<string, List<double>> DictionaryStringListOfFloats { get; set; } } // 反序列化调用 var dataList = JsonConvert.DeserializeObject<List<DataItem>>(jsonString);
方案2:自定义JsonConverter处理DataTable的复杂字段
如果必须使用DataTable,可自定义转换器,将字典等复杂类型序列化为JSON字符串,反序列化时再解析回对象:
public class ComplexDataTableConverter : DataTableConverter { public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var dt = base.ReadJson(reader, objectType, existingValue, serializer) as DataTable; if (dt == null) return null; // 处理字典类型列,将JSON字符串反序列化为Dictionary foreach (DataRow row in dt.Rows) { foreach (DataColumn col in dt.Columns) { if (col.ColumnName.Contains("Dictionary") && row[col] is string jsonStr && !string.IsNullOrEmpty(jsonStr)) { // 根据实际字段类型调整反序列化目标 if (col.ColumnName == "Dictionary String - Flaot") { row[col] = JsonConvert.DeserializeObject<Dictionary<string, double>>(jsonStr); } else if (col.ColumnName == "Dictionary String - List of floats") { row[col] = JsonConvert.DeserializeObject<Dictionary<string, List<double>>>(jsonStr); } } } } return dt; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var dt = value as DataTable; if (dt == null) return; // 序列化前将字典转为JSON字符串 foreach (DataRow row in dt.Rows) { foreach (DataColumn col in dt.Columns) { if (row[col] is IDictionary dict) { row[col] = JsonConvert.SerializeObject(dict); } } } base.WriteJson(writer, dt, serializer); } } // 使用方式 var settings = new JsonSerializerSettings(); settings.Converters.Add(new ComplexDataTableConverter()); // 序列化 var json = JsonConvert.SerializeObject(dataTable, settings); // 反序列化 var dt = JsonConvert.DeserializeObject<DataTable>(json, settings);
方案3:手动预处理JSON字符串(临时方案)
若不想修改代码结构,可手动将JSON中的字典对象转为字符串后再反序列化:
// 预处理:将字典对象转为JSON字符串(需根据实际字段调整替换规则) var processedJson = jsonString .Replace("\"Dictionary String - Flaot\": {", "\"Dictionary String - Flaot\": \"{") .Replace("},", "}\",") .Replace("\"Dictionary String - List of floats\": {", "\"Dictionary String - List of floats\": \"{") .Replace("}]", "}\"]"); // 反序列化 var dt = JsonConvert.DeserializeObject<DataTable>(processedJson); // 反序列化后手动解析字典字段 foreach (DataRow row in dt.Rows) { var floatDictJson = row["Dictionary String - Flaot"].ToString(); row["Dictionary String - Flaot"] = JsonConvert.DeserializeObject<Dictionary<string, double>>(floatDictJson); var listDictJson = row["Dictionary String - List of floats"].ToString(); row["Dictionary String - List of floats"] = JsonConvert.DeserializeObject<Dictionary<string, List<double>>>(listDictJson); }
注意:该方案仅适合临时场景,JSON结构变化时需同步修改替换规则,维护成本较高。
内容的提问来源于stack exchange,提问作者Math122
相关产品推荐
相关产品推荐

