web-dev-qa-db-fra.com

Méthode appropriée pour convertir la date JSON en .NET DateTime pendant la désérialisation

J'ai une fonction javascript qui appelle un contrôleur MVC avec des données JSON:

var specsAsJson = JSON.stringify(specs);
$.post('/Home/Save', { jsonData: specsAsJson });

Côté serveur, dans le contrôleur, je n'arrive pas à surmonter cette erreur:

/ Date (1347992529530)/n'est pas une valeur valide pour DateTime.

Cette exception se produit lorsque j'appelle Deserialize () (troisième ligne de la méthode ci-dessous):

    public ActionResult Save(string jsonData)
    {
        var serializer = new JavaScriptSerializer();
        serializer.RegisterConverters(new[] { new TimeSpanJsonConverter() });
        var specs = serializer.Deserialize<List<EquipmentSpecWithParameterlessConstructor>>(jsonData);

        return View("Index", _allTrackerJobs);
    }

J'ai fait quelques recherches sur Google, et le code ci-dessus est ma dernière tentative pour faire ce travail (en utilisant le TimeSpanJsonConverter de ici ). D'autres approches montrent l'envoi uniquement d'une date au serveur, mais j'ai une liste d'objets qui ont des dates comme propriétés.

Existe-t-il une approche élégante et généralement acceptée pour résoudre ce problème, ou avons-nous encore besoin d'une sorte de laideur? Quelle est la bonne façon de résoudre ce problème?

=================== Fin de la question d'origine =================== =


Édition - RÉSOLU en sérialisant à l'aide de JsonConvert

Voir ma réponse ci-dessous (pas la solution de contournement merdique de cette question).


Modifier - Contournement de merde

J'ai créé un DTO avec exactement les mêmes champs que l'objet domaine, sauf que j'ai créé les chaînes de champs de date afin qu'elles se désérialisent. Maintenant que je peux le désérialiser, je vais travailler pour obtenir les dates dans un format valide afin que je puisse créer des objets de domaine à partir de mes DTO.

public class EquipmentSpecDto
{
    public string StartTime { get; set; }
    public string EndTime { get; set; }
    // more properties here
}

Et j'ai simplement utilisé le DTO pour la désérialisation:

var specs = serializer.Deserialize<List<EquipmentSpecDto>>(jsonData);

Édition 2 - Conversion de dates JavaScript en .NET

Pour être complet, et dans l'espoir de faire gagner une heure à quelqu'un d'autre, voici comment j'ai pu convertir les dates javascript:

    foreach (EquipmentSpecDto specDto in specDtos)
    {
        // JavaScript uses the unix Epoch of 1/1/1970. Note, it's important to call ToLocalTime()
        // after doing the time conversion, otherwise we'd have to deal with daylight savings hooey.
        DateTime unixEpoch       = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        Double startMilliseconds = Convert.ToDouble(specDto.StartTime.Substring(6, 13));
        Double endMilliseconds   = Convert.ToDouble(specDto.EndTime.Substring(6, 13));
        DateTime startTime       = unixEpoch.AddMilliseconds(startMilliseconds).ToLocalTime();
        DateTime endTime         = unixEpoch.AddMilliseconds(endMilliseconds).ToLocalTime();
        EquipmentSpec spec       = new EquipmentSpec(startTime, endTime, specDto.Equipment);

        specs.Add(spec);
    }
15
Bob Horn

J'ai trouvé une réponse simple. Dans mon javascript, je sérialisais les données à l'aide de JavaScriptSerializer. Après beaucoup de recherches sur Google, j'ai trouvé ceci article qui montre comment sérialiser en utilisant JsonConvert qui provoque l'utilisation d'un DateTime plus convivial .NET.

Ancien:

var specs = @Html.Raw(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(ViewBag.JobSpecEquipment))

Les dates ressemblent à ceci: Date(1348017917565)

Nouveau:

var specs = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(ViewBag.JobSpecEquipment));

Les dates ressemblent à ceci: 2012-09-18T21:27:31.1285861-04:00

Donc, le problème était vraiment comment je sérialisais en premier lieu. Une fois que j'ai utilisé JsonConvert, la désérialisation à l'arrière a simplement fonctionné.

13
Bob Horn

J'ai trouvé ce morceau de code sur Internet. Cela a fonctionné comme un charme pour moi ...

function customJSONstringify(obj) {
    return JSON.stringify(obj).replace(/\/Date/g, "\\\/Date").replace(/\)\//g, "\)\\\/")
}
6

Une chose qui surprend assez souvent lors de la conversion entre des dates Javascript et diverses langues côté serveur est que, bien que les deux parties puissent comprendre une valeur d'horodatage de style Unix, JS utilise un horodatage de précision en microsecondes, tandis que dans la plupart des autres langues, la valeur par défaut la précision d'horodatage est à la seconde près.

En d'autres termes, 1347993132851 en Javascript doit être divisé par 1000 afin d'être reconnu comme horodatage Unix dans d'autres langues.

Sinon, si votre plate-forme peut accepter des chaînes de date formatées, utilisez l'objet Javascript Date() pour convertir une valeur d'horodatage en une date formatée à envoyer au serveur. Ou encore mieux, utilisez une bibliothèque d'assistance telle que Date.js ou Moment.js .

3
Spudley

J'ai pris la réponse @Bob Horn mais cela ne fonctionnait pas pour moi. Mon service REST utilise des dates Javascritpt. J'ai adapté la réponse référencée à une méthode d'extension.


using System;

namespace Mediatel.Framework
{
    public static class JsonDate
    {
        public static DateTime ConvertToDateTime(this string jsonDate)
        {
            // JavaScript uses the unix Epoch of 1/1/1970. Note, it's important to call ToLocalTime()
            // after doing the time conversion, otherwise we'd have to deal with daylight savings hooey.
            DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            Double milliseconds = Convert.ToDouble(jsonDate);
            DateTime dateTime = unixEpoch.AddMilliseconds(milliseconds).ToLocalTime();

            return dateTime;
        }
    }
}
2
Tiago Freitas Leal

Après avoir reçu l'erreur

l'utilisation de ce remplacement a fonctionné pour moi.

var data = ko.toJSON({ objext: obj}); $.ajax({ url: "/API/API.asmx/SaveObject", type: "POST", dataType: "json", contentType: "application/json; char-utf8;", data: data.replace(/\/Date/g, "\\\/Date").replace(/\)\//g, "\)\\\/"), success: function (r) {}, error: function (e) {}, complete: function () {} }); .

1
BunkerBilly

JavaScript (enfin, EcmaScript) définit son format d'échange de chaînes DateTime basé sur une simplification de la norme ISO-8601.

Le schéma XML définit également son format d'échange de chaînes DateTime basé sur ISO-8601.

Je l'ai trouvé pratique pour utiliser la classe .NET System.Runtime.Remoting.Metadata.W3cXsd2001.SoapDateTime pour gérer la conversion des valeurs .NET DateTime aux formats XML et inversement.

Étant donné que JavaScript est basé sur la même norme ISO-8601, cela fonctionnera peut-être également pour votre boîtier JSON.

1
dthorpe