I wrote a custom JsonConverter called CompententsConverter
and it works fine, however I'm curious if there is a way to make use of the alternate constructor which takes params object[] converterParameters and pass over my own custom parameters from the attribute accordingly.
Likewise, I am not sure how to actually retrieve the parameters inside the JsonConverter class definition or if it's even possible to do this with a JsonConverter attribute.
inside the model, attribute with theoretical params, where some_parameter_here
is a placeholder for a constant expression:
[JsonProperty("components")]
[JsonConverter(typeof(ComponentsConverter), some_parameter_here)]
public List<ComponentModel> Components { get; set; }
ComponentsConverter custom JsonConverter:
public class ComponentsConverter : JsonConverter
{
public override bool CanConvert (Type t) => t == typeof(List<ComponentModel>);
public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// ... any way to access params object[] customParameters here?
}
public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer)
{
// ...
}
}
Would be nice to be able to define some custom conversion behavior for specific model properties by using these extra params.
Json.NET basically just calls Activator.CreateInstance(ConverterType, ConverterParameters)
[1] so the converter parameters are passed into the converter's constructor. You can remember them there and use them in ReadJson()
and WriteJson()
e.g. like so:
public class ComponentsConverter : JsonConverter
{
public string CustomString { get; init; }
public ComponentsConverter(string customString)
{
// Remember the converter parameters for use in WriteJson() and ReadJson()
this.CustomString = customString;
}
public override bool CanConvert (Type t) => t == typeof(List<ComponentModel>);
public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer)
{
// Customize the serialized contents using the CustomString passed in to the constructor
writer.WriteValue(CustomString);
}
public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// ...
}
}
For a converter applied to the items of a collection, use JsonPropertyAttribute.ItemConverterType
with JsonPropertyAttribute.ItemConverterParameters
. E.g.:
[JsonProperty("components",
ItemConverterType = typeof(ComponentConverter),
ItemConverterParameters = new object [] { "custom_string_value" })]
And then:
public class ComponentConverter : JsonConverter
{
public string CustomString { get; init; }
public ComponentConverter(string customString)
{
// Remember the converter parameters for use in WriteJson() and ReadJson()
this.CustomString = customString;
}
public override bool CanConvert (Type t) => t == typeof(ComponentModel);
public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer)
{
// Customize the serialized contents using the CustomString passed in to the constructor
writer.WriteValue(CustomString);
}
public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// ...
}
}
Footnotes:
[1]: I'm oversimplifying a little here. It actually uses code-generation techniques to generate and cache delegates on the fly that do the same thing as Activator.CreateInstance()
but without the performance penalties of late-bound reflection. See e.g. ExpressionReflectionDelegateFactory.ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
.