Especificar un formato de fecha y hora personalizado al serializar con Json.Net

137

Estoy desarrollando una API para exponer algunos datos usando la API web ASP.NET.

En una de las API, el cliente quiere que expongamos la fecha en yyyy-MM-ddformato. No quiero cambiar la configuración global (por ejemplo GlobalConfiguration.Configuration.Formatters.JsonFormatter) para eso, ya que es muy específico para este cliente. Y lo desarrollo en una solución para múltiples clientes.

Una de las soluciones que se me JsonConverterocurre es crear una personalizada y luego ponerla en la propiedad que necesito para el formateo personalizado.

p.ej

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

Solo me pregunto si hay alguna otra manera fácil de hacerlo.

Mantenerse tonto
fuente
16
Por lo que vale, las API son para la legibilidad de la computadora, no para la legibilidad del usuario, por lo que es mejor atenerse a un único formato de fecha especificado, como ISO 8601 . Si el cliente muestra directamente el resultado de la API al usuario, o escribe su propio código de análisis de fechas para la API, entonces lo está haciendo mal. El formateo de una fecha para la visualización debe dejarse en la capa superior de la interfaz de usuario.
MCattle
Cree API web utilizando Visual Studio 2019, arreglado formateando DateTime en ASP.NET Core 3.0 usando System.Text.Json
Stephen

Respuestas:

162

Estás en el camino correcto. Como usted dijo que no puede modificar la configuración global, lo mejor que puede hacer es aplicar el JsonConverteratributo según sea necesario, como lo sugirió. Resulta que Json.Net ya tiene un incorporado IsoDateTimeConverterque le permite especificar el formato de fecha. Desafortunadamente, no puede establecer el formato a través del JsonConverteratributo, ya que el único argumento del atributo es un tipo. Sin embargo, hay una solución simple: subclase IsoDateTimeConverter, luego especifique el formato de fecha en el constructor de la subclase. Aplique el JsonConverteratributo donde sea necesario, especifique su convertidor personalizado y estará listo para comenzar. Aquí está la totalidad del código necesario:

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

Si no le importa tener tiempo allí también, ni siquiera necesita subclasificar el IsoDateTimeConverter. Su formato de fecha predeterminado es yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK(como se ve en el código fuente ).

Brian Rogers
fuente
1
@Koen Zomers: las comillas simples que eliminó de mis formatos de fecha técnicamente SON correctas, aunque no son estrictamente necesarias aquí. Consulte Delimitadores de cadenas literales en la documentación para cadenas de formato de fecha y hora personalizadas . Sin embargo, el formato que cité como el formato predeterminado para el IsonDateTimeConverterfue tomado directamente del código fuente de Json.Net ; por lo tanto, estoy volviendo tu edición sobre eso.
Brian Rogers el
no funcionó aquí con las citas y funcionó sin ellas, pero si dices que debería, probablemente hice algo mal. Perdón por la edición.
Koen Zomers
96

Podrías usar este enfoque:

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

Y úsalo de esta manera:

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

La cadena DateTimeFormat utiliza la sintaxis de cadena en formato .NET descrita aquí: https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings

Keith Hill
fuente
55
Esto no funciona para mí - entiendo'JsonConverterAttribute' does not contain a constructor that takes 2 arguments
Tam Coton
1
Esta es la solución más flexible. Si obtiene el siguiente error: 'JsonConverterAttribute' does not contain a constructor that takes 2 argumentssignifica que su versión de json.net es demasiado antigua. Necesita actualizar a la última versión de json.net.
Florian Lavorel
Funciona para mi. ¿Alguna idea de cómo puedo eliminar el tiempo? Entonces, solo regrese 2020-02-12, por ejemplo, con el T00: 00: 00
Enrico
53

También se puede hacer con una IsoDateTimeConverterinstancia, sin cambiar la configuración de formato global:

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

Esto usa la JsonConvert.SerializeObjectsobrecarga que toma un params JsonConverter[]argumento.

Saeb Amini
fuente
55
Si está serializando el mismo objeto de clase en muchos lugares, entonces la respuesta aceptada es mejor que esto
kgzdev
16

También disponible con una de las sobrecargas de configuración del serializador:

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

O

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

Las sobrecargas que toman un tipo también están disponibles.

Mate
fuente
2
FYI creo que te refieres yyyy-MM-ddTHH:mm:ssZ.. 24 horas en las horas.
Neek
9

Cree una clase auxiliar y aplíquela a su atributo de propiedad

Clase auxiliar:

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

Su código se usa así:

[JsonConverter(typeof(ESDateTimeConverter))]
public DateTime timestamp { get; set; }
Xin
fuente
8
¿Por qué acabas de repetir lo que otras personas ya han dicho?
Liam
3

Hay otra solución que he estado usando. Simplemente cree una propiedad de cadena y úsela para json. Esta propiedad tendrá una fecha de devolución con el formato correcto.

class JSonModel {
    ...

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

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

    ...
}

De esta manera no tienes que crear clases adicionales. Además, le permite crear diferentes formatos de datos. por ejemplo, puede crear fácilmente otra propiedad por hora utilizando la misma fecha y hora.

Antonio Rodríguez
fuente
0

Algunas veces decorar el atributo json convert no funcionará, lo hará a través de la excepción diciendo que " 2010-10-01" es una fecha válida . Para evitar este tipo, eliminé el atributo json convert en la propiedad y lo mencioné en el método deserilizedObject como se muestra a continuación.

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

Con el siguiente convertidor

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

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

Puede usarlo con un formato personalizado predeterminado

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

O cualquier formato especificado para una propiedad

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