2014-06-05 18 views
5

Tôi có một ứng dụng MVC và tôi xử lý một số JSON trong đó. Thật đơn giản. Tôi có đoạn mã đơn giản này trong ModelBinder của tôi:Xử lý các giá trị thập phân trong 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 
}); 

Và nó hoạt động hoàn hảo.

Vâng, sắp xếp.

Hãy nói rằng tôi có lớp học này:

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

Nếu tôi cố gắng deserialize json này:

"{\"MyProp\": 9888.77}" 

Tất nhiên nó hoạt động, kể từ 9888.77 là một giá trị float Javascript. Tôi nghĩ.

Nhưng tôi có một đầu vào đeo mặt nạ với tiền bỏ ra trong trang của tôi mà làm cho giao diện JSON như thế này (xin lỗi về tiếng anh của tôi):

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

AAAND, nó không thành công. Nó nói rằng nó Could not convert string to decimal.

Ok, công bằng. Nó không phải là một phao JS, nhưng Convert.ToDecimal("9.888,77") hoạt động theo cách tôi muốn.

Tôi đã đọc một số hướng dẫn trên internet về tùy chỉnh deserializers, nhưng nó inviable cho tôi để xác định một deserializer tùy chỉnh cho mỗi lớp duy nhất tôi có trong ứng dụng của tôi.

Điều tôi muốn đơn giản là xác định lại cách mà JSON.Net chuyển đổi một chuỗi thành một thuộc tính thập phân, trong bất kỳ lớp nào tôi sẽ muốn deserialize. Tôi muốn tiêm hàm Convert.ToDecimal trong quá trình chuyển đổi số thập phân, khi bộ chuyển đổi hiện tại không hoạt động.

Có cách nào tôi có thể làm điều đó không?

Tôi nghĩ rằng có một cách để làm điều đó, vì vậy tôi đã thay đổi mã của mình một chút.

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

Và tạo ra lớp này:

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

Nhưng nó rất xấu xí: nó thực hiện những gì tôi muốn chỉ khi nó bị treo, và phụ thuộc vào base.ReadAsDecimal() đâm. Nó không thể xấu hơn được nữa.

Và không hoạt động: Error converting value "1.231,23" to type 'System.Nullable1[System.Decimal]'. Path 'MyProp', line X, position Y.

Giá trị bản thân đang được chuyển đổi, nhưng có lẽ vì một lý do nó vẫn cố gắng để đưa chuỗi "1.231,23" vào một số thập phân.

Vì vậy, có cách nào để thực hiện đúng không?

Trả lời

17

Bạn có thể xử lý cả hai định dạng (biểu diễn số JSON và định dạng chuỗi đeo mặt nạ) bằng cách sử dụng lớp tùy chỉnh JsonConverter như thế này.

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

Để cắm này vào chất kết dính, chỉ cần thêm một thể hiện của bộ chuyển đổi vào danh sách Converters trong đối tượng JsonSerializerSettings:

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

Hoạt động như một sự quyến rũ. Để đảm bảo trả lại số thập phân có thể vô hiệu hóa an toàn, tôi đã thực hiện các thay đổi sau đối với một trong các điều kiện nếu (token.Type == JTokenType.String) { Decimal d; if (Decimal.TryParse (token.ToString(), System.Globalization.NumberStyles.AllowParentheses, System.Globalization.CultureInfo.CurrentCulture, out d)) return d; else trả về null; } – Arjmand

1

Thanx rất nhiều! Tôi đã tìm kiếm một giải pháp để làm cho thập phân luôn luôn serialize một cách tương tự và bài đăng này đã gửi cho tôi đi đúng hướng. Đây là mã của tôi:

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); 
     } 
    } 
Các vấn đề liên quan