我正在尝试将.NET DataTable序列化为JSON文件,然后将JSON文件反序列化为DataTable。我想很简单。
但是,我有一个表,3行3列,每个元素的类型都是double。如果第一行中的任何值为null,则当JSON.Net将json文件反序列化为DataTable对象时,第一行中为null的列的所有值都变为字符串。
需要明确的是,只有第一行的值为空时,才会发生这种情况。如果除第一行外的任何其他行中的任何值均为null,则该列中的其余值将保持为双精度。
如果我将null替换为double,那么一切都会按预期进行(但是,我无法执行此操作)。
如果我设置NullValueHandling = NullValueHandling.Ignore,则所有值都保持为双精度,除了第一行现在列为最后一行:
例:
"Column2": 1.0, "Column3": 1.1 }, { "Column1": 0.0, "Column2": 0.5, "Column3": 2.0 },
成为:
"Column2": 1.0, "Column3": 1.1 }, { "Column2": 0.5, "Column3": 2.0, "Column1": 0.0 },
我需要能够反序列化JSON,保持Columns的顺序,并且第一行中没有空值,导致该行中的所有值都变成字符串。我还需要保持第一行的Column1(在上述情况下)为空- 不管它是空字符串还是DBNull。
有什么想法吗?(我的测试代码在下面。.comment/ uncomment NullValueHandling看问题)
DataTable table = new DataTable("MyTable"); table.Columns.Add("Column1", typeof(double)); table.Columns.Add("Column2", typeof(double)); table.Columns.Add("Column3", typeof(double)); for (int i = 0; i < 10; i++) { if (i == 0) table.Rows.Add(null, 1.0, 1.1); else table.Rows.Add(0.0, 0.5, 2.0); } JsonSerializer serializer = new JsonSerializer(); //serializer.TypeNameHandling = TypeNameHandling.All; serializer.NullValueHandling = NullValueHandling.Ignore; using (StreamWriter sw1 = new StreamWriter("1st.json")) using (JsonWriter writer1 = new JsonTextWriter(sw1)) { writer1.Formatting = Formatting.Indented; serializer.Serialize(writer1, table); } DataTable newtable; using (StreamReader sr = new StreamReader("1st.json")) using (JsonReader reader = new JsonTextReader(sr)) { newtable = (DataTable)serializer.Deserialize(reader, typeof(DataTable)); } using (StreamWriter sw = new StreamWriter("3rd.json")) using (JsonWriter writer = new JsonTextWriter(sw)) { writer.Formatting = Formatting.Indented; serializer.Serialize(writer, newtable); }
Json.NET是根据MIT许可证开放的源代码,因此一种可能性是创建一个DataTableConverter适合您需要的修改版本。其源代码可在此处获得。
DataTableConverter
首先,创建您自己的此类的派生版本,称为JsonDefaultTypeDataTableConverter<T>。修改方法GetColumnDataType以返回typeof(T)空列值:
JsonDefaultTypeDataTableConverter<T>
GetColumnDataType
typeof(T)
/// <summary> /// Converts a <see cref="DataTable"/> to and from JSON. /// Adapted from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/DataTableConverter.cs /// </summary> public class JsonDefaultTypeDataTableConverter<T> : JsonConverter { private static Type GetColumnDataType(JsonReader reader) { JsonToken tokenType = reader.TokenType; switch (tokenType) { case JsonToken.Integer: case JsonToken.Boolean: case JsonToken.Float: case JsonToken.String: case JsonToken.Date: case JsonToken.Bytes: return reader.ValueType; case JsonToken.Null: case JsonToken.Undefined: return typeof(T); // WAS typeof(string). case JsonToken.StartArray: CheckedRead(reader); if (reader.TokenType == JsonToken.StartObject) { return typeof (DataTable); // nested datatable } Type arrayType = GetColumnDataType(reader); return arrayType.MakeArrayType(); default: throw new JsonSerializationException(string.Format("Unexpected JSON token when reading DataTable: {0}", tokenType)); } } }
您还需要修复调用以JsonSerializationException在第232行周围抛出a ,例如,如下所示:
JsonSerializationException
private static void CheckedRead(JsonReader reader) { if (!reader.Read()) { throw new JsonSerializationException(string.Format("Unexpected end when reading DataTable.")); } }
并且,围绕第114行:
if (reader.TokenType != JsonToken.StartArray) { throw new JsonSerializationException(string.Format("Unexpected JSON token when reading DataTable. Expected StartArray, got {0}.", reader.TokenType)); }
现在,假设您 知道 表包含double值的列,则可以这样使用它:
double
JsonSerializer serializer = new JsonSerializer(); //serializer.TypeNameHandling = TypeNameHandling.All; //serializer.NullValueHandling = NullValueHandling.Ignore; -- DO NOT USE THIS OPTION. serializer.Converters.Add(new JsonDefaultTypeDataTableConverter<double>());
请注意,这样做并不是在修改Json.NET本身的内部结构,而是在为常用的.Net类型复制和修改其一组标准转换器。
更新:完整版本在这里。