Formato de fecha ASP.NET MVC JsonResult

Resuelto Jon Archway asked hace 15 años • 24 respuestas

Tengo una acción de controlador que efectivamente simplemente devuelve un JsonResult de mi modelo. Entonces, en mi método tengo algo como lo siguiente:

return new JsonResult(myModel);

Esto funciona bien, excepto por un problema. Hay una propiedad de fecha en el modelo y parece devolverse en el resultado Json de la siguiente manera:

"\/Date(1239018869048)\/"

¿Cómo debo manejar las fechas para que se devuelvan en el formato que necesito? ¿O cómo manejo este formato anterior en el script?

Jon Archway avatar Apr 07 '09 22:04 Jon Archway
Aceptado

Solo para ampliar la respuesta de casperOne .

La especificación JSON no tiene en cuenta los valores de fecha. MS tuvo que hacer una llamada, y el camino que eligieron fue explotar un pequeño truco en la representación de cadenas en JavaScript: la cadena literal "/" es la misma que "\/", y una cadena literal nunca se serializará en " \/" (incluso "\/" debe asignarse a "\\/").

Consulte http://msdn.microsoft.com/en-us/library/bb299886.aspx#intro_to_json_topic2 para obtener una mejor explicación (desplácese hacia abajo hasta "De literales de JavaScript a JSON")

Uno de los puntos delicados de JSON es la falta de un literal de fecha/hora. Muchas personas se sorprenden y decepcionan al saber esto cuando encuentran JSON por primera vez. La explicación simple (consoladora o no) para la ausencia de un literal de fecha/hora es que JavaScript tampoco tuvo nunca uno: el soporte para valores de fecha y hora en JavaScript se proporciona completamente a través del objeto Fecha. Por lo tanto, la mayoría de las aplicaciones que utilizan JSON como formato de datos generalmente tienden a utilizar una cadena o un número para expresar valores de fecha y hora. Si se utiliza una cadena, generalmente puede esperar que esté en el formato ISO 8601. Si, en cambio, se utiliza un número, entonces el valor generalmente se considera el número de milisegundos en el Tiempo Universal Coordinado (UTC) desde la época, donde la época se define como la medianoche del 1 de enero de 1970 (UTC). Nuevamente, esto es una mera convención y no forma parte del estándar JSON. Si está intercambiando datos con otra aplicación, deberá consultar su documentación para ver cómo codifica los valores de fecha y hora dentro de un literal JSON. Por ejemplo, ASP.NET AJAX de Microsoft no utiliza ninguna de las convenciones descritas. Más bien, codifica valores .NET DateTime como una cadena JSON, donde el contenido de la cadena es /Fecha(ticks)/ y donde los ticks representan milisegundos desde la época (UTC). Entonces, el 29 de noviembre de 1989, 4:55:30 a. m., en UTC, está codificado como "\/Fecha(628318530718)\/".

Una solución sería simplemente analizarlo:

value = new Date(parseInt(value.replace("/Date(", "").replace(")/",""), 10));

Sin embargo, escuché que hay una configuración en algún lugar para que el serializador genere DateTimeobjetos con la new Date(xxx)sintaxis. Intentaré desenterrar eso.


El segundo parámetro de JSON.parse()acepta una reviverfunción donde prescribe cómo se produjo originalmente el valor antes de ser devuelto.

Aquí hay un ejemplo de fecha:

var parsed = JSON.parse(data, function(key, value) {
  if (typeof value === 'string') {
    var d = /\/Date\((\d*)\)\//.exec(value);
    return (d) ? new Date(+d[1]) : value;
  }
  return value;
});

Ver los documentos de JSON.parse()

JPot avatar Apr 07 '2009 17:04 JPot

Aquí está mi solución en Javascript, muy parecida a la de JPot, pero más corta (y posiblemente un poquito más rápida):

value = new Date(parseInt(value.substr(6)));

"value.substr(6)" elimina la parte "/Date(" y la función parseInt ignora los caracteres no numéricos que aparecen al final.

EDITAR: He omitido intencionalmente la base (el segundo argumento de parseInt); vea mi comentario a continuación . Además, tenga en cuenta que se prefieren las fechas ISO-8601 a este formato antiguo, por lo que este formato generalmente no debe usarse para nuevos desarrollos.

Para fechas JSON con formato ISO-8601, simplemente pase la cadena al constructor Fecha:

var date = new Date(jsonDate); //no ugly parsing needed; full timezone support
Roy Tinker avatar Nov 20 '2009 00:11 Roy Tinker

Hay bastantes respuestas para manejarlo en el lado del cliente, pero puede cambiar el lado del servidor de salida si lo desea.

Hay algunas formas de abordar esto; comenzaré con lo básico. Tendrás que subclasificar la clase JsonResult y anular el método ExecuteResult. A partir de ahí, puede adoptar algunos enfoques diferentes para cambiar la serialización.

