Подтвердить что ты не робот

Может ли Newtonsoft Json.NET пропускать сериализацию пустых списков?

Я пытаюсь сериализовать некоторые устаревшие объекты, которые "ленивы" создают разные списки. Я не могу изменить прежнее поведение.

Я перевернул его на этот простой пример:

public class Junk
{
    protected int _id;

    [JsonProperty( PropertyName = "Identity" )]
    public int ID 
    { 
        get
        {
            return _id;
        }

        set
        {
            _id = value;
        }
    }

    protected List<int> _numbers;
    public List<int> Numbers
    {
        get
        {
            if( null == _numbers )
            {
                _numbers = new List<int>( );
            }

            return _numbers;
        }

        set
        {
            _numbers = value;
        }
    }
}

class Program
{
    static void Main( string[] args )
    {
        Junk j = new Junk( ) { ID = 123 };

        string newtonSoftJson = JsonConvert.SerializeObject( j, Newtonsoft.Json.Formatting.Indented );

        Console.WriteLine( newtonSoftJson );

    }
}

Текущие результаты: { "Идентичность": 123, "Числа": [] }

Я хотел бы получить: { "Идентичность": 123 }

То есть, я хотел бы пропустить любые списки, коллекции, массивы или такие пустые вещи.

4b9b3361

Ответ 1

Если вы не нашли для этого решения, ответ замечательно прост, если вам удастся его отслеживать.

Если вам разрешено расширять исходный класс, добавьте к нему функцию ShouldSerializePropertyName. Это должно возвращать логическое значение, указывающее, должно ли это свойство быть сериализовано для текущего экземпляра класса. В вашем примере это может выглядеть так (не проверено, но вы должны получить изображение):

public bool ShouldSerializeNumbers()
{
    return _numbers.Count > 0;
}

Этот подход работает для меня (хотя и в VB.NET). Если вам не разрешено изменять исходный класс, то метод IContractResolver, описанный на связанной странице, - это путь.

Ответ 2

Просто, чтобы быть pendantic commonorgarden, я структурировал тест if:

public bool ShouldSerializecommunicationmethods()
{
    if (communicationmethods != null && communicationmethods.communicationmethod != null && communicationmethods.communicationmethod.Count > 0)
        return true;
    else
        return false;
}

В качестве пустого списка часто также будет null. Спасибо за публикацию решения. ATB.

Ответ 3

Что касается предложения Дэвида Джонса использовать IContractResolver, то это работает для меня, чтобы охватить все варианты IEnumerables без явного изменения класса, который необходимо сериализовать:

public class ShouldSerializeContractResolver : DefaultContractResolver
{
    public static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (property.PropertyType.GetInterface(nameof(IEnumerable)) != null)
            property.ShouldSerialize =
                instance => (instance?.GetType().GetProperty(property.PropertyName).GetValue(instance) as IEnumerable<object>)?.Count() > 0;

        return property;
    }
}

Затем я встраиваю его в свой объект настроек:

static JsonSerializerSettings JsonSettings = new JsonSerializerSettings
{
    Formatting = Formatting.Indented,
    NullValueHandling = NullValueHandling.Ignore,
    DefaultValueHandling = DefaultValueHandling.Ignore,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    ContractResolver = ShouldSerializeContractResolver.Instance,
};

и используйте это так:

JsonConvert.SerializeObject(someObject, JsonSettings);

Ответ 4

Брайан, вы, скорее всего, там, где вам не нужны служебные данные переменной экземпляра, и вам нужно перехватывать оба экземпляра поля и члена, плюс я бы не стал запускать операцию подсчета, которая требует, чтобы перечислимое значение исчерпало всю коллекцию, вы можете просто запустить Функция MoveNext().

public class IgnoreEmptyEnumerableResolver : CamelCasePropertyNamesContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member,
        MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        if (property.PropertyType != typeof(string) &&
            typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
        {
            property.ShouldSerialize = instance =>
            {
                IEnumerable enumerable = null;
                // this value could be in a public field or public property
                switch (member.MemberType)
                {
                    case MemberTypes.Property:
                        enumerable = instance
                            .GetType()
                            .GetProperty(member.Name)
                            ?.GetValue(instance, null) as IEnumerable;
                        break;
                    case MemberTypes.Field:
                        enumerable = instance
                            .GetType()
                            .GetField(member.Name)
                            .GetValue(instance) as IEnumerable;
                        break;
                }

                return enumerable == null ||
                       enumerable.GetEnumerator().MoveNext();
                // if the list is null, we defer the decision to NullValueHandling
            };
        }

        return property;
    }
}