Порядок сериализованных полей с использованием JSON.NET - PullRequest
114 голосов
/ 26 июля 2010

Есть ли способ указать порядок полей в сериализованном объекте JSON, используя JSON.NET ?

Было бы достаточно указать, что одно поле всегда появляется первым.

Ответы [ 13 ]

216 голосов
/ 26 декабря 2012

Поддерживаемый способ - использовать атрибут JsonProperty в свойствах класса, для которого вы хотите установить порядок. Прочитайте документацию заказа JsonPropertyAttribute для получения дополнительной информации.

Передайте значение JsonProperty и Order, а сериализатор позаботится обо всем остальном.

 [JsonProperty(Order = 1)]

Это очень похоже на

 DataMember(Order = 1) 

из System.Runtime.Serialization дней.

Вот важное замечание от @ kevin-babcock

... установка порядка 1 будет работать, только если вы установите порядок больше 1 для всех других свойств. По умолчанию любое свойство без параметра Order будет иметь порядок -1. Таким образом, вы должны либо дать все сериализованные свойства и порядок, либо установить свой первый элемент на -2

116 голосов
/ 03 июля 2012

Фактически вы можете контролировать заказ, реализовав IContractResolver или переопределив CreateProperties метод *1003*.

Вот пример моей простой реализации 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);
15 голосов
/ 17 февраля 2015

В моем случае ответ Маттиаса не сработал. Метод 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;
    }
}
9 голосов
/ 18 ноября 2016

В моем случае решение 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;
        }
    }
}
3 голосов
/ 03 апреля 2017

Как заметил Чарли, вы можете в некоторой степени управлять упорядочением свойств JSON, упорядочивая свойства в самом классе. К сожалению, этот подход не работает для свойств, унаследованных от базового класса. Свойства базового класса будут упорядочены так, как они выложены в коде, но появятся перед свойствами базового класса.

И для тех, кто интересуется, почему вы можете захотеть расположить по алфавиту свойства JSON, гораздо проще работать с необработанными файлами JSON, особенно для классов с большим количеством свойств, если они упорядочены.

2 голосов
/ 13 марта 2019

Это будет работать и для обычных классов, словарей и ExpandoObject (динамический объект).

0 голосов
/ 15 февраля 2019

Я хочу сериализовать объект comblex и сохранить порядок свойств, как они были определены в коде.Я не могу просто добавить [JsonProperty(Order = 1)], потому что сам класс находится вне моей области.

Это решение также учитывает, что свойства, определенные в базовом классе, должны иметь более высокий приоритет.1005 * Это не может быть пуленепробиваемым, поскольку нигде не определено, что 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();
    }
}

0 голосов
/ 26 сентября 2018

Если вы хотите глобально настроить свой API с упорядоченными полями, объедините ответ Маттиаса Нордберга:

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?

0 голосов
/ 27 ноября 2016

Если вы управляете (то есть пишете) классом, поместите свойства в алфавитном порядке, и они будут сериализованы в алфавитном порядке при вызове JsonConvert.SerializeObject().

0 голосов
/ 09 июня 2016

На самом деле, поскольку мой объект уже был объектом 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));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...