Método 1: la implementación predeterminada utiliza JsonScriptSerializer . Si echa un vistazo a la documentación, puede utilizar el método RegisterConverters para agregar JavaScriptConverters personalizados . Sin embargo, hay algunos problemas con esto: JavaScriptConverter se serializa en un diccionario, es decir, toma un objeto y se serializa en un diccionario Json. Para que el objeto se serialice en una cadena, se requiere un poco de piratería, consulte la publicación . Este truco en particular también escapará de la cadena.

public class CustomJsonResult : JsonResult
{
    private const string _dateFormat = "yyyy-MM-dd HH:mm:ss";

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        HttpResponseBase response = context.HttpContext.Response;

        if (!String.IsNullOrEmpty(ContentType))
        {
            response.ContentType = ContentType;
        }
        else
        {
            response.ContentType = "application/json";
        }
        if (ContentEncoding != null)
        {
            response.ContentEncoding = ContentEncoding;
        }
        if (Data != null)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();

            // Use your custom JavaScriptConverter subclass here.
            serializer.RegisterConverters(new JavascriptConverter[] { new CustomConverter });

            response.Write(serializer.Serialize(Data));
        }
    }
}

Método 2 (recomendado): el segundo método es comenzar con el JsonResult anulado e ir con otro serializador Json, en mi caso el serializador Json.NET . Esto no requiere la piratería del enfoque 1. Aquí está mi implementación de la subclase JsonResult:

public class CustomJsonResult : JsonResult
{
    private const string _dateFormat = "yyyy-MM-dd HH:mm:ss";

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        HttpResponseBase response = context.HttpContext.Response;

        if (!String.IsNullOrEmpty(ContentType))
        {
            response.ContentType = ContentType;
        }
        else
        {
            response.ContentType = "application/json";
        }
        if (ContentEncoding != null)
        {
            response.ContentEncoding = ContentEncoding;
        }
        if (Data != null)
        {
            // Using Json.NET serializer
            var isoConvert = new IsoDateTimeConverter();
            isoConvert.DateTimeFormat = _dateFormat;
            response.Write(JsonConvert.SerializeObject(Data, isoConvert));
        }
    }
}

Ejemplo de uso:

[HttpGet]
public ActionResult Index() {
    return new CustomJsonResult { Data = new { users=db.Users.ToList(); } };
}

Créditos adicionales: James Newton-King

Perishable Dave avatar Feb 15 '2012 21:02 Perishable Dave

Moment.js es una extensa biblioteca de fecha y hora que también admite esto. http://momentjs.com/docs/#/parsing/asp-net-json-dates/

ej: momento("/Fecha(1198908717056-0700)/")

Podría ayudar. salida del émbolo

Eric avatar Nov 05 '2012 15:11 Eric

Descubrí que crear algo nuevo JsonResulty regresar es insatisfactorio: tener que reemplazar todas las llamadas a return Json(obj)es return new MyJsonResult { Data = obj }una molestia.


Entonces pensé, ¿por qué no simplemente secuestrar el JsonResultuso de un ActionFilter:

public class JsonNetFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is JsonResult == false)
        {
            return;
        }

        filterContext.Result = new JsonNetResult(
            (JsonResult)filterContext.Result);
    }

    private class JsonNetResult : JsonResult
    {
        public JsonNetResult(JsonResult jsonResult)
        {
            this.ContentEncoding = jsonResult.ContentEncoding;
            this.ContentType = jsonResult.ContentType;
            this.Data = jsonResult.Data;
            this.JsonRequestBehavior = jsonResult.JsonRequestBehavior;
            this.MaxJsonLength = jsonResult.MaxJsonLength;
            this.RecursionLimit = jsonResult.RecursionLimit;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            var isMethodGet = string.Equals(
                context.HttpContext.Request.HttpMethod, 
                "GET", 
                StringComparison.OrdinalIgnoreCase);

            if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet
                && isMethodGet)
            {
                throw new InvalidOperationException(
                    "GET not allowed! Change JsonRequestBehavior to AllowGet.");
            }

            var response = context.HttpContext.Response;

            response.ContentType = string.IsNullOrEmpty(this.ContentType) 
                ? "application/json" 
                : this.ContentType;

            if (this.ContentEncoding != null)
            {
                response.ContentEncoding = this.ContentEncoding;
            }

            if (this.Data != null)
            {
                response.Write(JsonConvert.SerializeObject(this.Data));
            }
        }
    }
}

Esto se puede aplicar a cualquier método que devuelva a JsonResultpara usar JSON.Net en su lugar:

[JsonNetFilter]
public ActionResult GetJson()
{
    return Json(new { hello = new Date(2015, 03, 09) }, JsonRequestBehavior.AllowGet)
}

que responderá con

{"hello":"2015-03-09T00:00:00+00:00"}

¡como se desee!


Si no le importa realizar la iscomparación en cada solicitud, puede agregar esto a su FilterConfig:

// ...
filters.Add(new JsonNetFilterAttribute());

y todo su JSON ahora se serializará con JSON.Net en lugar del archivo JavaScriptSerializer.

dav_i avatar Mar 09 '2015 09:03 dav_i