2016-11-04 79 views
2

我有以下類:JsonSerializer.CreateDefault()填充(..)重置我的價值觀

public class MainClass 
{ 
    public static MainClass[] array = new MainClass[1] 
    { 
     new MainClass 
     { 
      subClass = new SubClass[2] 
      { 
       new SubClass 
       { 
        variable1 = "my value" 
       }, 
       new SubClass 
       { 
        variable1 = "my value" 
       } 
      } 
     } 
    }; 

    public SubClass[] subClass; 
    [DataContract] 
    public class SubClass 
    { 
     public string variable1 = "default value"; 
     [DataMember] // because only variable2 should be saved in json 
     public string variable2 = "default value"; 
    } 
} 

我保存如下:

File.WriteAllText("data.txt", JsonConvert.SerializeObject(new 
{ 
    MainClass.array 
}, new JsonSerializerSettings { Formatting = Formatting.Indented })); 

的data.txt:

{ 
    "array": [ 
    { 
     "subClass": [ 
     { 
      "variable2": "value from json" 
     }, 
     { 
      "variable2": "value from json" 
     } 
     ] 
    } 
    ] 
} 

然後我反序列化並填充我的對象,如下所示:

JObject json = JObject.Parse(File.ReadAllText("data.txt")); 
if (json["array"] != null) 
{ 
    for (int i = 0, len = json["array"].Count(); i < len; i++) 
    { 
     using (var sr = json["array"][i].CreateReader()) 
     { 
      JsonSerializer.CreateDefault().Populate(sr, MainClass.array[i]); 
     } 
    } 
} 

然而,當我打印以下變量:

Console.WriteLine(MainClass.array[0].subClass[0].variable1); 
Console.WriteLine(MainClass.array[0].subClass[0].variable2); 
Console.WriteLine(MainClass.array[0].subClass[1].variable1); 
Console.WriteLine(MainClass.array[0].subClass[1].variable2); 

那麼它的輸出是:

default value 
value from json 
default value 
value from json 

而不是「默認值」應該有「我的價值」,因爲那是什麼我在創建類的實例時使用了JsonSerializer,應該只使用來自json的值填充對象。

如何正確填充整個對象,而不重置其未包含在json中的屬性?

回答

0

看起來好像JsonSerializer.Populate()缺少可用於JObject.Merge()MergeArrayHandling設置。通過測試我發現:

  • 填充成員是數組或一些其他類型的只讀集合好像MergeArrayHandling.Replace工作。

    這是您正在經歷的行爲 - 現有數組及其中的所有項目將被丟棄,並替換爲包含具有默認值的新構建項目的新數組。相反,您需要MergeArrayHandling.Merge將數組項合併在一起,並按索引進行匹配。

  • 填充讀/寫集合的成員,如List<T>似乎像MergeArrayHandling.Concat一樣工作。

這似乎是合理的request an enhancementPopulate()支持此設置 - 儘管我不知道這將是多麼容易實現。至少Populate()的文檔應該解釋這種行爲。

在此期間,這裏有一個自定義的JsonConverter必要的邏輯效仿MergeArrayHandling.Merge行爲:

public class ArrayMergeConverter<T> : ArrayMergeConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType.IsArray && objectType.GetArrayRank() == 1 && objectType.GetElementType() == typeof(T); 
    } 
} 

public class ArrayMergeConverter : JsonConverter 
{ 
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (!objectType.IsArray) 
      throw new JsonSerializationException(string.Format("Non-array type {0} not supported.", objectType)); 
     var contract = (JsonArrayContract)serializer.ContractResolver.ResolveContract(objectType); 
     if (contract.IsMultidimensionalArray) 
      throw new JsonSerializationException("Multidimensional arrays not supported."); 
     if (reader.TokenType == JsonToken.Null) 
      return null; 
     if (reader.TokenType != JsonToken.StartArray) 
      throw new JsonSerializationException(string.Format("Invalid start token: {0}", reader.TokenType)); 
     var itemType = contract.CollectionItemType; 
     var existingList = existingValue as IList; 
     IList list = new List<object>(); 
     while (reader.Read()) 
     { 
      switch (reader.TokenType) 
      { 
       case JsonToken.Comment: 
        break; 
       case JsonToken.Null: 
        list.Add(null); 
        break; 
       case JsonToken.EndArray: 
        var array = Array.CreateInstance(itemType, list.Count); 
        list.CopyTo(array, 0); 
        return array; 
       default: 
        // Add item to list 
        var existingItem = existingList != null && list.Count < existingList.Count ? existingList[list.Count] : null; 
        if (existingItem == null) 
        { 
         existingItem = serializer.Deserialize(reader, itemType); 
        } 
        else 
        { 
         serializer.Populate(reader, existingItem); 
        } 
        list.Add(existingItem); 
        break; 
      } 
     } 
     // Should not come here. 
     throw new JsonSerializationException("Unclosed array at path: " + reader.Path); 
    } 

    public override bool CanWrite { get { return false; } } 

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

    public override bool CanConvert(Type objectType) 
    { 
     throw new NotImplementedException(); 
    } 
} 

然後轉換器添加到您的subClass成員如下:

[JsonConverter(typeof(ArrayMergeConverter))] 
    public SubClass[] subClass; 

或者,如果你不想添加Json。NET屬性數據模型,您可以在串行設置添加:

var settings = new JsonSerializerSettings 
    { 
     Converters = new[] { new ArrayMergeConverter<MainClass.SubClass>() }, 
    }; 
    JsonSerializer.CreateDefault(settings).Populate(sr, MainClass.array[i]); 

轉換器是專門爲陣列設計的,但類似的轉換器可以很容易地進行讀/寫的集合,如List<T>創建。

+0

感謝您的解釋。我擔心Populate()將無法合併當前和現有的數組,但其名稱和文檔混淆了我。最後,我通過添加或分配每個數組成員的屬性與來自json的值來做到這一點。你的函數可能不太容易在我的真實代碼中工作,因爲我有一個具有各種屬性和其他數組的類的數組。 – LukAss741