Reputation: 4511
I have a need to specify a JsonConverter
for properties which are decorated with a specific attribute, in this case [DataType(DataType.PostalCode)]
.
I already have a custom JsonConverter
for which I have set the CanConvert
method as follows:
public override bool CanConvert(Type objectType) => objectType == typeof(string);
How can I make sure the PostcodeJsonConverter
is used instead when the API encounters a PostalCode
property?
[DataType(DataType.PostalCode)]
public string Postcode { get; set; }
I've tried the following but I suspect the DataType
attribute is not available at this point.
public override bool CanConvert(Type objectType) =>
objectType == typeof(string) &&
objectType.GetCustomAttributes(true)
.OfType<DataTypeAttribute>()
.Any(dta => dta.DataType == DataType.PostalCode);
Do I need to decorate my model as follows instead?
[DataType(DataType.PostalCode)]
[JsonConverter(typeof(PostcodeJsonConverter))]
public string Postcode { get; set; }
Upvotes: 2
Views: 1360
Reputation: 129707
You can make a custom ContractResolver
that looks for your DataType
attribute on each property and maps the values to the appropriate converter. Here is the code you would need:
public class DataTypeResolver : DefaultContractResolver
{
private Dictionary<DataType, JsonConverter> ConvertersByDataType { get; set; }
public DataTypeResolver()
{
// Adjust this list to match your actual data types and converters
ConvertersByDataType = new Dictionary<DataType, JsonConverter>
{
{ DataType.PostalCode, new PostalCodeConverter() },
{ DataType.PhoneNumber, new PhoneNumberConverter() },
};
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
var att = prop.AttributeProvider.GetAttributes(true).OfType<DataTypeAttribute>().FirstOrDefault();
if (att != null)
{
JsonConverter converter;
if (ConvertersByDataType.TryGetValue(att.DataType, out converter))
{
prop.Converter = converter;
}
}
return prop;
}
}
Then pass the resolver to SerializeObject
and/or DeserializeObject
via the settings:
var settings = new JsonSerializerSettings
{
ContractResolver = new DataTypeResolver()
};
string json = JsonConvert.SerializeObject(yourObject, settings);
Here is a working demo: https://dotnetfiddle.net/k1kWv5
Upvotes: 3
Reputation: 5574
You can add Converters to the JsonSerializerSettings. So instead of decorating everything, you may as well add your PostcodeJsonConverter there (depending on how often it is used, a decorator may be better though):
For aspnet core defaults:
services.AddMvc().AddJsonOptions(o => o.SerializerSettings.Converters.Add(new PostcodeJsonConverter()))
For JsonConvert:
JsonConvert.SerializeObject(obj, new JsonSerializerSettings
{
Converters = { new PostcodeJsonConverter() }
});
Upvotes: 1