2013-07-19 11 views
6

Mam kod .NET, który deserializuje obiekty JSON utworzone przez webservice z dynamicznym językiem. Ponieważ źródło jest dynamiczne, czasami szereguje wartości całkowe w formacie zmiennoprzecinkowym (na przykład 2 zserializowano do "2.0").Jak mogę przywrócić zachowanie deserializacji int po aktualizacji Json.NET?

Z Json.NET 4.0.4, działało to płynnie (wydaje się, że zaokrąglanie było stosowane podczas deserializacji). Jednak po aktualizacji do Json.NET 4.5 przekształcenie wersji 2.0 powoduje obecnie wyświetlenie FormatException. Oto kod:

// works as expected in both versions 
var s = "2"; 
Console.WriteLine(JsonConvert.DeserializeObject<int>(s)); 

// throws FormatException in 4.5 only 
var s = "2.0"; 
Console.WriteLine(JsonConvert.DeserializeObject<int>(s)); 

// throws FormatException in 4.5, rounds to 3 in 4.0.4 
var s = "2.6"; 
Console.WriteLine(JsonConvert.DeserializeObject<int>(s)); 

Czy istnieje prosty sposób przywrócenia oryginalnego zachowania? Idealnym zachowaniem byłoby deserializowanie tylko liczb o wartościach integralnych, ale w dowolnym formacie (np. 2.0, 1e10, ale nie 2.5), ale zadowalałoby mnie zachowanie w wersji 4.0.4.

Odpowiedz

6

Możesz to zrobić, tworząc niestandardową JsonConverter, która zajmie się zaokrąglaniem (lub odrzucaniem) wartości dziesiętnych. Może to wyglądać mniej więcej tak:

class CustomIntConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return (objectType == typeof(int)); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JValue jsonValue = serializer.Deserialize<JValue>(reader); 

     if (jsonValue.Type == JTokenType.Float) 
     { 
      return (int)Math.Round(jsonValue.Value<double>()); 
     } 
     else if (jsonValue.Type == JTokenType.Integer) 
     { 
      return jsonValue.Value<int>(); 
     } 

     throw new FormatException(); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Następnie można użyć niestandardowego konwertera takiego:

JsonSerializerSettings settings = new JsonSerializerSettings 
{ 
    Converters = new List<JsonConverter> { new CustomIntConverter() } 
}; 

string json = @"[2.6, 0, 4.1, 5, -3, -2.2]"; 

List<int> list = JsonConvert.DeserializeObject<List<int>>(json, settings); 

foreach (int val in list) 
{ 
    Console.WriteLine(val); 
} 

Wyjście powyżej byłoby to:

3 
0 
4 
5 
-3 
-2 

Jeśli byś raczej konwerter ignoruje wartości dziesiętne zamiast je zaokrąglać, zastępuje następujący wiersz kodu:

 return (int)Math.Round(jsonValue.Value<double>()); 

z tym:

 return (existingValue ?? default(int)); 

Po dokonaniu tej zmiany, wyjście kodu testowego powyżej będzie wtedy wyglądać tak:

0 
0 
0 
5 
-3 
0 
+0

może trzeba rozważyć dodanie 'objectType == typeof (object) 'w' CanConvert', jeśli klasa docelowa "nie określa" rzeczywistego typu np 'class MyObject {identyfikator obiektu publicznego; } ' – drzaus

+0

@drzaus Może, ale bądź ostrożny. Jeśli to zrobisz i będziesz miał inne właściwości typu 'object', które są * nie * liczbami, ten konwerter spróbuje je obsłużyć, co może nie być tym, czego się spodziewasz. Będziesz musiał dodać kod do obsługi tego przypadku. –

+0

Ach tak, myślałem o niektórych [innych odpowiedziach] (http://stackoverflow.com/a/28748973/1037948), które obsługują to, sprawdzając 'JTokenType' i jeśli nie jest to typ oczekiwany, zamiast rzucać' FormatException' po prostu zwraca 'serializer.Deserialize (reader)', który wydaje się delegować go do tego, co jeszcze powinno poprawnie obsługiwać. Do tej pory działało to w moich testach z zagnieżdżonymi, złożonymi typami. – drzaus

Powiązane problemy