Визначення користувацького формату DateTime під час серіалізації з Json.Net


137

Я розробляю API для викриття деяких даних за допомогою веб-API ASP.NET.

В одному з 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
Stephen

Відповіді:


162

Ви на правильному шляху. Оскільки ви сказали, що не можете змінювати глобальні налаштування, то наступне найкраще - застосувати JsonConverterатрибут за потребою, як вам було запропоновано. Виявляється, Json.Net вже має вбудований, IsoDateTimeConverterякий дозволяє задавати формат дати. На жаль, ви не можете встановити формат через JsonConverterатрибут, оскільки єдиним аргументом атрибута є тип. Однак існує просте рішення: підклас the 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" });

Також можливі перевантаження, що приймають тип.


2
FYI Я думаю, ти маєш на увазі 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; }

8
Чому ви просто повторили те, що вже заявили кілька інших людей?
Ліам

3

Є ще одне рішення, яким я користувався. Просто створіть властивість string та використовуйте її для 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;}
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.