dks*_*dks 4 json.net asp.net-web-api
在我的项目中,我编写了一个自定义 json 转换器来修剪 string 属性中存在的空格。
这是我们将使用的典型类的示例,
public class Candidate
{
public string CandidateName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
这是我的自定义 json 转换器
public class StringSanitizingConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue , JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
if (reader.Value != null)
{
string sanitizedString = (reader.Value as string).Trim();
if (StringSanitizeOptions.HasFlag(StringSanitizeOptions.ToLowerCase))
sanitizedString = sanitizedString.ToLowerInvariant();
return sanitizedString;
}
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(text.Trim());
}
}
Run Code Online (Sandbox Code Playgroud)
使用我的自定义转换器,我现在可以通过使用我的“候选”作为其参数之一修剪发送到操作方法的任何空白来格式化字符串。
public void Post(ComplexType complexTypeParameter){
}
Run Code Online (Sandbox Code Playgroud)
到目前为止,一切都运行良好。后来我想增强这个 json 转换器,以根据 Candidate 类中设置为字符串属性的属性来格式化字符串属性。例如,假设我已经像这样编写了我的候选类,
public class Candidate
{
[StringSanitizingOptions(Option.ToLowerCase)]
public string CandidateName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
如果我想根据 json 转换器中的自定义属性配置来格式化类的字符串属性,我将无法在自定义转换器的 ReadJson 方法中访问此自定义属性及其配置。
这是我迄今为止尝试过但没有运气的方法,
不存在于
发送到方法CustomAttributes的objectType
参数的属性中ReadJson()。
试图查看我是否可以提取ReadJson()方法内部属性的父类,以便我可以对类应用反射以提取赋予其任何属性的自定义属性,但我也无法提取。
包含对象的堆栈不可用于JsonConverter.ReadJson(),因此您无法在 内部做您想做的事情ReadJson()。
相反,您可以做的是创建一个自定义合同解析器,它StringSanitizingConverter根据为其生成合同的对象的属性应用适当配置的实例。
首先,假设您的数据模型、属性和JsonConverter如下所示(我必须修改一些内容以使您的代码编译并包含一些额外的测试用例):
public class Candidate
{
[StringSanitizingOptions(Option.ToLowerCase)]
public string CandidateName { get; set; }
[StringSanitizingOptions(Option.DoNotTrim)]
public string StringLiteral { get; set; }
public string DefaultString { get; set; }
public List<string> DefaultStrings { get; set; }
}
[System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Field | System.AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public class StringSanitizingOptionsAttribute : System.Attribute
{
public Option StringSanitizeOptions { get; set; }
public StringSanitizingOptionsAttribute(Option stringSanitizeOptions)
{
this.StringSanitizeOptions = stringSanitizeOptions;
}
}
[Flags]
public enum Option
{
Default = 0,
ToLowerCase = (1<<0),
DoNotTrim = (1<<1),
}
public static class StringSanitizeOptionsExtensions
{
public static bool HasFlag(this Option options, Option flag)
{
return (options & flag) == flag;
}
}
public class StringSanitizingConverter : JsonConverter
{
readonly Option options;
public StringSanitizingConverter() : this(Option.Default) { }
public StringSanitizingConverter(Option options)
{
this.options = options;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
if (reader.Value != null)
{
var sanitizedString = (reader.Value as string);
if (!options.HasFlag(Option.DoNotTrim))
sanitizedString = sanitizedString.Trim();
if (options.HasFlag(Option.ToLowerCase))
sanitizedString = sanitizedString.ToLowerInvariant();
return sanitizedString;
}
return reader.Value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// WriteJson is never called with null
var text = (string)value;
if (!options.HasFlag(Option.DoNotTrim))
text = text.Trim();
writer.WriteValue(text);
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,ConfigurableContractResolver从How to add metadata 中抓取描述 JSON.Net 中哪些属性是日期,并定义扩展方法JsonContractExtensions.AddStringConverters():
public static class JsonContractExtensions
{
public static JsonContract AddStringConverters(this JsonContract contract)
{
if (contract is JsonPrimitiveContract)
{
if (contract.UnderlyingType == typeof(string))
contract.Converter = new StringSanitizingConverter();
}
else if (contract is JsonObjectContract)
{
var objectContract = (JsonObjectContract)contract;
foreach (var property in objectContract.Properties)
{
if (property.PropertyType == typeof(string))
{
var attr = property.AttributeProvider.GetAttributes(typeof(StringSanitizingOptionsAttribute), true)
.Cast<StringSanitizingOptionsAttribute>()
.SingleOrDefault();
if (attr != null)
{
property.Converter = property.MemberConverter = new StringSanitizingConverter(attr.StringSanitizeOptions);
}
}
}
}
return contract;
}
}
public class ConfigurableContractResolver : DefaultContractResolver
{
// This contract resolver taken from the answer to
// /sf/ask/3223311591/
// /sf/answers/3225824101/
readonly object contractCreatedPadlock = new object();
event EventHandler<ContractCreatedEventArgs> contractCreated;
int contractCount = 0;
void OnContractCreated(JsonContract contract, Type objectType)
{
EventHandler<ContractCreatedEventArgs> created;
lock (contractCreatedPadlock)
{
contractCount++;
created = contractCreated;
}
if (created != null)
{
created(this, new ContractCreatedEventArgs(contract, objectType));
}
}
public event EventHandler<ContractCreatedEventArgs> ContractCreated
{
add
{
lock (contractCreatedPadlock)
{
if (contractCount > 0)
{
throw new InvalidOperationException("ContractCreated events cannot be added after the first contract is generated.");
}
contractCreated += value;
}
}
remove
{
lock (contractCreatedPadlock)
{
if (contractCount > 0)
{
throw new InvalidOperationException("ContractCreated events cannot be removed after the first contract is generated.");
}
contractCreated -= value;
}
}
}
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
OnContractCreated(contract, objectType);
return contract;
}
}
public class ContractCreatedEventArgs : EventArgs
{
public JsonContract Contract { get; private set; }
public Type ObjectType { get; private set; }
public ContractCreatedEventArgs(JsonContract contract, Type objectType)
{
this.Contract = contract;
this.ObjectType = objectType;
}
}
public static class ConfigurableContractResolverExtensions
{
public static ConfigurableContractResolver Configure(this ConfigurableContractResolver resolver, EventHandler<ContractCreatedEventArgs> handler)
{
if (resolver == null || handler == null)
throw new ArgumentNullException();
resolver.ContractCreated += handler;
return resolver;
}
}
Run Code Online (Sandbox Code Playgroud)
然后,最后你可以反序列化和序列化Candidate如下:
var settings = new JsonSerializerSettings
{
ContractResolver = new ConfigurableContractResolver
{
}.Configure((s, e) => { e.Contract.AddStringConverters(); }),
};
var candidate = JsonConvert.DeserializeObject<Candidate>(json, settings);
var json2 = JsonConvert.SerializeObject(candidate, Formatting.Indented, settings);
Run Code Online (Sandbox Code Playgroud)
笔记:
我不知道为什么包含对象的堆栈在ReadJson(). 可能性包括:
由于 的默认实例StringSanitizingConverter应用于为string自己生成的合约,因此无需将转换器添加到JsonSerializer.SettingsConverters。这反过来可能会导致小的性能增强,因为CanConvert将不再被调用。
JsonProperty.MemberConverter最近在Json.NET 11.0.1 中被标记为过时,但必须设置为与JsonProperty.Converter以前版本的 Json.NET相同的值。如果您使用的是 11.0.1 或更新版本,您应该能够删除该设置。
您可能希望缓存合同解析器以获得最佳性能。
要JsonSerializerSettings在asp.net-web-api 中进行修改,请参阅JsonSerializerSettings 和 Asp.Net Core,Web API:在操作或控制器级别配置 JSON 序列化器设置,如何在 MVC 4 Web API 中为 Json.NET 设置自定义 JsonSerializerSettings?或ASP.NET Core API JSON serializersettings per request,具体取决于您的要求和使用的框架版本。
示例工作 .Net 小提琴在这里。
| 归档时间: |
|
| 查看次数: |
532 次 |
| 最近记录: |