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

Selectively escape HTML in strings during deserialization(在反序列化期间选择性地转义字符串中的 HTML)

本文介绍了在反序列化期间选择性地转义字符串中的 HTML的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一个 JsonConverter 来转义字符串中的 HTML,除非已应用 [AllowHtml] 属性;

I'm looking to write a JsonConverter which escapes HTML in strings, unless the [AllowHtml] attribute has been applied;

    private class ObjectWithStrings
    {
        // will be HTML-escaped
        public string Name { get; set; }

        // won't be escaped
        [AllowHtml]
        public string Unsafe { get; set; }
    }

所以我正在尝试使用自定义 ReadJson 属性编写 JsonConverter;

So I'm trying to write a JsonConverter with a custom ReadJson property;

    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 是否会让我知道我正在阅读的属性.因此,我无法弄清楚如何获取属性的自定义属性.

The gap I've got is that I can't see if Json.Net will let me know the property I'm reading into. Consequently, I can't figure out how to get the property's custom attributes.

有没有办法找出我正在序列化的属性,或者为这种事情推荐的不同模式?

Is there a way to find out what property I'm serialising into, or a different pattern recommended for this kind of thing?

编辑:我没有写清楚的问题;我试图编写一个 JsonConverter 来反序列化 strings, -- 请参阅上面的 CanConvert() 实现.我怀疑选择是我问题的开始;我可能需要反序列化具有字符串属性的对象,并进行标准反序列化,除非反序列化特定属性.

EDIT: I failed to write a clear question; I've attempted to write a JsonConverter which deserialises strings, -- see the implementation above of CanConvert(). I suspect that choice is the start of my problem; I may need to deserialise objects with string properties, and do a standard deserialize except when deserialising particular properties.

推荐答案

在自定义 JsonConverter 中,您可以通过从 中挑选出正在反序列化的 JSON 属性的名称JsonReader 中的 Path 属性.

From within a custom JsonConverter, you can find the name of the JSON property being deserialized by picking it out of the Path property from the JsonReader.

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

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

However, this will not solve your overall problem. Assuming the name of the JSON property matches your target class property, you'd still need a way to get the parent object type so you can get the custom attributes from it. Unfortunately, this information is not available to you inside a converter. A converter is intended to be responsible only for the object type it says it can convert (string in your case), and that object's child properties (none in this case, since string is a primitive). So, to make it work, the converter would need to be written to operate on the parent class, and would then need to handle all the string properties of that class. Since your goal seems to be to apply the HTML encoding behavior to all strings in all classes, then you would need a generic converter that handles all non-primitive types, which could get pretty messy, depending on the breadth of what you're trying to deserialize.

幸运的是,有更好的方法.除了使用 JsonConverter,您可以使用自定义 IContractResolver 结合 IValueProvider 来解决这个问题.ContractResolver 更适合于此类您希望广泛应用某种行为的问题.

Fortunately, there is a better way. Instead of using a JsonConverter, you can use a custom IContractResolver in combination with a IValueProvider to solve this. A ContractResolver is much better suited to problems like this where you want to apply a certain behavior broadly.

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

Below is an example of the code you would need. The CustomResolver class extends the DefaultContractResolver provided by Json.Net. The CreateProperties() method inspects the JsonProperty objects created by the base resolver and attaches an instance of the inner HtmlEncodingValueProvider class to any string properties which do not have the [AllowHtml] attribute applied. Each value provider later handles the actual encoding of its target string property via the SetValue() method.

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() 方法.

To use the resolver, create a new JsonSerializerSettings instance, then set its ContractResolver property to a new instance of the custom resolver and pass the settings to the JsonConvert.DeserializeObject() method.

这是一个简短的演示:

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 属性不会.

Here is the output. Notice that the Name property gets HTML encoded while the Description property does not.

Name: &lt;b&gt;Foo Bar&lt;/b&gt;
Desc: <p>Bada Boom Bada Bing</p>

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

这篇关于在反序列化期间选择性地转义字符串中的 HTML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:在反序列化期间选择性地转义字符串中的 HTML

基础教程推荐