JsonSerializer.CreateDefault().Populate(..)重置我的值 [英] JsonSerializer.CreateDefault().Populate(..) resets my values
问题描述
我有以下课程:
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"
}
]
}
]
}
然后我反序列化并填充对象,如下所示:
then I deserialize and populate my object like this:
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]);
}
}
}
但是,当我打印以下变量时:
however, when I print following variables:
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中的值填充对象.
but instead of "default value" there should be "my value" because that is what I used while creating an instance of class and JsonSerializer should only populate the object with values from json.
如何正确填充整个对象,而不重置其不包含在json中的属性?
How do I properly populate the whole object without resetting its properties which are not included in json?
推荐答案
它似乎 MergeArrayHandling
可用于 JObject.Merge()
的设置.通过测试,我发现:
It looks as though JsonSerializer.Populate()
lacks the MergeArrayHandling
setting that is available for JObject.Merge()
. Through testing I have found that:
-
填充数组或其他类型的只读集合成员似乎像
MergeArrayHandling.Replace
一样工作.
这是您遇到的行为-现有数组及其中的所有项目都将被丢弃,并替换为包含新构造的具有默认值的项目的新数组.相反,您需要 MergeArrayHandling.Merge
:合并数组项,按索引匹配.
This is the behavior you are experiencing -- the existing array and all the items therein are being discarded and replaced with a fresh array containing newly constructed items that have default values. In contrast, you require MergeArrayHandling.Merge
: Merge array items together, matched by index.
填充为读/写集合(例如List<T>
)的成员的工作方式类似于MergeArrayHandling.Concat
.
Populating members that are read/write collections such as List<T>
seems to work like MergeArrayHandling.Concat
.
请求增强功能(Populate()
支持此设置)似乎是合理的- -尽管我不知道实施起来有多么容易.至少Populate()
的文档应该解释这种行为.
It seems reasonable to request an enhancement that Populate()
support this setting -- though I don't know how easy it would be to implement. At the minimum the documentation for Populate()
should explain this behavior.
同时,这是一个自定义JsonConverter
,具有模拟MergeArrayHandling.Merge
行为的必要逻辑:
In the meantime, here's a custom JsonConverter
with the necessary logic to emulate the behavior of 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
成员:
Then add the converter to your subClass
member as follows:
[JsonConverter(typeof(ArrayMergeConverter))]
public SubClass[] subClass;
或者,如果您不想将Json.NET属性添加到数据模型,则可以在序列化程序设置中添加它:
Or, if you don't want to add Json.NET attributes to your data model, you can add it in serializer settings:
var settings = new JsonSerializerSettings
{
Converters = new[] { new ArrayMergeConverter<MainClass.SubClass>() },
};
JsonSerializer.CreateDefault(settings).Populate(sr, MainClass.array[i]);
该转换器是专门为数组设计的,但是可以很容易地为诸如List<T>
之类的读/写集合创建类似的转换器.
The converter is specifically designed for arrays but a similar converter could easily be created for read/write collections such as List<T>
.
这篇关于JsonSerializer.CreateDefault().Populate(..)重置我的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!