使用JSON.NET的序列化字段的顺序

Kev*_*ose 122 c# json json.net

有没有办法使用JSON.NET指定序列化JSON对象中的字段顺序?

指定单个字段始终首先出现就足够了.

Ste*_*eve 232

支持的方法是JsonProperty在要为其设置顺序的类属性上使用该属性.有关更多信息,请阅读JsonPropertyAttribute订单文档.

传递JsonProperty一个Order值,序列化器将处理其余的事情.

 [JsonProperty(Order = 1)]
Run Code Online (Sandbox Code Playgroud)

这非常类似于

 DataMember(Order = 1) 
Run Code Online (Sandbox Code Playgroud)

System.Runtime.Serialization日子.

这是来自@ kevin-babcock的重要说明

...将订单设置为1只有在所有其他属性上设置大于1的订单时才有效.默认情况下,任何没有Order设置的属性都将被赋予-1的顺序.因此,您必须提供所有序列化属性和订单,或将您的第一个项目设置为-2

  • 使用`JsonPropertyAttribute`的`Order`属性可以用来控制字段序列化/反序列化的顺序.但是,只有在所有其他属性上设置大于1的订单时,才将订单设置为1.默认情况下,任何没有Order设置的属性都将被赋予-1的顺序.因此,您必须提供所有序列化属性和订单,或将您的第一个项目设置为-2. (92认同)
  • 它适用于序列化,但在反序列化时不考虑顺序。根据文档, order 属性用于序列化和反序列化。有解决方法吗? (2认同)
  • @cangosta反序列化的顺序无关紧要......除了一些非常"奇怪"的期望案例. (2认同)

Mat*_*erg 120

实际上,你可以通过实现控制命令IContractResolver或重写DefaultContractResolverCreateProperties方法.

这是我的简单实现的一个例子,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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后设置设置并序列化对象,JSON字段将按字母顺序排列:

var settings = new JsonSerializerSettings()
{
    ContractResolver = new OrderedContractResolver()
};

var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
Run Code Online (Sandbox Code Playgroud)

  • 这非常有帮助(+1),但有一点需要注意:看起来字典的序列化不使用此CreateProperties自定义.他们序列化很好,但最终没有排序.我假设有一种不同的方式来自定义字典的序列化,但我还没有找到它. (8认同)

nia*_*her 15

就我而言,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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是我们使用dicts时所需要的修复. (2认同)

小智 9

在我的情况下,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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Jay*_*hah 6

这也适用于普通类、字典和 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);
Run Code Online (Sandbox Code Playgroud)

  • 为了节省其他人浪费的几分钟时间,请注意,尽管有这样的说法,但该答案不适用于字典。在字典序列化期间不会调用“CreateProperties”。我探索了 JSON.net 存储库,了解哪些机器实际上正在浏览字典条目。它不会挂钩任何“覆盖”或其他订购自定义。它只是从对象的枚举器中按原样获取条目。看来我必须构造一个“SortedDictionary”或“SortedList”来强制 JSON.net 执行此操作。提交的功能建议:https://github.com/JamesNK/Newtonsoft.Json/issues/2270 (6认同)

twi*_*kes 5

如果您只想将单个属性拉到前面而不考虑可能不直观的数字系统,只需使用int.MinValue

[JsonProperty(Order = int.MinValue)]
Run Code Online (Sandbox Code Playgroud)