Gestion des valeurs décimales dans Newtonsoft.Json

J'ai une application MVC et je m'occupe de JSON. C'est simple. J'ai ce code simple dans mon ModelBinder:

return JsonConvert.DeserializeObject(jsonString, bindingContext.ModelType, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore, Formatting = Formatting.None, DateFormatHandling = DateFormatHandling.IsoDateFormat, FloatParseHandling = FloatParseHandling.Decimal }); 

Et cela fonctionne parfaitement.

Eh bien, sorte de.

Disons que j'ai cette classe:

 public class MyClass { public decimal MyProp { get; set; } } 

Si j'essaie de désérialiser ce json:

 "{\"MyProp\": 9888.77}" 

Bien sûr, cela fonctionne, puisque 9888.77 est une valeur flottante Javascript. Je pense.

Mais j'ai une contribution masquée pour l'argent dans ma page qui fait que le JSON ressemble à ça (désolé pour mon anglais):

 "{\"MyProp\": \"9.888,77\" }" 

AAAND, il échoue. Il dit qu'il ne Could not convert string to decimal .

D'accord, c'est juste. Ce n'est pas un flot JS, mais Convert.ToDecimal("9.888,77") fonctionne comme je le souhaite.

J'ai lu quelques tutoriels sur Internet à propos des désérialisateurs personnalisés, mais il me permet de définir un désérialisateur personnalisé pour chaque classe que j'ai dans ma application.

Ce que je veux, c'est redéfinir la façon dont JSON.Net convertit une chaîne en une propriété décimale, dans toute classe que je souhaiterais jamais déseréraliser. Je souhaite injecter la fonction Convert.ToDecimal dans le processus de conversion des décimales lorsque le convertisseur actuel ne fonctionne pas.

Y a-t-il une façon de le faire?

Je pensais qu'il y avait un moyen de le faire, alors j'ai changé mon code un peu.

 JsonSerializer serializer = new JsonSerializer { NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore, Formatting = Formatting.None, DateFormatHandling = DateFormatHandling.IsoDateFormat, FloatParseHandling = FloatParseHandling.Decimal, }; return serializer.Deserialize(new DecimalReader(jsonStr), bindingContext.ModelType); 

Et a créé cette classe:

 public class DecimalReader : JsonTextReader { public DecimalReader(string s) : base(new StringReader(s)) { } public override decimal? ReadAsDecimal() { try { return base.ReadAsDecimal(); } catch (Exception) { if (this.TokenType == JsonToken.String) { decimal value = 0; bool convertible = Decimal.TryParse(this.Value.ToString(), out value); if (convertible) { return new Nullable<decimal>(value); } else { throw; } } else { throw; } } } } 

Mais c'est très moche: il exécute ce que je veux seulement quand il se bloque et dépend de base.ReadAsDecimal() . Il ne pouvait pas être plus laid.

Et ne fonctionne pas : Error converting value "1.231,23" to type 'System.Nullable1[System.Decimal]'. Path 'MyProp', line X, position Y. Error converting value "1.231,23" to type 'System.Nullable1[System.Decimal]'. Path 'MyProp', line X, position Y.

La valeur elle-même est en cours de conversion, mais peut-être pour une raison quelconque, elle essaie toujours de mettre la chaîne "1.231,23" en une décimale.

Alors, y a-t-il un moyen de le faire correctement?

Vous pouvez gérer les deux formats (la représentation du numéro JSON et le format de la chaîne masquée) à l'aide d'une classe JsonConverter personnalisée comme celle-ci.

 class DecimalConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(decimal) || objectType == typeof(decimal?)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JToken token = JToken.Load(reader); if (token.Type == JTokenType.Float || token.Type == JTokenType.Integer) { return token.ToObject<decimal>(); } if (token.Type == JTokenType.String) { // customize this to suit your needs return Decimal.Parse(token.ToString(), System.Globalization.CultureInfo.GetCultureInfo("es-ES")); } if (token.Type == JTokenType.Null && objectType == typeof(decimal?)) { return null; } throw new JsonSerializationException("Unexpected token type: " + token.Type.ToString()); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } 

Pour connecter ceci dans votre classeur, il suffit d'ajouter une instance du convertisseur à la liste des Converters dans l'objet JsonSerializerSettings :

 JsonSerializerSettings settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore, Formatting = Formatting.None, DateFormatHandling = DateFormatHandling.IsoDateFormat, Converters = new List<JsonConverter> { new DecimalConverter() } }; 

Merci beaucoup! Je cherchais une solution pour que les décimales soient toujours sérialisés de manière similaire et cette publication m'a envoyé dans la bonne direction. C'est mon code:

  internal class DecimalConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(decimal) || objectType == typeof(decimal?)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { Decimal? d = default(Decimal?); if (value != null) { d = value as Decimal?; if (d.HasValue) // If value was a decimal?, then this is possible { d = new Decimal?(new Decimal(Decimal.ToDouble(d.Value))); // The ToDouble-conversion removes all unnessecary precision } } JToken.FromObject(d).WriteTo(writer); } }