Сериализация нулевого значения в JSON.NET

При сериализации произвольных данных через JSON.NET любое свойство, которое является null, записывается в JSON как

“propertyName”: null

Это, конечно, правильно.

Однако у меня есть требование автоматически перевести все значения null в пустую string по умолчанию, например, нулевая string s должна стать String.Empty , null int? s должно стать 0 , null bool? s должно быть false и т. д.

NullValueHandling не помогает, так как я не хочу Ignore нули, но я также не хочу Include их (Хм, новая функция?).

Поэтому я обратился к внедрению пользовательского JsonConverter .
Хотя сама реализация была ветерок, к сожалению, это все еще не сработало – CanConvert() никогда не вызывается для свойства с нулевым значением, и поэтому WriteJson() не вызывается. По-видимому, нули автоматически сериализуются непосредственно в null , без настраиваемого конвейера.

Например, здесь приведен пример пользовательского конвертера для нулевых строк:

 public class StringConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(string).IsAssignableFrom(objectType); } ... public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { string strValue = value as string; if (strValue == null) { writer.WriteValue(String.Empty); } else { writer.WriteValue(strValue); } } } 

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

Вдаваясь в исходный код JSON.NET, я обнаружил, что (по-видимому, я не вдавался в большую глубину) существует специальный случай проверки нhive и явный вызов .WriteNull() .

Для чего это стоит, я попытался реализовать пользовательский JsonTextWriter и переопределить реализацию по умолчанию .WriteNull()

 public class NullJsonWriter : JsonTextWriter { ... public override void WriteNull() { this.WriteValue(String.Empty); } } 

Однако это не сработает, поскольку метод WriteNull() ничего не знает о базовом WriteNull() данных. Поэтому я уверен, что могу вывести "" для любого нулевого значения, но это не работает хорошо, например, int, bool и т. Д.

Итак, мой вопрос – не нужно ли конвертировать всю структуру данных вручную, есть ли какое-либо решение или обходное решение для этого?

Хорошо, я думаю, что я придумал решение (мое первое решение было не совсем правильным, но потом снова я был в поезде). Вам необходимо создать специальный контрактный преобразователь и настраиваемый ValueProvider для типов Nullable. Учти это:

 public class NullableValueProvider : IValueProvider { private readonly object _defaultValue; private readonly IValueProvider _underlyingValueProvider; public NullableValueProvider(MemberInfo memberInfo, Type underlyingType) { _underlyingValueProvider = new DynamicValueProvider(memberInfo); _defaultValue = Activator.CreateInstance(underlyingType); } public void SetValue(object target, object value) { _underlyingValueProvider.SetValue(target, value); } public object GetValue(object target) { return _underlyingValueProvider.GetValue(target) ?? _defaultValue; } } public class SpecialContractResolver : DefaultContractResolver { protected override IValueProvider CreateMemberValueProvider(MemberInfo member) { if(member.MemberType == MemberTypes.Property) { var pi = (PropertyInfo) member; if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof (Nullable<>)) { return new NullableValueProvider(member, pi.PropertyType.GetGenericArguments().First()); } } else if(member.MemberType == MemberTypes.Field) { var fi = (FieldInfo) member; if(fi.FieldType.IsGenericType && fi.FieldType.GetGenericTypeDefinition() == typeof(Nullable<>)) return new NullableValueProvider(member, fi.FieldType.GetGenericArguments().First()); } return base.CreateMemberValueProvider(member); } } 

Затем я протестировал его, используя:

 class Foo { public int? Int { get; set; } public bool? Boolean { get; set; } public int? IntField; } 

И следующий случай:

 [TestFixture] public class Tests { [Test] public void Test() { var foo = new Foo(); var settings = new JsonSerializerSettings { ContractResolver = new SpecialContractResolver() }; Assert.AreEqual( JsonConvert.SerializeObject(foo, Formatting.None, settings), "{\"IntField\":0,\"Int\":0,\"Boolean\":false}"); } } 

Надеюсь, это немного поможет …

Изменить – Лучшая идентификация типа Nullable<>

Edit – добавлена ​​поддержка полей, а также свойств, а также поддержка DynamicValueProvider поверх обычного DynamicValueProvider для выполнения большей части работы с обновленным тестом

  • C # десериализация структуры после ее получения через TCP
  • Как можно полиморфная десериализация Json String с использованием Java и библиотеки Jackson?
  • Django rest framework, используйте разные сериализаторы в том же ModelViewSet
  • Можно ли выполнять бинарную сериализацию .NET в случае, если у вас нет исходного кода classа?
  • Преобразование байтов в Int / uint в C
  • Игнорирование нулевых полей в Json.net
  • jackson не десериализует общий список, который он сериализовал
  • Может ли Json.NET сериализовать / десериализовать в / из streamа?
  • Можете ли вы определить, был ли объект, десериализованный объект, отсутствием поля с classом JsonConvert в Json.NET
  • Обнаружено, что
  • Почему java.util.Optional не является Serializable, как сериализовать объект с такими полями
  • Interesting Posts

    Отправка электронной почты с помощью JSP

    Получение пути относительно текущего рабочего каталога?

    VirtualBox 4.3.20 перестает работать после обновления Windows

    Ноутбук де-краппинг

    Как компьютеры отображают исходный, низкоуровневый текст и графику

    React Native fetch () Ошибка сетевого запроса

    Для чего используются диски Windows A и B:

    WinRT и региональные настройки. Правильный способ форматирования дат и чисел на основе региональных настроек пользователя?

    Получить список тем

    Как настроены PCI / PCIe BAR для доступа к памяти на устройстве?

    В чем разница между публичным, частным, защищенным и не имеющим модификатора доступа?

    Как нарисовать дерево, представляющее график подключенных узлов?

    Есть ли случай, когда полезно использовать ссылку RValue Reference (&&)?

    Разделить временные ряды данных на временные интервалы (скажем, час), а затем построить график

    Как и где используются annotations, используемые в Java?

    Давайте будем гением компьютера.