Почему Json.net не использует настроенный IsoDateTimeConverter? - PullRequest
2 голосов
/ 17 июня 2011

Я использую Json.net для сериализации своих объектов и хочу настроить DateTime вывод:

Вот небольшой пример:

[DataContract]
class x
{
    [DataMember]
    [JsonConverter(typeof(IsoDateTimeConverter))]
    public DateTime datum = new DateTime(1232, 3, 23);
}

var dtc = new IsoDateTimeConverter();
dtc.DateTimeFormat = "yy";
JsonConvert.SerializeObject(new x(), dtc);

Результат {"datum":"1232-03-23T00:00:00"} вместо {"datum":"1232"}.

Это работает правильно (возвращает "32"):

return JsonConvert.SerializeObject(new DateTime(1232, 3, 23), dtc);

Где подвох?

1 Ответ

1 голос
/ 19 декабря 2016

Подвох состоит в том, что преобразователь, примененный через [JsonConverter(typeof(IsoDateTimeConverter))] , заменяет преобразователь, переданный в сериализатор. Это задокументировано в Атрибуты сериализации: JsonConverterAttribute :

The JsonConverterAttribute указывает, какой JsonConverter используется для преобразования объекта.

Атрибут может быть помещен в класс или член. Когда помещено на класс, JsonConverter, указанный атрибутом, будет способ сериализации этого класса по умолчанию. Когда атрибут находится на поле или свойство, тогда указанный JsonConverter всегда будет используется для сериализации этого значения.

Приоритет, для которого используется JsonConverter - это атрибут member атрибут класса, и, наконец, любые преобразователи, переданные в JsonSerializer.

В качестве обходного пути в методах ReadJson() и WriteJson() применяемого преобразователя можно найти соответствующий преобразователь в списке преобразователей сериализатора, и, если он найден, использовать его. Шаблон декоратора может использоваться для отделения этой логики от базовой логики преобразования. Сначала введем:

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; } }
}

Затем примените декоратор поверх IsoDateTimeConverter следующим образом:

[DataContract]
class x
{
    [DataMember]
    [JsonConverter(typeof(OverridableJsonConverterDecorator), typeof(IsoDateTimeConverter))]
    public DateTime datum = new DateTime(1232, 3, 23);
}

Теперь статически применяемый конвертер будет заменен по мере необходимости. Образец скрипка .

Обратите внимание, что для этого конкретного контрольного примера с даты Json.NET 4.5.1 по умолчанию сериализуются в ISO и IsoDateTimeConverter больше не требуется. Принудительное выполнение сериализации дат в определенном формате можно выполнить, установив 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"}

Образец Скрипка . Тем не менее общий вопрос заслуживает ответа.

...