我正在尝试使用 Json.Net 将一些动态创建的 JSON 反序列化为数据表,结果表没有预期的十进制值。
string data = @"[
{""RowNumber"":1,""ID"":4289,""Assets Variance"":100,""Rules Diff"":10.72,""TotalFunding"":0},
{""RowNumber"":2,""ID"":4233,""Assets Variance"":75,""Rules Diff"":6.7,""TotalFunding"":0},
{""RowNumber"":3,""ID"":2222,""Assets Variance"":43,""Rules Diff"":6.7,""TotalFunding"":43.22}
]";
DataTable dt = JsonConvert.DeserializeObject<DataTable>(data);
如果您查看此 JSON 中的前两项,属性
Total Funding
有值 0
第三项具有值(value) 43.22
但是当我们将其转换为数据表时,它会呈现为43
.属性 Rules Diff
不会发生这种情况因为它在第一项本身中有一个有效的十进制值。JSON 中的属性是动态的,因此不能针对特定类型进行转换。我们如何反序列化这个 JSON 以便它在数据表中保留小数?
最佳答案
这是 DataTableConverter
的已知限制随 Json.Net 一起提供。转换器假定 JSON 中的第一行数据是所有行的代表性样本,并使用它来确定 DataTable
中列的数据类型。 .
如果您事先知道 JSON 中有哪些数据类型,解决该问题的一种方法是反序列化为 List<T>
而不是 DataTable
, 其中 T
是一个具有与 JSON 匹配的属性名称和类型的类。然后,如果您仍然需要一个表,您可以从列表中构建它作为后处理步骤。
但是,您说您的 JSON 是动态的,因此您需要使用自定义 JsonConverter
反而。可以制作一个可以提前读取 JSON 以确定用于每一列的最佳数据类型的数据类型。像下面这样的东西应该可以工作。随意根据您的需要定制它。
public class ReadAheadDataTableConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DataTable);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
var dataTypes = DetermineColumnDataTypes(array);
var table = BuildDataTable(array, dataTypes);
return table;
}
private DataTable BuildDataTable(JArray array, Dictionary<string, Type> dataTypes)
{
DataTable table = new DataTable();
foreach (var kvp in dataTypes)
{
table.Columns.Add(kvp.Key, kvp.Value);
}
foreach (JObject item in array.Children<JObject>())
{
DataRow row = table.NewRow();
foreach (JProperty prop in item.Properties())
{
if (prop.Value.Type != JTokenType.Null)
{
Type dataType = dataTypes[prop.Name];
row[prop.Name] = prop.Value.ToObject(dataType);
}
}
table.Rows.Add(row);
}
return table;
}
private Dictionary<string, Type> DetermineColumnDataTypes(JArray array)
{
var dataTypes = new Dictionary<string, Type>();
foreach (JObject item in array.Children<JObject>())
{
foreach (JProperty prop in item.Properties())
{
Type currentType = GetDataType(prop.Value.Type);
if (currentType != null)
{
Type previousType;
if (!dataTypes.TryGetValue(prop.Name, out previousType) ||
(previousType == typeof(long) && currentType == typeof(decimal)))
{
dataTypes[prop.Name] = currentType;
}
else if (previousType != currentType)
{
dataTypes[prop.Name] = typeof(string);
}
}
}
}
return dataTypes;
}
private Type GetDataType(JTokenType tokenType)
{
switch (tokenType)
{
case JTokenType.Null:
return null;
case JTokenType.String:
return typeof(string);
case JTokenType.Integer:
return typeof(long);
case JTokenType.Float:
return typeof(decimal);
case JTokenType.Boolean:
return typeof(bool);
case JTokenType.Date:
return typeof(DateTime);
case JTokenType.TimeSpan:
return typeof(TimeSpan);
case JTokenType.Guid:
return typeof(Guid);
case JTokenType.Bytes:
return typeof(byte[]);
case JTokenType.Array:
case JTokenType.Object:
throw new JsonException("This converter does not support complex types");
default:
return typeof(string);
}
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
要使用转换器,请将实例传递给
DeserializeObject
像这样的方法:DataTable dt = JsonConvert.DeserializeObject<DataTable>(data, new ReadAheadDataTableConverter());
请注意,此转换器的运行速度会比 OOB
DataTableConverter
慢一点由于额外的处理。对于小数据集,它不应该引起注意。这里的工作演示:https://dotnetfiddle.net/iZ0u6Y
关于c# - 当前几个 JSON 项具有非十进制值时,将动态 JSON 反序列化为 DataTable 会丢失小数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61372252/