自定义 Newtonsoft JsonConverter 用于数组和集合以进行进一步操作
custom Newtonsoft JsonConverter for arrays and collections for further manipulation
我想为字符串数组(或 IEnumerable)使用自定义 Json转换器并对数组进行一些操作(实际上是删除所有为 null 或空白的字符串)。
但我已经卡在ReadJson方法中不知道如何正确获取string[].
我为检查 JsonToken.String 的简单字符串做了一个自定义转换器。但是数组有 StartArray 和 EndArray...
有人已经 de/serialized 他们的自定义字符串数组并且可以帮助我吗?
更多详情:
我想要实现的是在模型绑定上集中或可选字符串 trimming(所以我不必在每个控制器中都这样做)并且模型验证检查重复项会检测到 "a string" 和“一个字符串”重复。
我正在尝试这样做,因为 Json转换器(挖掘模型绑定日志输出、.net 核心文档、.net 核心 github 代码使我达到 json转换器最好)。
集中使用将在 StartUp Json 选项中配置:
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions
(
options =>
{
options.SerializerSettings.Converters.Add(new TrimmedStringConverter());
options.SerializerSettings.Converters.Add(new CleanStringArrayConverter());
}
);
}
每个模型的使用情况看起来像
public class RequestModel
{
[JsonConverter(typeof(TrimmedStringConverter))]
public string MyValue { get; set; }
[JsonConverter(typeof(CleanStringArrayConverter))]
public string[] Entries { get; set; }
}
为模型绑定上的自动 trim 字符串提供了转换器。我刚加了点盐。
public class TrimmedStringConverter : JsonConverter
{
public bool EmptyStringsAsNull { get; }
public TrimmedStringConverter()
{
EmptyStringsAsNull = true;
}
public TrimmedStringConverter(bool emptyStringsAsNull)
{
EmptyStringsAsNull = emptyStringsAsNull;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
private string CleanString(string str)
{
if (str == null) return null;
str = str.Trim();
if (str.Length == 0 && EmptyStringsAsNull) return null;
return str;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
//if (reader.Value != null)
return CleanString(reader.Value as string);
}
return reader.Value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var text = (string)value;
if (text == null)
writer.WriteNull();
else
writer.WriteValue(CleanString(text));
}
}
现在这使我的模型在字符串[] 中有空字符串或空值。我现在尝试在第二个转换器中自动删除它(或者一个转换器在上面做同样的事情,但对于字符串数组,集合)。
我只是不知道如何使用 reader 和序列化程序正确处理数组 serialization/deserialization。
这就是我的进展(感谢 Silvermind)。字符串数组的第二个转换器。
首先,我也设法在 CleanStringArrayConverter 中使用了全局注册的 TrimmedStringConverter(检查附加的注释代码)。只要全局使用 TrimmedStringConverter 并且 CleanStringArrayConverter 是基于每个模型的,这就有效。全局使用会导致无限循环和 SERVER CRASHES 并出现访问冲突异常。
所以我把它改成了这个版本,两者都可以在全球范围内并排注册。
不幸的是,它只适用于数组。
是否有人会找到此代码,使用它并分享改进?
public class CleanStringArrayConverter : JsonConverter
{
public bool TrimStrings { get; }
public bool EmptyStringsAsNull { get; }
public bool RemoveWhitespace { get; }
public bool RemoveNulls { get; }
public bool RemoveEmpty { get; }
public CleanStringArrayConverter()
{
TrimStrings = true;
EmptyStringsAsNull = true;
RemoveWhitespace = true;
RemoveNulls = true;
RemoveEmpty = true;
}
public CleanStringArrayConverter(bool trimStrings = true, bool emptyStringsAsNull = true, bool removeWhitespace = true, bool removeEmpty = true, bool removeNulls = true)
{
TrimStrings = trimStrings;
EmptyStringsAsNull = emptyStringsAsNull;
RemoveWhitespace = removeWhitespace;
RemoveNulls = removeNulls;
RemoveEmpty = removeEmpty;
}
private string CleanString(string str)
{
if (str == null) return null;
if (TrimStrings) str = str.Trim();
if (str.Length == 0 && EmptyStringsAsNull) return null;
return str;
}
private string[] CleanStringCollection(IEnumerable<string> strings)
{
if (strings == null) return null;
return strings
.Select(s => CleanString(s))
.Where
(
s =>
{
if (s == null) return !RemoveNulls;
else if (s.Equals(string.Empty)) return !RemoveEmpty;
else if (string.IsNullOrWhiteSpace(s)) return !RemoveWhitespace;
else return true;
}
)
.ToArray();
}
public override bool CanConvert(Type objectType)
{
return objectType.IsArray && objectType.GetElementType() == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string[] arr = null; // use null as default value
//string[] arr = new string[]; // use empty array as default value
// deserialze the array
if (reader.TokenType != JsonToken.Null)
{
if (reader.TokenType == JsonToken.StartArray)
{
// this one respects other registered converters (e.g. the TrimmedStringConverter)
// but causes server crashes when used globally due to endless loops
//arr = serializer.Deserialize<string[]>(reader);
// this doesn't respect others!!!
JToken token = JToken.Load(reader);
arr = token.ToObject<string[]>();
}
}
// clean up the array
if (arr != null) arr = CleanStringCollection(arr);
return arr;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
string[] arr = (string[])value;
if (value == null)
{
writer.WriteNull();
return;
}
arr = CleanStringCollection(arr);
// endless loops and server crashes!!!
//serializer.Serialize(writer, arr);
writer.WriteStartArray();
string v;
foreach(string s in arr)
{
v = CleanString(s);
if (v == null)
writer.WriteNull();
else
writer.WriteValue(v);
}
writer.WriteEndArray();
}
}
基本思路是一样的:
internal sealed class TrimmedStringCollectionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsArray && objectType.GetElementType() == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (existingValue is null)
{
// Returning empty array???
return new string[0];
}
var array = (string[])existingValue;
return array.Where(s => !String.IsNullOrEmpty(s)).ToArray();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
也许您可能想对写入部分执行相同的操作。
我想为字符串数组(或 IEnumerable)使用自定义 Json转换器并对数组进行一些操作(实际上是删除所有为 null 或空白的字符串)。
但我已经卡在ReadJson方法中不知道如何正确获取string[].
我为检查 JsonToken.String 的简单字符串做了一个自定义转换器。但是数组有 StartArray 和 EndArray...
有人已经 de/serialized 他们的自定义字符串数组并且可以帮助我吗?
更多详情:
我想要实现的是在模型绑定上集中或可选字符串 trimming(所以我不必在每个控制器中都这样做)并且模型验证检查重复项会检测到 "a string" 和“一个字符串”重复。
我正在尝试这样做,因为 Json转换器(挖掘模型绑定日志输出、.net 核心文档、.net 核心 github 代码使我达到 json转换器最好)。
集中使用将在 StartUp Json 选项中配置:
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions
(
options =>
{
options.SerializerSettings.Converters.Add(new TrimmedStringConverter());
options.SerializerSettings.Converters.Add(new CleanStringArrayConverter());
}
);
}
每个模型的使用情况看起来像
public class RequestModel
{
[JsonConverter(typeof(TrimmedStringConverter))]
public string MyValue { get; set; }
[JsonConverter(typeof(CleanStringArrayConverter))]
public string[] Entries { get; set; }
}
public class TrimmedStringConverter : JsonConverter
{
public bool EmptyStringsAsNull { get; }
public TrimmedStringConverter()
{
EmptyStringsAsNull = true;
}
public TrimmedStringConverter(bool emptyStringsAsNull)
{
EmptyStringsAsNull = emptyStringsAsNull;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
private string CleanString(string str)
{
if (str == null) return null;
str = str.Trim();
if (str.Length == 0 && EmptyStringsAsNull) return null;
return str;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
//if (reader.Value != null)
return CleanString(reader.Value as string);
}
return reader.Value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var text = (string)value;
if (text == null)
writer.WriteNull();
else
writer.WriteValue(CleanString(text));
}
}
现在这使我的模型在字符串[] 中有空字符串或空值。我现在尝试在第二个转换器中自动删除它(或者一个转换器在上面做同样的事情,但对于字符串数组,集合)。
我只是不知道如何使用 reader 和序列化程序正确处理数组 serialization/deserialization。
这就是我的进展(感谢 Silvermind)。字符串数组的第二个转换器。
首先,我也设法在 CleanStringArrayConverter 中使用了全局注册的 TrimmedStringConverter(检查附加的注释代码)。只要全局使用 TrimmedStringConverter 并且 CleanStringArrayConverter 是基于每个模型的,这就有效。全局使用会导致无限循环和 SERVER CRASHES 并出现访问冲突异常。
所以我把它改成了这个版本,两者都可以在全球范围内并排注册。
不幸的是,它只适用于数组。
是否有人会找到此代码,使用它并分享改进?
public class CleanStringArrayConverter : JsonConverter
{
public bool TrimStrings { get; }
public bool EmptyStringsAsNull { get; }
public bool RemoveWhitespace { get; }
public bool RemoveNulls { get; }
public bool RemoveEmpty { get; }
public CleanStringArrayConverter()
{
TrimStrings = true;
EmptyStringsAsNull = true;
RemoveWhitespace = true;
RemoveNulls = true;
RemoveEmpty = true;
}
public CleanStringArrayConverter(bool trimStrings = true, bool emptyStringsAsNull = true, bool removeWhitespace = true, bool removeEmpty = true, bool removeNulls = true)
{
TrimStrings = trimStrings;
EmptyStringsAsNull = emptyStringsAsNull;
RemoveWhitespace = removeWhitespace;
RemoveNulls = removeNulls;
RemoveEmpty = removeEmpty;
}
private string CleanString(string str)
{
if (str == null) return null;
if (TrimStrings) str = str.Trim();
if (str.Length == 0 && EmptyStringsAsNull) return null;
return str;
}
private string[] CleanStringCollection(IEnumerable<string> strings)
{
if (strings == null) return null;
return strings
.Select(s => CleanString(s))
.Where
(
s =>
{
if (s == null) return !RemoveNulls;
else if (s.Equals(string.Empty)) return !RemoveEmpty;
else if (string.IsNullOrWhiteSpace(s)) return !RemoveWhitespace;
else return true;
}
)
.ToArray();
}
public override bool CanConvert(Type objectType)
{
return objectType.IsArray && objectType.GetElementType() == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string[] arr = null; // use null as default value
//string[] arr = new string[]; // use empty array as default value
// deserialze the array
if (reader.TokenType != JsonToken.Null)
{
if (reader.TokenType == JsonToken.StartArray)
{
// this one respects other registered converters (e.g. the TrimmedStringConverter)
// but causes server crashes when used globally due to endless loops
//arr = serializer.Deserialize<string[]>(reader);
// this doesn't respect others!!!
JToken token = JToken.Load(reader);
arr = token.ToObject<string[]>();
}
}
// clean up the array
if (arr != null) arr = CleanStringCollection(arr);
return arr;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
string[] arr = (string[])value;
if (value == null)
{
writer.WriteNull();
return;
}
arr = CleanStringCollection(arr);
// endless loops and server crashes!!!
//serializer.Serialize(writer, arr);
writer.WriteStartArray();
string v;
foreach(string s in arr)
{
v = CleanString(s);
if (v == null)
writer.WriteNull();
else
writer.WriteValue(v);
}
writer.WriteEndArray();
}
}
基本思路是一样的:
internal sealed class TrimmedStringCollectionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsArray && objectType.GetElementType() == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (existingValue is null)
{
// Returning empty array???
return new string[0];
}
var array = (string[])existingValue;
return array.Where(s => !String.IsNullOrEmpty(s)).ToArray();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
也许您可能想对写入部分执行相同的操作。