c# - 在反序列化过程中有选择地转义字符串中的 HTML

标签 c# json serialization json.net deserialization

我正在寻找一个 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<AllowHtmlAttribute>();

        var encoded = System.Web.Security.AntiXss.AntiXssEncoder.HtmlEncode(s, useNamedEntities: true);
        return encoded;
    }

我遇到的问题是我看不到 Json.Net 是否会让我知道我正在读入的属性。因此,我不知道如何获取属性的自定义属性。

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

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

最佳答案

来自自定义 JsonConverter ,您可以通过从 Path 中挑选它来找到被反序列化的 JSON 属性的名称。来自 JsonReader 的属性(property).

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

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

幸运的是,有更好的方法。而不是使用 JsonConverter ,您可以使用自定义 IContractResolver结合 IValueProvider解决这个问题。 ContractResolver更适合像这样的问题,你想广泛地应用某种行为。

以下是您需要的代码示例。 CustomResolver类扩展了 DefaultContractResolver由 Json.Net 提供。 CreateProperties()方法检查 JsonProperty由基本解析器创建的对象并附加内部 HtmlEncodingValueProvider 的实例对任何不具有 [AllowHtml] 的字符串属性进行分类应用的属性。每个值提供者稍后通过 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()方法。

这是一个简短的演示:

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属性在 Description 时得到 HTML 编码属性(property)没有。

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

fiddle :https://dotnetfiddle.net/cAg4NC

关于c# - 在反序列化过程中有选择地转义字符串中的 HTML,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32562381/

相关文章:

javascript - JSON回调问题

javascript - JSON.stringify 向我的 Json 对象添加额外的\和 ""的问题

c# - Lambda 编译抛出 "variable ' ' of type '' referenced from scope '' , but it is not defined"

c# - 使用 serilog 解构开放泛型

ios - 获取 JSON 并尝试解析它时出错 [iOS]

javascript - IE 中的 HTTP 获取 JSON 问题,适用于 Chrome FF

c# - 带有 SecureString 的 JSON ToObject

c# - 将值从字符串转换为流时出错

c# - 在 Unity 中通过代码创建枢轴点

c# - 将 C# 自动属性重构为字段和 getter/setter 属性?