159

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

指定一个字段总是首先出现就足够了。

4

15 回答 15

272

支持的方法是使用JsonProperty要为其设置顺序的类属性上的属性。阅读JsonPropertyAttribute 订单文档以获取更多信息。

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

 [JsonProperty(Order = 1)]

这非常类似于

 DataMember(Order = 1) 

System.Runtime.Serialization日子。

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

... 仅当您在所有其他属性上设置大于 1 的顺序时,将顺序设置为 1 才有效。默认情况下,任何没有 Order 设置的属性都将被赋予 -1 的顺序。所以你必须要么给出所有序列化的属性和顺序,要么将你的第一个项目设置为 -2

于 2012-12-26T02:33:18.503 回答
137

您实际上可以通过实现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);
于 2012-07-03T10:34:58.433 回答
21

在我的情况下,马蒂亚斯的回答没有奏效。该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;
    }
}
于 2015-02-17T07:47:45.367 回答
17

就我而言,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;
        }
    }
}
于 2016-11-18T14:16:42.743 回答
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);
于 2019-03-13T17:57:44.167 回答
4

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

[JsonProperty(Order = int.MinValue)]
于 2021-04-01T23:17:34.817 回答
3

正如查理所指出的,您可以通过对类本身的属性进行排序来在一定程度上控制 JSON 属性的排序。不幸的是,这种方法不适用于从基类继承的属性。基类属性将按照它们在代码中的布局进行排序,但会出现在基类属性之前。

对于任何想知道为什么要按字母顺序排列 JSON 属性的人,使用原始 JSON 文件要容易得多,特别是对于具有大量属性的类(如果它们是有序的)。

于 2017-04-02T23:03:34.620 回答
3

如果您不想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);
于 2020-01-23T00:25:33.550 回答
2

实际上,由于我的 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));
于 2016-06-08T22:49:09.557 回答
1

以下递归方法使用反射对现有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);
    }
}
于 2012-07-06T11:40:34.583 回答
0

JsonConvert.SerializeObject()如果您控制(即编写)类,请按字母顺序放置属性,并且在调用时它们将按字母顺序序列化。

于 2016-11-27T18:15:50.137 回答
0

我想序列化一个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();
    }
}

于 2019-02-15T13:54:02.937 回答
-1

如果您想使用有序字段全局配置您的 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();
    }
}

我的回答在这里:

如何强制 ASP.NET Web API 始终返回 JSON?

于 2018-09-26T11:17:09.470 回答
-5

更新

我刚刚看到了反对票。请参阅下面“史蒂夫”的答案,了解如何执行此操作。

原来的

JsonConvert.SerializeObject(key)通过反射跟踪方法调用(其中键是 IList),发现 JsonSerializerInternalWriter.SerializeList 被调用。它需要一个列表并循环通过

for (int i = 0; i < values.Count; i++) { ...

其中 values 是引入的 IList 参数。

简短的回答是......不,没有内置的方法来设置字段在 JSON 字符串中列出的顺序。

于 2010-08-06T16:11:17.490 回答
-6

JSON 格式的字段没有顺序,因此定义顺序没有意义。

{ id: 1, name: 'John' }等价于{ name: 'John', id: 1 }(都表示严格等价的对象实例)

于 2010-07-25T21:02:37.723 回答