2011-11-28 13 views
16

Sto deserializzando alcune proprietà a Dictionary<string, object>.Come si modifica il tipo predefinito per la deserializzazione numerica?

Quando ho deserializzare alcuni JSON, si popola il Dictionary con Int64 oggetti piuttosto che Int32. Mi piacerebbe che fosse Int32 come predefinito ben sapendo che potrei avere i javascript Numerics che avrebbero un overflow sulla conversione. Lanciare un'eccezione in quel caso sarebbe del tutto accettabile.

Esiste un modo per raggiungerlo? Spero di avere alcuni attributi interessanti o un'interfaccia comoda che potrebbe essere implementata e aggiunta allo JsonSerializer. E temo di dover andare in profondità nelle profondità di Json.NET.

Fondamentalmente desidero avere un modo per controllare i tipi noti per gli oggetti in modo da poter ottenere Int32 's anziché Int64 e DateTimes anziché Strings.

+0

Commenti riordinata: OP è a conoscenza della possibilità POCO, ma non vuole farlo –

risposta

18

Per quanto ne so, non esiste un modo integrato per farlo.

C'era un issue su questo argomento, ma è stato chiuso. Alcuni commenti da parte dell'autore sulla questione:

Json.NET di default legge i valori interi come Int64 perché non c'è modo di sapere se il valore deve essere Int32 o Int64, Int64 ed è meno probabile che traboccare. Per una proprietà digitata, il deserializzatore sa convertire l'Int64 in Int32 ma poiché la tua proprietà non è tipizzata stai ricevendo un Int64. [...] È proprio il modo in cui Json.NET deve funzionare.

La soluzione più semplice sarebbe di coure essere quello di cambiare il tipo di Dictionary<string, int>, ma suppongo che non si sta solo leggendo numerici e quindi è bloccato con object.

Un'altra opzione sarebbe quella di utilizzare sia per Serialization Callbacks e manualmente convertire i Int64 s per Int32 o creare il proprio Contract ResolverJsonConverter e direttamente controllare il (de-) serializzazione.


Edit: ho creato un piccolo esempio per essere più precisi.

Ecco un convertitore molto di base che funziona solo con il Dizionario specifc:

public class Int32Converter : JsonConverter { 
    public override bool CanConvert(Type objectType) { 
     // may want to be less concrete here 
     return objectType == typeof(Dictionary<string, object>); 
    } 

    public override bool CanWrite { 
     // we only want to read (de-serialize) 
     get { return false; } 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { 
     // again, very concrete 
     Dictionary<string, object> result = new Dictionary<string, object>(); 
     reader.Read(); 

     while (reader.TokenType == JsonToken.PropertyName) { 
      string propertyName = reader.Value as string; 
      reader.Read(); 

      object value; 
      if (reader.TokenType == JsonToken.Integer) 
       value = Convert.ToInt32(reader.Value);  // convert to Int32 instead of Int64 
      else 
       value = serializer.Deserialize(reader);  // let the serializer handle all other cases 
      result.Add(propertyName, value); 
      reader.Read(); 
     } 

     return result; 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { 
     // since CanWrite returns false, we don't need to implement this 
     throw new NotImplementedException(); 
    } 
} 

È possibile utilizzare gli attributi per decorare i membri con il convertitore o pass it as parameter ad un (de-) metodo Serialize.Ecco un esempio in cui ho usato un attributo:

[JsonObject] 
public class MyObject { 
    [JsonConverter(typeof(Int32Converter))] 
    public Dictionary<string, object> Properties { get; set; } 
} 

Ed ecco il codice che ho usato per testare l'implementazione:

class Program { 
    static void Main(string[] args) { 
     MyObject test = new MyObject(); 
     test.Properties = new Dictionary<string, object>() { { "int", 15 }, { "string", "hi" }, { "number", 7 } }; 
     Print("Original:", test); 

     string json = JsonConvert.SerializeObject(test); 
     Console.WriteLine("JSON:\n{0}\n", json); 

     MyObject parsed = JsonConvert.DeserializeObject<MyObject>(json); 
     Print("Deserialized:", parsed); 
    } 

    private static void Print(string heading, MyObject obj) { 
     Console.WriteLine(heading); 
     foreach (var kvp in obj.Properties) 
      Console.WriteLine("{0} = {1} of {2}", kvp.Key, kvp.Value, kvp.Value.GetType().Name); 
     Console.WriteLine(); 
    } 
} 

Senza il convertitore, il risultato sarebbe:

Deserialized: 
int = 15 of Int64 
string = hi of String 
number = 7 of Int64 

e con il convertitore è:

Deserialized: 
int = 15 of Int32 
string = hi of String 
number = 7 of Int32 
+0

Che risposta assolutamente impressionante. La classe Int32Converter è esattamente ciò che stavo cercando. Non lo userò da quando ho cambiato il modo in cui faccio queste cose, ma al momento questa sarebbe stata la risposta perfetta. :) – Mithon

+0

Funziona bene quando si hanno valori interi nel dizionario. Tuttavia, se si hanno valori interi negli oggetti nel dizionario di quanto non funzioni. Qualche suggerimento per questo per favore? Funziona se il dizionario ha {{"First", 1}, {"Second", 2}} Non funziona se Dizionario ha {{"Primo", {Età: "1"}}, {"Secondo ", {Età:" 2 "}} –

+0

@HamidShahid dipende dalla modalità di de-serializzazione. Se possiedi l'oggetto nel Dizionario (quello con il campo 'Età'), puoi scrivere un convertitore per esso anziché per il dizionario. Tuttavia, se hai a che fare con vari oggetti diversi, non lo consiglierei. È possibile creare un convertitore numerico generico come [qui] (http://stackoverflow.com/questions/17745866/how-can-i-restore-the-int-deserialization-behavior-after-upgrading-json-net). Tieni presente che la mia risposta è piuttosto vecchia ormai e potrebbe esserci una soluzione integrata ormai. – enzi

1

Accetto la risposta di Enzi poiché è quello che stavo chiedendo.

Tuttavia, da allora ho cambiato la mia strategia.

In questo momento sto deserializzando su un ChangeSet<T> che invece del dizionario ha un oggetto Entity (T) fortemente digitato con le modifiche. Ha anche un List<string> con i nomi di proprietà delle proprietà che era presente nel json in arrivo. Quindi compilo quell'elenco durante la deserializzazione usando un MediaFormatter personalizzato. In questo modo ottengo un oggetto fortemente tipizzato e correggo la deserializzazione di tutte le proprietà, e so dall'elenco quali proprietà dovrei impostare sulla mia raccolta di T quando voglio eseguire la mia operazione batch.

In questo modo, in pratica, utilizzo le mie entità come DTO senza dover disporre di una miriade di DTO diversi per le diverse operazioni batch. È piuttosto elegante se lo dico anch'io. :)

1

Prova

var variable = Convert.ToInt32(object) 

Itera la Dictionary<string,object> una volta e riscrivere la sua object con questo Int32, o fare la conversione Int32 ogni volta che leggere la object.

1

Questo sta lavorando bene per me:

public class ParseNumbersAsInt32Converter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(long) || objectType == typeof(long?) || objectType == typeof(object); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     serializer.Serialize(writer, value); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.Value != null && reader.Value is long) 
     { 
      return Convert.ToInt32(reader.Value); 
     } 
     return reader.Value; 
    } 
} 
+0

Questa risposta è stata molto più semplice per entrare in contatto con l'override di JsonConvert e aggiungere ulteriori funzionalità. Grazie –

Problemi correlati