有没有一种方法可以使用JSON.NET指定序列化JSON对象中的字段顺序?
只需指定一个字段始终首先出现就足够了。
有没有一种方法可以使用JSON.NET指定序列化JSON对象中的字段顺序?
只需指定一个字段始终首先出现就足够了。
Answers:
支持的方法是JsonProperty
在要为其设置顺序的类属性上使用该属性。阅读JsonPropertyAttribute订单文档以获取更多信息。
传递JsonProperty
一个Order
值,串行器将处理其余的事情。
[JsonProperty(Order = 1)]
这非常类似于
DataMember(Order = 1)
的System.Runtime.Serialization
日子。
这是@ kevin-babcock的重要说明
...仅当您在所有其他属性上将顺序设置为大于1时,才能将顺序设置为1。默认情况下,任何没有“顺序”设置的属性都将被赋予-1的顺序。因此,您必须提供所有序列化的属性和顺序,或将第一个项目设置为-2
Order
属性JsonPropertyAttribute
可用于控制字段序列化/反序列化的顺序。但是,只有在所有其他属性上设置的顺序都大于1时,才能将顺序设置为1。默认情况下,任何不带“顺序”设置的属性都将被赋予-1的顺序。因此,您必须提供所有序列化的属性和顺序,或者将第一个项目设置为-2。
JavaScriptSerializer
。
实际上,你可以通过实现控制命令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);
就我而言,马蒂亚斯的答案行不通。该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;
}
}
在我的情况下,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;
}
}
}
这也适用于普通类,词典和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
如果您不想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);
以下递归方法使用反射对现有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);
}
}
实际上,由于我的对象已经是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));
我想序列化一个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();
}
}
如果要使用顺序字段全局配置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();
}
}
在这里我的答案:
更新
我刚刚看到了反对派。请参阅下面“史蒂夫”的答案。
原版的
我JsonConvert.SerializeObject(key)
通过反射(其中的键是一个IList)跟踪了方法调用,发现JsonSerializerInternalWriter.SerializeList被调用了。它需要一个列表并循环通过
for (int i = 0; i < values.Count; i++) { ...
其中values是引入的IList参数。
简短的答案是...不,没有内置的方法可以设置字段在JSON字符串中列出的顺序。
JSON格式的字段没有顺序,因此定义顺序是没有意义的。
{ id: 1, name: 'John' }
等价于{ name: 'John', id: 1 }
(均代表严格等效的对象实例)