2014-06-05 9 views
5

Ich habe eine MVC-Anwendung und ich handle etwas JSON darin. Das ist einfach. Ich habe dieses einfache Stück Code in meinem ModelBinder:Umgang mit Dezimalwerten in Newtonsoft.Json

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

Und es funktioniert einwandfrei.

Nun, irgendwie.

Lassen Sie uns sagen, ich habe diese Klasse:

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

Wenn ich versuche, dieses json zu deserialisieren:

"{\"MyProp\": 9888.77}" 

Natürlich funktioniert es, da 9888.77 ein Gleitkommawert Javascript ist. Ich denke.

Aber ich einen maskierten Eingang für Geld in meine Seite, die die JSON sieht wie folgt aus (sorry über mein Englisch) macht:

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

aaand, versagt es. Es sagt, dass es Could not convert string to decimal ist.

Ok, das ist fair. Es ist kein JS-Float, aber Convert.ToDecimal("9.888,77") funktioniert so, wie ich es möchte.

Ich habe einige Tutorials im Internet über benutzerdefinierte Deserializer gelesen, aber es ist für mich unantastbar, einen benutzerdefinierten Deserializer für jede einzelne Klasse zu definieren, die ich in meiner Anwendung habe.

Was ich will, ist einfach zu definieren, wie JSON.Net eine Zeichenfolge in eine Dezimal-Eigenschaft in jeder Klasse konvertiert, die ich jemals deserialize werden möchten. Ich möchte die Convert.ToDecimal Funktion in den Prozess der Konvertierung von Dezimalzahlen injizieren, wenn der aktuelle Konverter nicht funktioniert.

Gibt es einen Weg, ich könnte es tun?

Ich dachte, es gibt einen Weg, es zu tun, also habe ich meinen Code ein wenig geändert.

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); 

Und schuf diese Klasse:

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; 
      } 
     } 
    } 
} 

Aber es ist sehr hässlich: es führt, was ich will nur, wenn er abstürzt, und hängt von base.ReadAsDecimal() abstürzt. Es könnte nicht hässlicher sein.

Und funktioniert nicht: Error converting value "1.231,23" to type 'System.Nullable1[System.Decimal]'. Path 'MyProp', line X, position Y.

Der Wert selbst umgewandelt wird, aber vielleicht aus irgendeinem Grunde versucht es noch die Zeichenfolge „1.231,23“ in eine Dezimalzahl zu setzen.

Also, gibt es eine Möglichkeit, es richtig zu machen?

Antwort

17

Sie können beide Formate (die JSON-Zahlendarstellung und das maskierte Zeichenfolgenformat) mit einer benutzerdefinierten JsonConverter-Klasse wie dieser behandeln.

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(); 
    } 
} 

Um stecken diese in die Bindemittel, fügen Sie einfach eine Instanz des Wandlers auf die Converters Liste im JsonSerializerSettings Objekt:

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

Funktioniert wie ein Charme. Um eine sichere Rückkehr von Nullable-Dezimalzahlen zu gewährleisten, habe ich die folgenden Änderungen an einer der conditionals if (token.Type == JTokenType.String) { Dezimal d; if (Decimal.TryParse (token.ToString(), System.Globalization.NumberStyles.AllowParentheses, System.Globalization.CultureInfo.CurrentCulture, aus d)) return d; sonst Rückgabewert null; } – Arjmand

1

Thanx a lot! Ich war auf der Suche nach einer Lösung, um Dezimalzahlen immer auf ähnliche Weise zu serialisieren, und dieser Beitrag hat mich in die richtige Richtung geschickt. Das ist mein 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); 
     } 
    }