Указание пользовательского формата DateTime при сериализации с Json.Net

137

Я разрабатываю API для предоставления некоторых данных с использованием ASP.NET Web API.

В одном из API клиент хочет, чтобы мы выставили дату в yyyy-MM-ddформате. Я не хочу изменять глобальные настройки (например GlobalConfiguration.Configuration.Formatters.JsonFormatter) для этого, так как это очень специфично для этого клиента. И я занимаюсь разработкой этого решения для нескольких клиентов.

Одним из решений, которое я мог бы придумать, является создание пользовательского JsonConverterи добавление его к свойству, которое мне нужно для пользовательского форматирования.

например

class ReturnObjectA 
{
    [JsonConverter(typeof(CustomDateTimeConverter))]
    public DateTime ReturnDate { get;set;}
}

Просто интересно, есть ли другой простой способ сделать это.

Оставаться глупым
источник
16
Что бы это ни стоило, API-интерфейсы предназначены для удобочитаемости компьютера, а не читаемости пользователем, поэтому лучше придерживаться одного указанного формата даты, такого как ISO 8601 . Если клиент напрямую отображает результат API для пользователя или пишет собственный код анализа даты для API, то он делает это неправильно. Форматирование даты для отображения следует оставить верхнему слою пользовательского интерфейса.
MCattle
Создание веб-API с помощью Visual Studio 2019, исправлено с помощью Форматирования DateTime в ASP.NET Core 3.0 с использованием System.Text.Json
Стивен

Ответы:

162

Ты на правильном пути. Так как вы сказали, что не можете изменять глобальные настройки, то лучше всего применять JsonConverterатрибут по мере необходимости, как вы и предлагали. Оказывается, в Json.Net уже есть встроенная функция IsoDateTimeConverter, позволяющая указать формат даты. К сожалению, вы не можете установить формат через JsonConverterатрибут, так как единственным аргументом атрибута является тип. Однако есть простое решение: создать подкласс IsoDateTimeConverter, затем указать формат даты в конструкторе подкласса. Примените JsonConverterатрибут там, где это необходимо, указав свой собственный конвертер, и вы готовы к работе. Вот весь необходимый код:

class CustomDateTimeConverter : IsoDateTimeConverter
{
    public CustomDateTimeConverter()
    {
        base.DateTimeFormat = "yyyy-MM-dd";
    }
}

Если вы также не против иметь время, вам даже не нужно создавать подкласс IsoDateTimeConverter. Формат даты по умолчанию yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK(как видно из исходного кода ).

Брайан Роджерс
источник
1
@ Koen Zomers - Единичные кавычки, которые вы удалили из моих форматов дат, технически верны, хотя они здесь не являются строго необходимыми. См. Разделители буквенных строк в документации для пользовательских строк формата даты и времени . Однако формат, который я цитировал в качестве формата по умолчанию для, IsonDateTimeConverterбыл взят непосредственно из исходного кода Json.Net ; поэтому я возвращаю ваше редактирование по этому вопросу.
Брайан Роджерс
это не сработало здесь с кавычками и без них, но если вы скажете, что должно, я, вероятно, сделал что-то не так. Извините за редактирование.
Коен Зомерс
96

Вы можете использовать этот подход:

public class DateFormatConverter : IsoDateTimeConverter
{
    public DateFormatConverter(string format)
    {
        DateTimeFormat = format;
    }
}

И используйте это так:

class ReturnObjectA 
{
    [JsonConverter(typeof(DateFormatConverter), "yyyy-MM-dd")]
    public DateTime ReturnDate { get;set;}
}

В строке DateTimeFormat используется синтаксис строки формата .NET, описанный здесь: https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings

Кит Хилл
источник
5
Это не работает для меня - я получаю'JsonConverterAttribute' does not contain a constructor that takes 2 arguments
Там Котон
1
Это самое гибкое решение. Если вы получаете следующую ошибку:, 'JsonConverterAttribute' does not contain a constructor that takes 2 argumentsэто означает, что ваша версия json.net слишком старая. Вам необходимо выполнить обновление до последней версии json.net.
Флориан Лаворель
Работает для меня. Есть идеи, как убрать время? Так что возвращайте только 2020-02-12, например, с T00: 00: 00
Энрико
53

Это также может быть сделано с IsoDateTimeConverterэкземпляром, без изменения глобальных настроек форматирования:

string json = JsonConvert.SerializeObject(yourObject,
    new IsoDateTimeConverter() { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" });

Это использует JsonConvert.SerializeObjectперегрузку, которая принимает params JsonConverter[]аргумент.

Саиб Амини
источник
5
Если вы сериализуете один и тот же объект класса во многих местах, тогда принятый ответ лучше, чем этот
kgzdev
16

Также доступно использование одной из перегрузок настроек сериализатора:

var json = JsonConvert.SerializeObject(someObject, new JsonSerializerSettings() { DateFormatString = "yyyy-MM-ddThh:mm:ssZ" });

Или

var json = JsonConvert.SerializeObject(someObject, Formatting.Indented, new JsonSerializerSettings() { DateFormatString = "yyyy-MM-ddThh:mm:ssZ" });

Перегрузки, принимающие Тип, также доступны.

Matt
источник
2
К вашему сведению, вы имеете в виду yyyy-MM-ddTHH:mm:ssZ... 24 часа в сутки.
Neek
9

Создайте вспомогательный класс и примените его к атрибуту вашего свойства

Хелпер класс:

public class ESDateTimeConverter : IsoDateTimeConverter
{
    public ESDateTimeConverter()
    {
        base.DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffZ";
    }
}

Ваш код использовать так:

[JsonConverter(typeof(ESDateTimeConverter))]
public DateTime timestamp { get; set; }
Xin
источник
8
Почему вы только что повторили то, что уже заявили несколько других людей?
Лиам
3

Есть еще одно решение, которое я использовал. Просто создайте строковое свойство и используйте его для json. Это свойство вернет дату, правильно отформатированную.

class JSonModel {
    ...

    [JsonProperty("date")]
    public string MyDate { get; set; }

    public string CustomDate {
        get { return MyDate.ToString("DDMMYY"); }
        set { MyDate = DateTime.Parse(value); }
    }

    ...
}

Таким образом, вам не нужно создавать дополнительные классы. Кроме того, он позволяет создавать разные форматы данных. Например, вы можете легко создать другое свойство для часа, используя тот же DateTime.

Антонио Родригес
источник
0

Иногда декорирование атрибута json convert не будет работать, за исключением того, что « 2010-10-01» является допустимой датой . Чтобы избежать этого, я удалил атрибут json convert в свойстве и упомянул в методе deserilizedObject, как показано ниже.

var addresss = JsonConvert.DeserializeObject<AddressHistory>(address, new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd" });
Муни Читтем
источник
0

С ниже конвертер

public class CustomDateTimeConverter : IsoDateTimeConverter
    {
        public CustomDateTimeConverter()
        {
            DateTimeFormat = "yyyy-MM-dd";
        }

        public CustomDateTimeConverter(string format)
        {
            DateTimeFormat = format;
        }
    }

Можно использовать его с пользовательским форматом по умолчанию

class ReturnObjectA 
{
    [JsonConverter(typeof(DateFormatConverter))]
    public DateTime ReturnDate { get;set;}
}

Или любой указанный формат для свойства

class ReturnObjectB 
{
    [JsonConverter(typeof(DateFormatConverter), "dd MMM yy")]
    public DateTime ReturnDate { get;set;}
}
Ранга
источник