Сериализация нулевого значения в 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 ++ для функций протокола ввода-вывода протокола Buffer в Java?
  • Как изменить имена свойств при сериализации с помощью Json.net?
  • Json.net сериализует определенное частное поле
  • Jackson ObjectMapper - указать порядок сортировки свойств объекта
  • Служба WCF Максимальная квота длины массива (16384) превышена
  • Ошибка JSON.NET Локальный цикл привязки для типа
  • Серийный цикл ActiveRecord с использованием JSON вместо YAML
  • jackson не десериализует общий список, который он сериализовал
  • Каковы различия между XmlSerializer и BinaryFormatter
  • JSON.Net Обнаружен собственный цикл привязки
  • Когда и почему сущности JPA должны реализовывать интерфейс Serializable?
  • Interesting Posts

    Mail.app: отображение отправленных сообщений в виде потока

    Почему возникла связанная с сетью или конкретная ошибка экземпляра при установлении соединения с SQL Server?

    WebDriver – дождитесь использования элемента с помощью Java

    JUnit 4 @BeforeClass & @AfterClass при использовании Suites

    C # маленький конец или большой endian?

    Как Windows 8 Runtime (приложения для WinRT / Windows Store / Windows 10 Universal App) сравниваются с Silverlight и WPF?

    Преобразование с двойным преобразованием без научной нотации

    Трюки для ускорения времени запуска? Windows 7

    C # Math calculator

    Является ли шумный вентилятор ноутбука причиной загрязнения на вентиляторе?

    Отправка файла Multipart в виде параметров POST с помощью запросов RestTemplate

    Как очистить кеш Facebook Sharer?

    Воспроизведение звука с использованием примера soundpool

    Использовать неподписанный драйвер в Windows 7 x64

    Настройка переменных среды через launchd.conf больше не работает в OS X Yosemite / El Capitan / macOS Sierra?

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