我正在尝试编写一个JsonConverter以字符串形式转义HTML的代码,除非[AllowHtml]已应用了该属性;否则,
JsonConverter
[AllowHtml]
private class ObjectWithStrings { // will be HTML-escaped public string Name { get; set; } // won't be escaped [AllowHtml] public string Unsafe { get; set; } }
所以我试图用一个自定义的ReadJson属性编写一个JsonConverter。
public override bool CanConvert(Type objectType) { return objectType == typeof(string); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var s = (string)reader.Value; if (s == null) { return null; } // here I need to get a PropertyInfo so I can call GetCustomAttribute<AllowHtmlAttribute>(); var encoded = System.Web.Security.AntiXss.AntiXssEncoder.HtmlEncode(s, useNamedEntities: true); return encoded; }
我的差距是,我看不到Json.Net是否会让我知道我正在阅读的属性。因此,我不知道如何获取属性的自定义属性。
有没有办法找出我要序列化的属性,或为这种情况推荐的其他模式?
编辑 :我没有写一个明确的问题;我试图编写一个JsonConverter反序列化 字符串的 -,请参见上面的实现CanConvert()。我怀疑选择是我问题的开始。除反序列化特定属性外,我可能需要反序列化具有字符串属性的对象,并进行标准反序列化。
CanConvert()
在自定义中JsonConverter,可以通过Path从中选择属性来找到要反序列化的JSON属性的名称JsonReader。
Path
JsonReader
string propertyName = reader.Path.Split('.').Last();
但是,这不能解决您的整体问题。假设JSON属性的名称与您的目标类属性匹配,那么您仍然需要一种获取父对象类型的方法,以便从中获取自定义属性。不幸的是,此信息在转换器内部不可用。转换器仅负责它说可以转换的对象类型(在您的情况下为字符串)以及该对象的子属性(在这种情况下,因为字符串是基元,所以不负责)。因此,要使其正常工作,需要编写转换器以在 父级上运行 类,然后需要处理该类的所有字符串属性。由于您的目标似乎是将HTML编码行为应用于所有类中的所有字符串,因此您将需要一个通用转换器来处理所有非原始类型,这可能会变得非常混乱,具体取决于您尝试的内容反序列化。
幸运的是,有更好的方法。除了使用之外JsonConverter,您还可以IContractResolver结合使用自定义项和IValueProvider来解决此问题。A ContractResolver更适合于您想广泛应用某种行为的此类问题。
IContractResolver
IValueProvider
ContractResolver
下面是您需要的代码示例。的CustomResolver类扩展DefaultContractResolver由Json.Net提供。该CreateProperties()方法检查JsonProperty由基础解析器创建的对象,并将内部HtmlEncodingValueProvider类的实例附加到未[AllowHtml]应用属性的任何字符串属性。每个值提供者随后都通过SetValue()方法处理其目标字符串属性的实际编码。
CustomResolver
DefaultContractResolver
CreateProperties()
JsonProperty
HtmlEncodingValueProvider
SetValue()
public class CustomResolver : DefaultContractResolver { protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { IList<JsonProperty> props = base.CreateProperties(type, memberSerialization); // Find all string properties that do not have an [AllowHtml] attribute applied // and attach an HtmlEncodingValueProvider instance to them foreach (JsonProperty prop in props.Where(p => p.PropertyType == typeof(string))) { PropertyInfo pi = type.GetProperty(prop.UnderlyingName); if (pi != null && pi.GetCustomAttribute(typeof(AllowHtmlAttribute), true) == null) { prop.ValueProvider = new HtmlEncodingValueProvider(pi); } } return props; } protected class HtmlEncodingValueProvider : IValueProvider { PropertyInfo targetProperty; public HtmlEncodingValueProvider(PropertyInfo targetProperty) { this.targetProperty = targetProperty; } // SetValue gets called by Json.Net during deserialization. // The value parameter has the original value read from the JSON; // target is the object on which to set the value. public void SetValue(object target, object value) { var encoded = System.Web.Security.AntiXss.AntiXssEncoder.HtmlEncode((string)value, useNamedEntities: true); targetProperty.SetValue(target, encoded); } // GetValue is called by Json.Net during serialization. // The target parameter has the object from which to read the string; // the return value is the string that gets written to the JSON public object GetValue(object target) { // if you need special handling for serialization, add it here return targetProperty.GetValue(target); } } }
要使用解析器,请创建一个新JsonSerializerSettings实例,然后将其ContractResolver属性设置为自定义解析器的新实例,并将设置传递给该JsonConvert.DeserializeObject()方法。
JsonSerializerSettings
JsonConvert.DeserializeObject()
这是一个简短的演示:
class Program { static void Main(string[] args) { string json = @" { ""Name"" : ""<b>Foo Bar</b>"", ""Description"" : ""<p>Bada Boom Bada Bing</p>"", }"; JsonSerializerSettings settings = new JsonSerializerSettings { ContractResolver = new CustomResolver() }; Foo foo = JsonConvert.DeserializeObject<Foo>(json, settings); Console.WriteLine("Name: " + foo.Name); Console.WriteLine("Desc: " + foo.Description); } } class Foo { public string Name { get; set; } [AllowHtml] public string Description { get; set; } } class AllowHtmlAttribute : Attribute { }
这是输出。请注意,该Name属性获取HTML编码,而该Description属性未获取HTML编码。
Name
Description
Name: <b>Foo Bar</b> Desc: <p>Bada Boom Bada Bing</p>
小提琴:https : //dotnetfiddle.net/cAg4NC