Сериализация нулевого значения в 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 для выполнения большей части работы с обновленным тестом

  • Как сериализовать объект в CSV-файл?
  • Json Сериализация Java, которая работает с GWT
  • Как превратить объект C # в строку JSON в .NET?
  • Как получить json.net для сериализации членов classа, полученных из List ?
  • Сериализация OpenCV Mat_
  • Json.Net: свойство Serialize / Deserialize в качестве значения, а не как объект
  • ShouldSerialize * () vs * Условный шаблон условной сериализации
  • C # JSON.NET - десериализация ответа, использующего необычную структуру данных
  • StackOverflowError при сериализации объекта в Java
  • Как сериализовать Java-примитивы с помощью Jersey REST
  • Каковы различия между XmlSerializer и BinaryFormatter
  • Interesting Posts

    Каково значение # в URL-адресе и как я могу это использовать?

    Как уменьшить размер файла изображения перед загрузкой на сервер

    Скопировать объект в объект (с помощью Automapper?)

    Структура иерархических данных в пустых строках

    Как реализовать PriorityBlockingQueue с помощью ThreadPoolExecutor и настраиваемых задач

    Файлы Rsync через промежуточный хост

    Как запустить GCC скомпилированный вывод на диске NTFS?

    gVim найти / заменить счетчиком

    Аутентифицированная среда равна нулю

    Изучение принципа единой ответственности с помощью C #

    Удобно отображать между enum и int / String

    jquery / js – Как выбрать родительскую форму, на основе которой нажата кнопка отправки?

    Как использовать клавишу Tab для отступов в текстовом поле (вместо перехода к следующему элементу)?

    Добавление или вычитание цвета из изображения в pictureBox с использованием C #

    Как преобразовать ImageSource в массив байтов?

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