在反序列化期间选择性地转义字符串中的HTML

我正在寻找一个JsonConverter ,它在字符串中转义HTML,除非应用了[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(); var encoded = System.Web.Security.AntiXss.AntiXssEncoder.HtmlEncode(s, useNamedEntities: true); return encoded; } 

我得到的差距是,我无法看到Json.Net是否会让我知道我正在阅读的属性。 因此,我无法弄清楚如何获取属性的自定义属性。

有没有办法找出我正在序列化的属性,或者推荐用于此类事物的不同模式?

编辑 :我没有写清楚的问题; 我试图编写一个反序列化字符串JsonConverter – 参见上面CanConvert() 。 我怀疑选择是我问题的开始; 我可能需要使用字符串属性反序列化对象,并执行标准反序列化,除非反序列化特定属性。

在自定义JsonConverter ,您可以通过从JsonReader中选择Path属性来查找要反序列化的JSON属性的名称。

 string propertyName = reader.Path.Split('.').Last(); 

但是,这不会解决您的整体问题。 假设JSON属性的名称与您的目标类属性匹配,您仍然需要一种获取父对象类型的方法,以便您可以从中获取自定义属性。 遗憾的是,转换器内部无法提供此信息。 转换器只是负责它可以转换的对象类型(在你的情况下是字符串),以及该对象的子属性(在这种情况下没有,因为string是一个原语)。 因此,为了使其工作,需要编写转换器以对类进行操作,然后需要处理该类的所有字符串属性。 由于您的目标似乎是将HTML编码行为应用于所有类中的所有字符串,因此您需要一个处理所有非基本类型的通用转换器,这可能会变得非常混乱,具体取决于您尝试的广度反序列化。

幸运的是,有一种更好的方法。 您可以使用自定义IContractResolverIValueProvider来解决此JsonConverter ,而不是使用JsonConverterContractResolver更适合于这样的问题,您希望广泛应用某种行为。

下面是您需要的代码示例。 CustomResolver类扩展了Json.Net提供的DefaultContractResolverCreateProperties()方法检查由基本解析程序创建的JsonProperty对象,并将内部HtmlEncodingValueProvider类的实例附加到未应用[AllowHtml]属性的任何字符串属性。 每个值提供程序稍后通过SetValue()方法处理其目标字符串属性的实际编码。

 public class CustomResolver : DefaultContractResolver { protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { IList 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()方法。

这是一个简短的演示:

 class Program { static void Main(string[] args) { string json = @" { ""Name"" : ""Foo Bar"", ""Description"" : ""

Bada Boom Bada Bing

"", }"; JsonSerializerSettings settings = new JsonSerializerSettings { ContractResolver = new CustomResolver() }; Foo foo = JsonConvert.DeserializeObject(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属性不编码。

 Name: <b>Foo Bar</b> Desc: 

Bada Boom Bada Bing

小提琴: https : //dotnetfiddle.net/cAg4NC