The catch is that the converter applied via [JsonConverter(typeof(IsoDateTimeConverter))]
supersedes the converter passed into the serializer. This is documented in Serialization Attributes: JsonConverterAttribute:
The
JsonConverterAttribute
specifies which
JsonConverter
is used to convert an object.
The attribute can be placed on a class or a member. When placed on a
class, the JsonConverter specified by the attribute will be the
default way of serializing that class. When the attribute is on a
field or property, then the specified JsonConverter will always be
used to serialize that value.
The priority of which JsonConverter is used is member attribute, then
class attribute, and finally any converters passed to the
JsonSerializer.
As a workaround, in the ReadJson()
and WriteJson()
methods of the applied converter, one could check for a relevant converter in the serializer's list of converters, and if one is found, use it. The decorator pattern can be used to separate this logic from the underlying conversion logic. First, introduce:
public class OverridableJsonConverterDecorator : JsonConverterDecorator
{
public OverridableJsonConverterDecorator(Type jsonConverterType) : base(jsonConverterType) { }
public OverridableJsonConverterDecorator(JsonConverter converter) : base(converter) { }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
foreach (var converter in serializer.Converters)
{
if (converter == this)
{
Debug.WriteLine("Skipping identical " + converter.ToString());
continue;
}
if (converter.CanConvert(value.GetType()) && converter.CanWrite)
{
converter.WriteJson(writer, value, serializer);
return;
}
}
base.WriteJson(writer, value, serializer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
foreach (var converter in serializer.Converters)
{
if (converter == this)
{
Debug.WriteLine("Skipping identical " + converter.ToString());
continue;
}
if (converter.CanConvert(objectType) && converter.CanRead)
{
return converter.ReadJson(reader, objectType, existingValue, serializer);
}
}
return base.ReadJson(reader, objectType, existingValue, serializer);
}
}
public abstract class JsonConverterDecorator : JsonConverter
{
readonly JsonConverter converter;
public JsonConverterDecorator(Type jsonConverterType) : this((JsonConverter)Activator.CreateInstance(jsonConverterType)) { }
public JsonConverterDecorator(JsonConverter converter)
{
if (converter == null)
throw new ArgumentNullException();
this.converter = converter;
}
public override bool CanConvert(Type objectType)
{
return converter.CanConvert(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return converter.ReadJson(reader, objectType, existingValue, serializer);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
converter.WriteJson(writer, value, serializer);
}
public override bool CanRead { get { return converter.CanRead; } }
public override bool CanWrite { get { return converter.CanWrite; } }
}
Then, apply the decorator on top of IsoDateTimeConverter
as follows:
[DataContract]
class x
{
[DataMember]
[JsonConverter(typeof(OverridableJsonConverterDecorator), typeof(IsoDateTimeConverter))]
public DateTime datum = new DateTime(1232, 3, 23);
}
Now the statically applied converter will be superseded as required. Sample fiddle.
Note that, for this specific test case, as of Json.NET 4.5.1 dates are serialized in ISO by default and IsoDateTimeConverter
is no longer required. Forcing dates to be serialized in a specific format can be accomplished by setting JsonSerializerSettings.DateFormatString
:
[DataContract]
class x
{
[DataMember]
public DateTime datum = new DateTime(1232, 3, 23);
}
var settings = new JsonSerializerSettings { DateFormatString = "yy" };
var json1 = JsonConvert.SerializeObject(new x(), settings);
Console.WriteLine(json1); // Prints {"datum":"32"}
var json2 = JsonConvert.SerializeObject(new x());
Console.WriteLine(json2); // Prints {"datum":"1232-03-23T00:00:00"}
Sample fiddle. Nevertheless the general question deserves an answer.