2011-11-28 58 views
16

我將一些屬性反序列化爲Dictionary<string, object>如何更改數字反序列化的默認類型?

當我反序列化一些json時,它用Int64對象填充Dictionary而不是Int32。我希望它選擇Int32作爲默認知道我可以有JavaScript數字會轉換溢出。在這種情況下拋出異常是完全可以接受的。

有什麼辦法可以達到這個目的嗎?我希望能夠實現一些好的屬性或方便的接口,並將其添加到JsonSerializer。我擔心我必須深入到Json.NET的深處。

基本上我想有一些方法來控制已知類型的對象,使我能得到Int32的,而不是Int64DateTimes代替Strings

+0

評論整理:OP是知道POCO選項,但並不想這樣做 –

回答

18

據我所知,沒有內置的方法來做到這一點。

在這個問題上有一個issue,但它已經關閉。 從筆者對這個問題的一些意見:

Json.NET默認讀取整數值作爲Int64的,因爲沒有辦法知道的值是否應的Int32或Int64類型,並Int64的是不太可能溢出。對於一個類型化的屬性,反序列化器知道將Int64轉換爲Int32,但是因爲你的屬性是無類型的,所以你得到了一個Int64。 [...]這只是Json.NET的工作方式。課的的

最簡單的辦法是將類型更改爲Dictionary<string, int>,但我想你不僅是閱讀數字和因此被套牢object

另一種選擇是要麼使用Serialization Callbacks和手動那些Int64秒值進行轉換,以Int32或創建自己的Contract ResolverJsonConverter和直接控制(去)序列化。


編輯:我創建了一個小例子更加具體。

這是一個非常基本的轉換器,只用你的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(); 
    } 
} 

您可以使用屬性來裝點你的轉換器或pass it as parameter到(去)序列化方法的成員。這裏就是我用了一個屬性的例子:

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

,這裏是我用來測試的執行代碼:

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

沒有轉換器,其結果必然是:

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

並與轉換器它是:

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

什麼完全真棒答案。 Int32Converter類正是我正在尋找的。我不會去使用它,因爲我已經改變了我如何做這些事情,但是在那個時候這將是最完美的答案。 :) – Mithon

+0

這是工作正常,當你有字典中的整數值。但是,如果在字典中的對象中具有整數值而不起作用。對此有任何建議嗎? Works如果字典有{{「First」,1},{「Second」,2}} 如果Dictionary有{{「First」,{Age:「1」}},{「Second 「,{Age:」2「}} –

+0

@HamidShahid取決於你如何反序列化。如果您擁有Dictionary中的對象(帶有「Age」字段的對象),則可以爲它編寫一個轉換器,而不是字典。但是,如果你正在處理各種不同的對象,我不會推薦它。你可以創建一個通用的數字轉換器,像[這裏](http://stackoverflow.com/questions/17745866/how-can-i-restore-the-int-deserialization-behavior-after-upgrading-json-net)。請記住,我的答案現在已經很老了,現在可能有一個內置解決方案。 – enzi

1

我接受Enzi的回答,因爲這是我所要求的。

但是,從那以後我改變了我的策略。

現在我正在反序列化爲ChangeSet<T>,而不是字典中包含強類型Entity(T)對象的更改。它還有一個List<string>,其中包含傳入json中存在的屬性的屬性名稱。然後,我使用自定義MediaFormatter在反序列化過程中填充該列表。這樣我得到一個強類型對象和正確的所有屬性的反序列化,並且我從列表中知道當我想要執行批處理操作時,我應該在我的T集合上設置哪些屬性。

這樣,我基本上使用我的實體作爲DTO的,而不必爲不同的批處理操作提供大量不同的DTO。如果我自己這樣說,那就很漂亮。 :)

1

嘗試

var variable = Convert.ToInt32(object) 

反覆調整Dictionary<string,object>一次改寫了object這個Int32,或每次讀取object時間做Int32轉換。

1

這是爲我工作得好:

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

這個答案更容易與重寫的JsonConvert取得聯繫並添加更多的功能。謝謝 –

相關問題