Newtonsoft Json.NET序列化DataSet时如何忽略默认值与空值?
问题:DataSet序列化JSON时无法排除值类型默认值(0、false)
维护旧代码库时必须使用DataSet做序列化操作,希望精简生成的JSON,移除空值以及int类型的0、bool类型的false。但现有代码并未排除第0行的IntValue(值为0)和第1行的BoolValue(值为false),求解决办法。
var dataSet = new DataSet(); dataSet.Tables.Add("MyTable"); dataSet.Tables[0].Columns.Add("StringValue", typeof(string)); dataSet.Tables[0].Columns.Add("IntValue", typeof(int)); dataSet.Tables[0].Columns.Add("BoolValue", typeof(bool)); var row = dataSet.Tables[0].NewRow(); row["StringValue"] = "Hello"; row["IntValue"] = 0; row["BoolValue"] = true; dataSet.Tables[0].Rows.Add(row); row = dataSet.Tables[0].NewRow(); row["StringValue"] = null; row["IntValue"] = 123; row["BoolValue"] = false; dataSet.Tables[0].Rows.Add(row); var settings = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore, Formatting = Formatting.Indented }; Console.WriteLine(JsonConvert.SerializeObject(dataSet, settings)); /*输出结果: { "MyTable": [ { "StringValue": "Hello", "IntValue": 0, "BoolValue": true }, { "IntValue": 123, "BoolValue": false } ] } */
解决办法
Json.NET对DataSet/DataRow的默认序列化逻辑里,不会把明确设置的0、false这类值类型默认值判定为"默认值"——因为DataSet中的字段是强类型的,这些值被视为有效赋值,而非未初始化的默认状态,所以DefaultValueHandling.Ignore不会生效。下面提供两种可行方案:
方案一:自定义JsonConverter处理DataRow
写一个自定义转换器,在序列化DataRow时逐个检查字段值,若为对应类型的默认值则跳过序列化:
public class DataRowDefaultValueConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(DataRow).IsAssignableFrom(objectType); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var row = (DataRow)value; writer.WriteStartObject(); foreach (DataColumn column in row.Table.Columns) { var columnValue = row[column]; // 跳过DBNull和值类型默认值 if (columnValue == DBNull.Value) continue; var type = column.DataType; if (type.IsValueType) { var defaultValue = Activator.CreateInstance(type); if (EqualityComparer<object>.Default.Equals(columnValue, defaultValue)) continue; } writer.WritePropertyName(column.ColumnName); serializer.Serialize(writer, columnValue); } writer.WriteEndObject(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // 若不需要反序列化,直接抛出异常或返回null throw new NotImplementedException("DataRowDefaultValueConverter不支持反序列化"); } }
然后在序列化设置中添加这个转换器:
var settings = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.Indented, Converters = new List<JsonConverter> { new DataRowDefaultValueConverter() } }; Console.WriteLine(JsonConvert.SerializeObject(dataSet, settings));
方案二:序列化前预处理DataRow
遍历所有DataRow,将值为默认值的字段设为DBNull.Value,这样NullValueHandling.Ignore就会忽略它们:
// 预处理DataSet中的所有行 foreach (DataTable table in dataSet.Tables) { foreach (DataRow row in table.Rows) { foreach (DataColumn column in table.Columns) { var value = row[column]; if (value == DBNull.Value) continue; var type = column.DataType; if (type.IsValueType) { var defaultValue = Activator.CreateInstance(type); if (EqualityComparer<object>.Default.Equals(value, defaultValue)) { row[column] = DBNull.Value; } } } } } // 再用原设置序列化 var settings = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.Indented }; Console.WriteLine(JsonConvert.SerializeObject(dataSet, settings));
两种方案都能得到预期的精简JSON:
{ "MyTable": [ { "StringValue": "Hello", "BoolValue": true }, { "IntValue": 123 } ] }
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

