2015-07-27 4 views
10

Sto usando Json.NET per serializzare/deserializzare alcune API JSON.Deserializzazione C# C con Json.Net: Errore nella conversione del valore nel tipo

La risposta API ha alcuni valori interi che si associano a un Enum definito nell'applicazione.

L'enum è come questo:

public enum MyEnum 
    { 
     Type1, 
     Type2, 
     Type3 
} 

e la risposta JSON API ha la seguente:

{ 
     "Name": "abc", 
     "MyEnumValue":"Type1" 
} 

volte l'API restituisce un valore per il campo MyEnumValue che non è definito nel mio enum, in questo modo:

{ 
      "Name": "abc", 
      "MyEnumValue":"Type4" 
    } 

questo genera un'eccezione:

Errore che converte il valore "Type4" per digitare 'MyEnum'

C'è un modo per gestire questo errore assegnando un valore predefinito o qualcosa per evitare il crash dell'applicazione?

+0

hai provato specificando il 'DefaultValue' per la proprietà http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_DefaultValueHandling.htm – 3dd

+0

Grazie, ho provato ma ancora viene generata l'eccezione –

risposta

6

Diciamo che abbiamo la seguente stringa JSON:

[ 
    { 
     "Name": "abc", 
     "MyEnumValue": "Type1" 
    }, 
    { 
     "Name": "abcd", 
     "MyEnumValue": "Type2" 
    }, 
    { 
     "Name": "abcde", 
     "MyEnumValue": "Type3" 
    } , 
    { 
     "Name": "abcdef", 
     "MyEnumValue": "Type4" 
    } 
] 

e la seguente classe e enum:

public class MyClass 
{ 
    public string Name { get; set; } 

    public MyEnum MyEnumValue { get; set; } 
} 

public enum MyEnum 
{ 
    Type1, 
    Type2, 
    Type3 
} 

Come si può notare, la matrice di stringa JSON contiene elemento (l'ultimo), che non può essere mappato correttamente su MyEnum. Per evitare errori di deserializzazione è possibile utilizzare il seguente frammento di codice:

static void Main(string[] args) 
{   
    var serializationSettings = new JsonSerializerSettings 
    { 
     Error = HandleDeserializationError 
    }; 

    var lst = JsonConvert.DeserializeObject<List<MyClass>>(jsonStr, serializationSettings); 
} 

public static void HandleDeserializationError(object sender, ErrorEventArgs errorArgs) 
{ 
    errorArgs.ErrorContext.Handled = true; 
    var currentObj = errorArgs.CurrentObject as MyClass; 

    if (currentObj == null) return; 
    currentObj.MyEnumValue = MyEnum.Type2;    
} 

dove la variabile jsonStr è la stringa JSON postato sopra. Nell'esempio di codice precedente, se MyEnumValue non può essere interpretato correttamente, viene impostato su un valore predefinito di Type2.

Esempio: https://dotnetfiddle.net/WKd2Lt

+0

Grazie, questa è una bella soluzione –

+0

Yikes, come si assegna un valore predefinito a una soluzione? Forse Type5 o Type6 o TypeX non possono essere equiparati a Type2? Il punto è che il cliente non può anticipare cosa il server potrebbe restituire in futuro, quindi supponendo che è possibile assegnare un valore predefinito è pericoloso. –

8

L'unico mio modo di vedere, si dovrebbe scrivere il proprio convertitore. Ma metà del lavoro è già stato fatto nella classe StringEnumConverter. Siamo in grado di eseguire l'override solo ReadJson metodo

class Program 
{ 
    static void Main(string[] args) 
    { 
     const string json = @"{ 
       'Name': 'abc', 
       'Type':'Type4' 
      }"; 

     // uncomment this if you want to use default value other then default enum first value 
     //var settings = new JsonSerializerSettings(); 
     //settings.Converters.Add(new FooTypeEnumConverter { DefaultValue = FooType.Type3 }); 

     //var x = JsonConvert.DeserializeObject<Foo>(json, settings); 

     var x = JsonConvert.DeserializeObject<Foo>(json); 
    } 
} 

public class Foo 
{ 
    public string Name { get; set; } 

    public FooType Type { get; set; } 
} 

public enum FooType 
{ 
    Type1, 
    Type2, 
    Type3 
} 

public class FooTypeEnumConverter : StringEnumConverter 
{ 
    public FooType DefaultValue { get; set; } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     try 
     { 
      return base.ReadJson(reader, objectType, existingValue, serializer); 
     } 
     catch (JsonSerializationException) 
     { 
      return DefaultValue; 
     } 
    } 
} 
+0

Grazie, questa può essere un'altra soluzione alternativa –

0

Un'alternativa, se non si desidera creare un convertitore personalizzato, è quello di mappare ad un campo di stringa privata in DTO e quindi utilizzare Enum.TryParse in proprietà che di campo getter:

public class MyClass 
{ 
    [JsonProperty("MyEnumValue")] 
    private string myEnumValue; 

    public string Name { get; set; } 

    [JsonIgnore] 
    public MyEnum MyEnumValue 
    { 
     get 
     { 
      MyEnum outputValue = MyEnum.Default; 
      Enum.TryParse(myEnumValue, out outputValue); 
      return outputValue; 
     } 
    } 
}