Reputation: 3725
Say I have a model class like this:
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;
}
}
and let's say I have an instance of this type:
var myObject = new MyModelClass("blablabla", "<>@%#^^@!%");
When I convert this object into a Json string using Json.net, I get something like:
{ 'first_field': 'blablabla', 'second_field': '<>@%#^^@!%' }
Is there a way to configure Json.net so that the content of 'SecondField' is URL encoded? Do I need to write my own custom converter or is there a simpler way?
Upvotes: 4
Views: 3894
Reputation: 3725
The answer provided by @brian-rogers is excellent, but I wanted to present an alternative that, I believe, is simpler:
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;
}
}
which can be used like so:
public class MyModelClass
{
[JsonProperty("first_field")]
public string FirstField { get; set; }
[JsonProperty("second_field")]
[JsonConverter(typeof(UrlEncodingConverter))]
public string SecondField { get; set; }
}
Upvotes: 1
Reputation: 129797
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:
UrlEncode
attribute and have the resolver look for properties with that attribute rather than the absence of an AllowHtml
attribute.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
Upvotes: 6
Reputation: 21
Try this:
var myObject = new MyModelClass("blablabla", WebUtility.UrlEncode("<>@%#^^@!%"));
You need to add System.Net
to your Using-directives
Upvotes: -1