Especificando um formato DateTime personalizado ao serializar com o Json.Net

137

Estou desenvolvendo uma API para expor alguns dados usando a API da Web do ASP.NET.

Em uma das APIs, o cliente deseja que exponha a data no yyyy-MM-ddformato. Não quero alterar as configurações globais (por exemplo GlobalConfiguration.Configuration.Formatters.JsonFormatter) para isso, pois é muito específico para este cliente. E desenvolvo isso em uma solução para vários clientes.

Uma das soluções que eu consegui pensar é criar um costume JsonConvertere, em seguida, colocá-lo na propriedade de que preciso fazer a formatação personalizada

por exemplo

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

Apenas imaginando se existe alguma outra maneira fácil de fazer isso.

Permaneça tolo
fonte
16
Quanto vale a pena, as APIs são para legibilidade do computador, não para o usuário. Por isso, é melhor seguir um único formato de data especificado, como ISO 8601 . Se o cliente estiver exibindo diretamente o resultado da API para o usuário ou gravando seu próprio código de análise de data para a API, ele estará fazendo errado. A formatação de uma data para exibição deve ser deixada na camada superior da interface do usuário.
MCattle
Crie a API da Web usando o Visual Studio 2019, corrigido pela Formatting DateTime no ASP.NET Core 3.0 usando System.Text.Json
Stephen

Respostas:

162

Você está no caminho certo. Como você disse que não pode modificar as configurações globais, a próxima melhor coisa a fazer é aplicar o JsonConverteratributo conforme necessário, conforme sugerido. Acontece que o Json.Net já possui um built-in IsoDateTimeConverterque permite especificar o formato da data. Infelizmente, você não pode definir o formato por meio do JsonConverteratributo, pois o único argumento do atributo é um tipo. No entanto, existe uma solução simples: subclasse the e IsoDateTimeConverter, em seguida, especifique o formato da data no construtor da subclasse. Aplique o JsonConverteratributo quando necessário, especificando seu conversor personalizado e você estará pronto para começar. Aqui está a totalidade do código necessário:

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

Se você não se importa em ter tempo lá também, não precisa nem subclassificar o IsoDateTimeConverter. Seu formato de data padrão é yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK(como visto no código-fonte ).

Brian Rogers
fonte
1
@ Koen Zomers - As aspas simples que você removeu dos meus formatos de data estão tecnicamente corretas, embora não sejam estritamente necessárias aqui. Consulte Delimitadores de seqüência de caracteres literais na documentação para seqüências de caracteres de formato de data e hora personalizadas . No entanto, o formato que citei como formato padrão IsonDateTimeConverterfoi extraído diretamente do código-fonte do Json.Net ; portanto, estou revertendo sua edição nisso.
Brian Rogers
não funcionou aqui com as aspas e funcionou sem elas, mas se você diz que deveria, provavelmente fiz algo errado. Desculpe pela edição.
Koen Zomers
96

Você pode usar esta abordagem:

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

E use-o desta maneira:

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

A string DateTimeFormat usa a sintaxe de string do formato .NET descrita aqui: https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings

Keith Hill
fonte
5
Isso não funciona para mim - eu recebo'JsonConverterAttribute' does not contain a constructor that takes 2 arguments
Tam Coton
1
Esta é a solução mais flexível. Se você receber o seguinte erro 'JsonConverterAttribute' does not contain a constructor that takes 2 arguments:, significa que sua versão do json.net é muito antiga. Você precisa atualizar para a versão mais recente do json.net.
Florian Lavorel 23/08/19
Funciona para mim. Alguma idéia de como posso remover o tempo? Portanto, retorne apenas 2020-02-12, por exemplo, com o T00: 00: 00
Enrico
53

Isso também pode ser feito com uma IsoDateTimeConverterinstância, sem alterar as configurações globais de formatação:

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

Isso usa a JsonConvert.SerializeObjectsobrecarga que recebe um params JsonConverter[]argumento.

Saeb Amini
fonte
5
Se você está serialização mesmo objeto classe em muitos lugares, resposta, então aceita é melhor do que este
kgzdev
16

Também disponível usando uma das sobrecargas das configurações do serializador:

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

Ou

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

Sobrecargas com um tipo também estão disponíveis.

Matt
fonte
2
Para sua informação, acho que você quer dizer yyyy-MM-ddTHH:mm:ssZ.. 24 horas por dia.
Neek
9

Crie uma classe auxiliar e aplique-a ao atributo de propriedade

Classe auxiliar:

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

Seu código usa assim:

[JsonConverter(typeof(ESDateTimeConverter))]
public DateTime timestamp { get; set; }
Xin
fonte
8
Por que você acabou de reiterar o que várias outras pessoas já declararam?
Liam
3

Há outra solução que estou usando. Apenas crie uma propriedade string e use-a para json. Esta propriedade retornará a data formatada corretamente.

class JSonModel {
    ...

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

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

    ...
}

Dessa forma, você não precisa criar classes extras. Além disso, permite criar diferentes formatos de dados. por exemplo, você pode criar facilmente outra propriedade por hora usando o mesmo DateTime.

Antonio Rodríguez
fonte
0

Algumas vezes, a decoração do atributo json convert não funcionará, por exceção dizendo que " 2010-10-01" é uma data válida . Para evitar esses tipos, removi o atributo json convert na propriedade e mencionei no método deserilizedObject como abaixo.

var addresss = JsonConvert.DeserializeObject<AddressHistory>(address, new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd" });
Muni Chittem
fonte
0

Com conversor abaixo

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

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

Pode usá-lo com um formato personalizado padrão

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

Ou qualquer formato especificado para uma propriedade

class ReturnObjectB 
{
    [JsonConverter(typeof(DateFormatConverter), "dd MMM yy")]
    public DateTime ReturnDate { get;set;}
}
Ranga
fonte