If it's just one field in one class, you could simply add a read-only property to your class to handle the encoding, and annotate it such that it will take the place of the original property during serialization:
public class MyModelClass
{
...
[JsonIgnore]
public string SecondField { get; set; }
[JsonProperty("second_field")]
private string UrlEncodedSecondField
{
get { return System.Web.HttpUtility.UrlEncode(SecondField); }
}
...
}
Demo fiddle: https://dotnetfiddle.net/MkVBVH
If you need this for multiple fields across multiple classes, you could use a solution similar to the one in Selectively escape HTML in strings during deserialization, with a couple of minor changes:
- Create a custom
UrlEncode
attribute and have the resolver look for properties with that attribute rather than the absence of an AllowHtml
attribute.
- Change the
HtmlEncodingValueProvider
to a UrlEncodingValueProvider
and have it apply the encoding in GetValue
rather than SetValue
(so that it does the encoding on serialization rather than deserialization).
Here is what the resulting code would look like:
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);
}
}
}
To use the custom resolver, first decorate any properties that you want to be URL encoded with the new [UrlEncode]
attribute:
public class MyModelClass
{
[JsonProperty("first_field")]
public string FirstField { get; set; }
[UrlEncode]
[JsonProperty("second_field")]
public string SecondField { get; set; }
...
}
Then, serialize your model like this:
var myObject = new MyModelClass("blablabla", "<>@%#^^@!%");
var settings = new JsonSerializerSettings
{
ContractResolver = new CustomResolver(),
Formatting = Formatting.Indented
};
string json = JsonConvert.SerializeObject(myObject, settings);
Demo fiddle: https://dotnetfiddle.net/iOOzFr