我在这里读过 Is the order of elements in a JSON list preserved?
Json中的顺序很重要。
我也在这里读过 Does List<T> guarantee insertion order? 保证在C#通用列表中的插入顺序。
我是否可以假设当我使用newtonsoft JsonSerializer来读取此Json
"answers": [
{
"choice": "36"
},
{
"choice": "50"
}
]
在属性为“答案”的对象中,该对象的类型为GenericList,答案[0]始终返回36,答案[1]始终返回50?
或者JsonSerializer可能会在周围乱洗数据吗?
我问的原因是我从一个外部API读取数据,他们说“您应该只得到1个答案,但是当您得到更多答案时,请使用最后一个答案”,而最后一个是文本中的最后一个答案,因此在本例中为“ 50”。
答案 0 :(得分:1)
是的,您可以依靠通过JsonSerializer
读取的数组节点的顺序。 JSON数组被定义为按排序的值的集合。。Json.NET会按照在JSON文件中遇到的顺序将它们添加到您的集合中。
这可以通过检查source code来验证。方法JsonSerializerInternalReader.CreateList()
是负责集合反序列化的顶级方法,它具有以下三种基本情况:
反序列化为读/写集合时,将按从JSON流中读取值的顺序调用ICollection.Add()
(或ICollection<T>.Add()
),从而保留JSON数组的顺序。可以在JsonSerializerInternalReader.PopulateList()
中看到。
(当集合缺少非通用的Add(object value)
方法时,将创建一个CollectionWrapper<T>
来处理强制转换为所需参数类型的操作,但是这完全不影响算法,因为包装器立即调用基础集合的Add(T Value)
方法。)
反序列化为.Net数组时,会为一些适当的List<T>
创建一个临时T
,并按照情况1的顺序通过{{1}添加值。 }或JsonSerializerInternalReader.PopulateMultidimensionalArray()
。随后,通过依次调用Array.CreateInstance
然后List<T>.ICollection.CopyTo(Array, Int32)
或CollectionUtils.ToMultidimensionalArray()
将列表转换为数组。两者都创建一个数组,以保留传入集合的值顺序。
在反序列化不可变集合时,该集合必须具有采用JsonSerializerInternalReader.PopulateList()
的构造函数,其中IEnumerable<T>
是集合项类型。 release notes for Json.NET 6.0.3中对此进行了解释:
对于将来所有不可变.NET集合的创建者:如果T的集合具有一个使用
T
的构造函数,那么Json.NET将在反序列化到您的集合时自动运行,否则您将很不走运。
假设您的不可变集合具有所需的构造函数,算法将按照情况2进行处理,反序列化为IEnumerable<T>
,然后从列表中构造碰到的不可变集合,并按遇到和反序列化的顺序排列值。
当然,集合本身可能会改变值的顺序:
反序列化为SortedSet<T>
时,值将由集合comparer动态重新排序。
在反序列化为Stack<T>
时,存在一个已知的问题,其中在反序列化时堆栈顺序相反:Issue #971: JsonConvert.DeserializeObject<Stack<T>>/JsonConvert.Serialize(Stack<T)
does not work as expected。
这里的问题是List<T>
实现了Stack<T>
而不是IEnumerable<T>
,因此Json.NET认为它是一个不可变的集合。幸运的是,它的required constructor接受了一个单独的ICollection<T>
输入参数。不幸的是,由于它是堆栈,因此在枚举时会颠倒输入的顺序。
有关详细信息,请参见 JsonConvert.Deserializer indexing issues 。
但是对于基本集合IEnumerable<T>
或List<T>
,这不会发生。