使用 JSON.NET 的序列化字段的顺序
Posted
技术标签:
【中文标题】使用 JSON.NET 的序列化字段的顺序【英文标题】:Order of serialized fields using JSON.NET 【发布时间】:2011-03-20 20:20:52 【问题描述】:有没有办法使用JSON.NET 指定序列化 JSON 对象中字段的顺序?
指定一个字段总是首先出现就足够了。
【问题讨论】:
我认为他可能有兴趣先显示 ID 字段(或类似字段),然后再显示所有其他字段。这对最终用户来说比在以 A..I 开头的字段后查找更友好 JSON 属性被定义为无序。我认为在序列化期间强制特定的 OUTPUT 顺序绝对没问题(也许是为了观察 JSON),但是在反序列化的任何特定顺序上创建 DEPENDENCY 将是一个糟糕的决定。 几个正当理由:(1) 伪造必须是 JSON 中的第一个属性的“$type”属性,(2) 尝试生成尽可能压缩的 JSON 另一个原因可能是 (3) 使用 JSON 语法的规范表示 - 必须保证相同的对象产生相同的 JSON 字符串。属性的确定顺序是这样做的必要先决条件。 Kevin,你能更新一下这个问题的接受答案吗? 【参考方案1】:支持的方法是在要为其设置顺序的类属性上使用JsonProperty
属性。阅读JsonPropertyAttribute order documentation了解更多信息。
传递 JsonProperty
和 Order
值,序列化程序将处理其余的工作。
[JsonProperty(Order = 1)]
这个很像
DataMember(Order = 1)
System.Runtime.Serialization
天。
这是来自@kevin-babcock 的重要说明
... 仅当您在所有其他属性上设置大于 1 的顺序时,将顺序设置为 1 才有效。默认情况下,任何没有 Order 设置的属性都将被赋予 -1 的顺序。所以你必须要么给出所有序列化的属性和顺序,要么将你的第一个项目设置为 -2
【讨论】:
使用JsonPropertyAttribute
的Order
属性可用于控制字段序列化/反序列化的顺序。但是,仅当您在所有其他属性上设置的顺序大于 1 时,将顺序设置为 1 才有效。默认情况下,任何没有 Order 设置的属性都将被赋予 -1 的顺序。因此,您必须要么提供所有序列化的属性和顺序,要么将您的第一项设置为 -2。
它适用于序列化,但在反序列化时不考虑顺序。根据文档, order 属性用于序列化和反序列化。有解决办法吗?
javascriptSerializer
是否有类似的属性。
@cangosta 反序列化的顺序应该无关紧要......除了一些非常“奇怪”的预期情况。
阅读关于在反序列化中尊重 Order 的愿望的类似 github 问题讨论:github.com/JamesNK/Newtonsoft.Json/issues/758 基本上没有这个机会。【参考方案2】:
您实际上可以通过实现IContractResolver
或覆盖DefaultContractResolver
的CreateProperties
方法来控制订单。
这是我对IContractResolver
的简单实现的示例,它按字母顺序排列属性:
public class OrderedContractResolver : DefaultContractResolver
protected override System.Collections.Generic.IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList();
然后设置设置并序列化对象,JSON字段将按字母顺序排列:
var settings = new JsonSerializerSettings()
ContractResolver = new OrderedContractResolver()
;
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
【讨论】:
这很有帮助(+1),但有一个警告:字典的序列化似乎不使用此 CreateProperties 自定义。他们序列化很好,但最终没有排序。我假设有一种不同的方法可以自定义字典的序列化,但我还没有找到。 这是一个很好的解决方案。非常适合我,尤其是在并排放置 2 个 JSON 对象并排列属性时。【参考方案3】:就我而言,Mattias 的回答无效。 CreateProperties
方法从未被调用过。
在对Newtonsoft.Json
内部进行了一些调试之后,我想出了另一个解决方案。
public class JsonUtility
public static string NormalizeJsonString(string json)
// Parse json string into JObject.
var parsedObject = JObject.Parse(json);
// Sort properties of JObject.
var normalizedObject = SortPropertiesAlphabetically(parsedObject);
// Serialize JObject .
return JsonConvert.SerializeObject(normalizedObject);
private static JObject SortPropertiesAlphabetically(JObject original)
var result = new JObject();
foreach (var property in original.Properties().ToList().OrderBy(p => p.Name))
var value = property.Value as JObject;
if (value != null)
value = SortPropertiesAlphabetically(value);
result.Add(property.Name, value);
else
result.Add(property.Name, property.Value);
return result;
【讨论】:
这是我们在使用 dicts 时需要解决的问题。 这增加了额外的反序列化和序列化的开销。我添加了一个适用于普通类、字典和 ExpandoObject(动态对象)的解决方案 总体上有用,但缺少对数组的处理(对数组内对象内的对象键进行排序)。相对容易添加。【参考方案4】:在我的情况下,niaher 的解决方案不起作用,因为它不处理数组中的对象。
根据他的解决方案,这是我想出的
public static class JsonUtility
public static string NormalizeJsonString(string json)
JToken parsed = JToken.Parse(json);
JToken normalized = NormalizeToken(parsed);
return JsonConvert.SerializeObject(normalized);
private static JToken NormalizeToken(JToken token)
JObject o;
JArray array;
if ((o = token as JObject) != null)
List<JProperty> orderedProperties = new List<JProperty>(o.Properties());
orderedProperties.Sort(delegate(JProperty x, JProperty y) return x.Name.CompareTo(y.Name); );
JObject normalized = new JObject();
foreach (JProperty property in orderedProperties)
normalized.Add(property.Name, NormalizeToken(property.Value));
return normalized;
else if ((array = token as JArray) != null)
for (int i = 0; i < array.Count; i++)
array[i] = NormalizeToken(array[i]);
return array;
else
return token;
【讨论】:
这增加了额外的反序列化和序列化的开销。【参考方案5】:这也适用于普通类、字典和 ExpandoObject(动态对象)。
class OrderedPropertiesContractResolver : DefaultContractResolver
protected override IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
var props = base.CreateProperties(type, memberSerialization);
return props.OrderBy(p => p.PropertyName).ToList();
class OrderedExpandoPropertiesConverter : ExpandoObjectConverter
public override bool CanWrite
get return true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
var expando = (IDictionary<string, object>)value;
var orderedDictionary = expando.OrderBy(x => x.Key).ToDictionary(t => t.Key, t => t.Value);
serializer.Serialize(writer, orderedDictionary);
var settings = new JsonSerializerSettings
ContractResolver = new OrderedPropertiesContractResolver(),
Converters = new OrderedExpandoPropertiesConverter()
;
var serializedString = JsonConvert.SerializeObject(obj, settings);
【讨论】:
这不是序列化过程中的默认排序行为吗? 为了节省其他人浪费的几分钟,请注意,尽管声称,此答案不适用于字典。CreateProperties
在字典序列化期间不会被调用。我探索了 JSON.net 存储库,以了解哪些机器实际上正在浏览字典条目。它不会挂接到任何override
或其他定制的订购。它只是从对象的枚举器中获取条目。看来我必须构造一个 SortedDictionary
或 SortedList
来强制 JSON.net 执行此操作。功能建议提交:github.com/JamesNK/Newtonsoft.Json/issues/2270【参考方案6】:
如果您只想将单个属性拉到前面而不考虑可能不直观的数字系统,只需使用int.MinValue
。
[JsonProperty(Order = int.MinValue)]
【讨论】:
【参考方案7】:正如 Charlie 所指出的,您可以通过对类本身的属性进行排序来在一定程度上控制 JSON 属性的排序。不幸的是,这种方法不适用于从基类继承的属性。基类属性将按照它们在代码中的布局进行排序,但会出现在基类属性之前。
对于任何想知道为什么要按字母顺序排列 JSON 属性的人,使用原始 JSON 文件要容易得多,特别是对于具有大量属性的类(如果它们是有序的)。
【讨论】:
【参考方案8】:如果您不想在每个类属性上放置 JsonProperty
Order
属性,那么制作自己的 ContractResolver 非常简单...
IContractResolver 接口提供了一种方法来自定义 JsonSerializer 如何将 .NET 对象序列化和反序列化为 JSON,而无需在类上放置属性。
像这样:
private class SortedPropertiesContractResolver : DefaultContractResolver
// use a static instance for optimal performance
static SortedPropertiesContractResolver instance;
static SortedPropertiesContractResolver() instance = new SortedPropertiesContractResolver();
public static SortedPropertiesContractResolver Instance get return instance;
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
var properties = base.CreateProperties(type, memberSerialization);
if (properties != null)
return properties.OrderBy(p => p.UnderlyingName).ToList();
return properties;
实施:
var settings = new JsonSerializerSettings ContractResolver = SortedPropertiesContractResolver.Instance ;
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
【讨论】:
【参考方案9】:其实我的Object已经是一个JObject了,所以我用了下面的方案:
public class SortedJObject : JObject
public SortedJObject(JObject other)
var pairs = new List<KeyValuePair<string, JToken>>();
foreach (var pair in other)
pairs.Add(pair);
pairs.OrderBy(p => p.Key).ForEach(pair => this[pair.Key] = pair.Value);
然后像这样使用它:
string serializedObj = JsonConvert.SerializeObject(new SortedJObject(dataObject));
【讨论】:
【参考方案10】:以下递归方法使用反射对现有JObject
实例上的内部令牌列表进行排序,而不是创建全新的排序对象图。此代码依赖于内部 Json.NET 实现细节,不应在生产环境中使用。
void SortProperties(JToken token)
var obj = token as JObject;
if (obj != null)
var props = typeof (JObject)
.GetField("_properties",
BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(obj);
var items = typeof (Collection<JToken>)
.GetField("items", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(props);
ArrayList.Adapter((IList) items)
.Sort(new ComparisonComparer(
(x, y) =>
var xProp = x as JProperty;
var yProp = y as JProperty;
return xProp != null && yProp != null
? string.Compare(xProp.Name, yProp.Name)
: 0;
));
foreach (var child in token.Children())
SortProperties(child);
【讨论】:
【参考方案11】:如果您控制(即编写)该类,请将属性按字母顺序排列,当调用JsonConvert.SerializeObject()
时,它们将按字母顺序序列化。
【讨论】:
【参考方案12】:我想序列化一个complex 对象并保持属性的顺序,因为它们在代码中定义。我不能只添加[JsonProperty(Order = 1)]
,因为该类本身超出了我的范围。
此解决方案还考虑到在基类中定义的属性应具有更高的优先级。
这可能不是万无一失的,因为没有任何地方定义 MetaDataAttribute
确保正确的顺序,但它似乎有效。对于我的用例,这没关系。因为我只想为自动生成的配置文件保持人类可读性。
public class PersonWithAge : Person
public int Age get; set;
public class Person
public string Name get; set;
public string GetJson()
var thequeen = new PersonWithAge Name = "Elisabeth", Age = Int32.MaxValue ;
var settings = new JsonSerializerSettings()
ContractResolver = new MetadataTokenContractResolver(),
;
return JsonConvert.SerializeObject(
thequeen, Newtonsoft.Json.Formatting.Indented, settings
);
public class MetadataTokenContractResolver : DefaultContractResolver
protected override IList<JsonProperty> CreateProperties(
Type type, MemberSerialization memberSerialization)
var props = type
.GetProperties(BindingFlags.Instance
| BindingFlags.Public
| BindingFlags.NonPublic
).ToDictionary(k => k.Name, v =>
// first value: declaring type
var classIndex = 0;
var t = type;
while (t != v.DeclaringType)
classIndex++;
t = type.BaseType;
return Tuple.Create(classIndex, v.MetadataToken);
);
return base.CreateProperties(type, memberSerialization)
.OrderByDescending(p => props[p.PropertyName].Item1)
.ThenBy(p => props[p.PropertyName].Item1)
.ToList();
【讨论】:
【参考方案13】:如果您想使用有序字段全局配置您的 API,请结合 Mattias Nordberg 答案:
public class OrderedContractResolver : DefaultContractResolver
protected override System.Collections.Generic.IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList();
这里有我的回答:
How to force ASP.NET Web API to always return JSON?
【讨论】:
【参考方案14】:更新
我刚刚看到了反对票。请参阅下面“史蒂夫”的答案,了解如何执行此操作。
原创
我通过反射跟踪 JsonConvert.SerializeObject(key)
方法调用(其中键是 IList),发现调用了 JsonSerializerInternalWriter.SerializeList。它需要一个列表并通过
for (int i = 0; i < values.Count; i++) ...
其中 values 是引入的 IList 参数。
简短的回答是...不,没有内置方法来设置字段在 JSON 字符串中列出的顺序。
【讨论】:
简短回答,但可能已过时。查看史蒂夫的回答(由 James Newton-king 提供支持)【参考方案15】:JSON 格式的字段没有顺序,因此定义顺序没有意义。
id: 1, name: 'John'
等价于 name: 'John', id: 1
(都表示严格等价的对象实例)
【讨论】:
@Darin - 但序列化中有一个顺序。 " id: 1, name: 'John' " 和 " name: 'John', id: 1 " 作为 strings 是不同的,这是我在这里关心的。当然,反序列化后的对象是等价的。 @Darin - 不,在这种情况下不是。我正在序列化某些内容,然后将其作为字符串传递给仅处理字符串(不支持 JSON)的服务,出于各种原因,一个字段首先出现在字符串中会很方便。跨度> 它也有利于测试,能够只查看字符串而不必反序列化。 稳定的序列化顺序对于缓存验证也很方便。对字符串进行校验和是微不足道的——对于完整的对象图来说不是这样。 序列化顺序在进行单元测试时也很方便,因此即使 json 属性的顺序不同,您也可以轻松地说预期响应字符串与实际响应字符串相等。以上是关于使用 JSON.NET 的序列化字段的顺序的主要内容,如果未能解决你的问题,请参考以下文章
使用 JSON.NET 反序列化 DateTime 时如何保留时区? [复制]