将Json.net配置为URL编码某些字段

时间:2017-09-12 15:32:07

标签: c# json.net

说我有这样的模型类:

public class MyModelClass
{
    [JsonProperty("first_field"]
    public string FirstField { get; set; }

    [JsonProperty("second_field"]
    public string SecondField { get; set; }

    public MyModelClass(string first, string second)
    {
        FirstField = first;
        SecondField = second;
    }
}

让我说我有这种类型的实例:

var myObject = new MyModelClass("blablabla", "<>@%#^^@!%");

当我使用Json.net将此对象转换为Json字符串时,我得到类似的内容:

{ 'first_field': 'blablabla', 'second_field': '<>@%#^^@!%' }

有没有办法配置Json.net以便'SecondField'的内容是URL编码的?我是否需要编写自己的自定义转换器或者是否有更简单的方法?

3 个答案:

答案 0 :(得分:4)

如果它只是一个类中的一个字段,您只需向类中添加一个只读属性来处理编码,并对其进行注释,使其在序列化过程中取代原始属性:

    public class MyModelClass
    {
        ...

        [JsonIgnore]
        public string SecondField { get; set; }

        [JsonProperty("second_field")]
        private string UrlEncodedSecondField
        {
            get { return System.Web.HttpUtility.UrlEncode(SecondField); }
        }

        ...
    }

演示小提琴:https://dotnetfiddle.net/MkVBVH

如果您需要跨多个类的多个字段使用此功能,您可以使用类似于Selectively escape HTML in strings during deserialization中的解决方案,并进行一些小的更改:

  1. 创建自定义UrlEncode属性,让解析器查找具有该属性的属性,而不是缺少AllowHtml属性。
  2. HtmlEncodingValueProvider更改为UrlEncodingValueProvider并让它在GetValue而不是SetValue中应用编码(以便它在序列化而不是反序列化时进行编码)。
  3. 以下是生成的代码:

    public class UrlEncodeAttribute : Attribute { }
    
    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 have a [UrlEncode] attribute applied
            // and attach an UrlEncodingValueProvider 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(UrlEncodeAttribute), true) != null)
                {
                    prop.ValueProvider = new UrlEncodingValueProvider(pi);
                }
            }
    
            return props;
        }
    
        protected class UrlEncodingValueProvider : IValueProvider
        {
            PropertyInfo targetProperty;
    
            public UrlEncodingValueProvider(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)
            {
                targetProperty.SetValue(target, (string)value);
            }
    
            // 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)
            {
                string value = (string)targetProperty.GetValue(target);
                return System.Web.HttpUtility.UrlEncode(value);
            }
        }
    }
    

    要使用自定义解析程序,请先装饰要使用新[UrlEncode]属性进行网址编码的所有属性:

    public class MyModelClass
    {
        [JsonProperty("first_field")]
        public string FirstField { get; set; }
    
        [UrlEncode]
        [JsonProperty("second_field")]
        public string SecondField { get; set; }
    
        ...
    }
    

    然后,像这样序列化您的模型:

    var myObject = new MyModelClass("blablabla", "<>@%#^^@!%");
    
    var settings = new JsonSerializerSettings
    {
        ContractResolver = new CustomResolver(),
        Formatting = Formatting.Indented
    };
    
    string json = JsonConvert.SerializeObject(myObject, settings);
    

    演示小提琴:https://dotnetfiddle.net/iOOzFr

答案 1 :(得分:1)

@ brian-rogers提供的答案非常好,但我想提出一个替代方案,我认为更简单:

public class UrlEncodingConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(string);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null) return;
        var encodedValue = System.Web.HttpUtility.UrlEncode((string)value);
        writer.WriteValue(encodedValue);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value == null) return null;
        var decodedValue = System.Web.HttpUtility.UrlDecode(reader.Value.ToString());
        return decodedValue;
    }
}

可以像这样使用:

public class MyModelClass
{
    [JsonProperty("first_field")]
    public string FirstField { get; set; }

    [JsonProperty("second_field")]
    [JsonConverter(typeof(UrlEncodingConverter))]
    public string SecondField { get; set; }
}

答案 2 :(得分:-1)

试试这个:

var myObject = new MyModelClass("blablabla", WebUtility.UrlEncode("<>@%#^^@!%"));

您需要将System.Net添加到Using-directives