Игнорирование некоторых значений по умолчанию для сериализации - PullRequest
2 голосов
/ 06 апреля 2020

ТЛ; др; В Newtonsoft JSON. NET как игнорировать значения по умолчанию для некоторых типов (перечислений), а не для других (целых)?


Моя команда использует библиотеку, которая использует буферы протокола для их хозяйствующие субъекты. Каждое перечисление в этой библиотеке / protobuf имеет значение по умолчанию 0, "ValueNotSet". Моя команда использует Newtonsoft JSON. NET для сериализации этих объектов. Вот разбавленный пример для инвентаря пекарни:

public enum Flavor { ValueNotSet, Cherry, Blueberry, Cheese };
public class DanishInventory { public int QtyInStock; public Flavor; }

Чтобы сэкономить ресурсы, мы не хотим сериализовать ValueNotSet (сценарий реального мира имеет много перечислений), но наличие нулевого количества сыра в запасах - это действительный и мы делаем хотим сериализовать ноль. Из-за этого мы не можем использовать DefaultValueHandling = Ignore в настройках.

Я создал собственный JsonConverter, но к тому времени, когда вызывается запись Json (...), ключ уже находится в JsonWriter. Поэтому, если я ничего не пишу, JSON является недопустимым, и я не вижу очевидного метода, позволяющего переписать записывающее устройство для перезаписи ключа. Итак, как лучше всего игнорировать значения по умолчанию для некоторых типов (например, перечислений), но не для других (например, целых)?

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

Ответы [ 2 ]

2 голосов
/ 06 апреля 2020

Вы можете использовать вариант DefaultValueContractResolver от этого ответа до Json. NET: как сделать DefaultValueHandling применимым только к определенным типам? для исключения всех перечисляемых свойств со значениями по умолчанию:

public class EnumDefaultValueContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        if (property.DefaultValueHandling == null)
        {
            if (property.PropertyType.IsEnum)
            {
                //For safety you could check here if the default value is named ValueNotSet and only set IgnoreAndPopulate in that case.
                //var defaultValue = Enum.ToObject(property.PropertyType, 0);
                //if (defaultValue.ToString() == "ValueNotSet")
                //{                 
                    property.DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate; // Or DefaultValueHandling.Ignore if you prefer
                //}
            }
        }

        return property;
    }
}

Затем используйте его следующим образом:

var resolver = new EnumDefaultValueContractResolver();

var settings = new JsonSerializerSettings { ContractResolver = resolver };
var json = JsonConvert.SerializeObject(inventory, settings);

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

Демонстрационная скрипка здесь .

0 голосов
/ 06 апреля 2020

Многие сериализаторы (включая Json. NET, я полагаю) поддерживают шаблон ShouldSerialize*(); если вы не возражаете против этого, вы можете сделать следующее:

public class DanishInventory {
    public int QtyInStock;
    public Flavor;
    public bool ShouldSerializeFlavor() => Flavor != 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...