Określanie niestandardowego formatu daty i godziny podczas serializacji z Json.Net

137

Opracowuję interfejs API w celu udostępnienia niektórych danych przy użyciu interfejsu API sieci Web ASP.NET.

W jednym z API klient chce, abyśmy ujawnili datę w yyyy-MM-ddformacie. Nie chcę zmieniać ustawień globalnych (np. GlobalConfiguration.Configuration.Formatters.JsonFormatter), Ponieważ jest to bardzo specyficzne dla tego klienta. I rozwijam to w rozwiązaniu dla wielu klientów.

Jednym z rozwiązań, które przychodzi mi do głowy, jest utworzenie niestandardowego, JsonConvertera następnie umieszczenie go we właściwości, której potrzebuję, aby wykonać niestandardowe formatowanie

na przykład

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

Zastanawiam się tylko, czy istnieje inny łatwy sposób na zrobienie tego.

Zostać głupim
źródło
16
Co ważne, interfejsy API służą czytelności komputera, a nie czytelności dla użytkownika, dlatego lepiej trzymać się jednego określonego formatu daty, takiego jak ISO 8601 . Jeśli klient bezpośrednio wyświetla wynik interfejsu API użytkownikowi lub pisze własny kod analizujący datę dla interfejsu API, robi to źle. Formatowanie daty do wyświetlenia należy pozostawić na najwyższej warstwie interfejsu użytkownika.
MCattle
Tworzenie internetowego interfejsu API przy użyciu programu Visual Studio 2019, naprawione przez formatowanie daty i godziny w ASP.NET Core 3.0 przy użyciu System.Text.Json
Stephen

Odpowiedzi:

162

Jesteś na dobrej drodze. Ponieważ powiedziałeś, że nie możesz modyfikować ustawień globalnych, następną najlepszą rzeczą jest zastosowanie JsonConverteratrybutu w razie potrzeby, tak jak zasugerowałeś. Okazuje się, że Json.Net ma już wbudowaną funkcję, IsoDateTimeConverterktóra pozwala określić format daty. Niestety nie możesz ustawić formatu za pomocą JsonConverteratrybutu, ponieważ jedynym argumentem atrybutu jest typ. Jest jednak proste rozwiązanie: podklasę podklasy IsoDateTimeConverter, a następnie określ format daty w konstruktorze podklasy. Zastosuj JsonConverteratrybut w razie potrzeby, określając swój niestandardowy konwerter i gotowe. Oto cały potrzebny kod:

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

Jeśli nie masz nic przeciwko temu, by mieć tam czas, nie musisz nawet tworzyć podklasy IsoDateTimeConverter. Jego domyślny format daty to yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK(jak widać w kodzie źródłowym ).

Brian Rogers
źródło
1
@Koen Zomers - Pojedyncze cudzysłowy, które usunąłeś z moich formatów daty, SĄ poprawne technicznie, chociaż nie są tutaj bezwzględnie konieczne. Zobacz ograniczniki ciągów literalnych w dokumentacji dla ciągów niestandardowego formatu daty i godziny . Jednak format, który podałem jako domyślny format, IsonDateTimeConverterzostał pobrany bezpośrednio z kodu źródłowego Json.Net ; dlatego cofam twoją edycję w tej sprawie.
Brian Rogers
nie działało tutaj z cytatami i bez nich, ale jeśli powiesz, że powinno, prawdopodobnie zrobiłem coś złego. Przepraszamy za zmianę.
Koen Zomers
96

Możesz użyć tego podejścia:

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

I użyj tego w ten sposób:

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

Ciąg DateTimeFormat używa składni ciągu formatu .NET opisanej tutaj: https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings

Keith Hill
źródło
5
To nie działa dla mnie - rozumiem'JsonConverterAttribute' does not contain a constructor that takes 2 arguments
Tam Coton
1
To najbardziej elastyczne rozwiązanie. Jeśli pojawi się następujący błąd: 'JsonConverterAttribute' does not contain a constructor that takes 2 argumentsoznacza to, że Twoja wersja json.net jest za stara. Musisz zaktualizować do najnowszej wersji json.net.
Florian Lavorel
Pracuje dla mnie. Masz pomysł, jak mogę usunąć czas? Więc powróć tylko 2020-02-12 na przykład z T00: 00: 00
Enrico
53

Można to również zrobić za pomocą IsoDateTimeConverterinstancji, bez zmiany globalnych ustawień formatowania:

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

Używa JsonConvert.SerializeObjectprzeciążenia, które przyjmuje params JsonConverter[]argument.

Saeb Amini
źródło
5
Jeśli serializujesz ten sam obiekt klasy w wielu miejscach, to zaakceptowana odpowiedź jest lepsza niż ta
kgzdev
16

Dostępne również przy użyciu jednego z przeciążeń ustawień serializatora:

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

Lub

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

Dostępne są również przeciążenia przyjmujące typ.

Matt
źródło
2
FYI, myślę, że masz na myśli yyyy-MM-ddTHH:mm:ssZ... 24-godzinny zegar na godziny.
Neek
9

Zbuduj klasę pomocniczą i zastosuj ją do atrybutu właściwości

Klasa pomocnika:

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

Twój kod wygląda następująco:

[JsonConverter(typeof(ESDateTimeConverter))]
public DateTime timestamp { get; set; }
Xin
źródło
8
Dlaczego właśnie powtórzyłeś to, co kilka innych osób już powiedziało?
Liam
3

Jest inne rozwiązanie, z którego korzystałem. Po prostu utwórz właściwość string i użyj jej dla json. Ta właściwość zwróci odpowiednio sformatowaną datę.

class JSonModel {
    ...

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

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

    ...
}

W ten sposób nie musisz tworzyć dodatkowych zajęć. Umożliwia także tworzenie różnych formatów danych. np. możesz łatwo utworzyć kolejną właściwość dla godziny przy użyciu tej samej daty i godziny.

Antonio Rodríguez
źródło
0

Czasami dekorowanie atrybutu konwersji json nie zadziała, będzie to wynikać z wyjątku mówiącego, że „ 2010-10-01” jest prawidłową datą . Aby uniknąć tego typu, usunąłem atrybut json convert we właściwości i wspomniano o tym w metodzie deserilizedObject, jak poniżej.

var addresss = JsonConvert.DeserializeObject<AddressHistory>(address, new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd" });
Muni Chittem
źródło
0

Z poniższym konwerterem

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

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

Można go używać z domyślnym formatem niestandardowym

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

Lub jakikolwiek określony format właściwości

class ReturnObjectB 
{
    [JsonConverter(typeof(DateFormatConverter), "dd MMM yy")]
    public DateTime ReturnDate { get;set;}
}
Ranga
źródło