Сериализация нулевого значения в JSON.NET
При сериализации произвольных данных через JSON.NET любое свойство, которое является null, записывается в JSON как
“propertyName”: null
Это, конечно, правильно.
- Рекомендации по сериализации объектов в пользовательский строковый формат для использования в выходном файле
- Можете ли вы определить, был ли объект, десериализованный объект, отсутствием поля с classом JsonConvert в Json.NET
- Есть ли у .NET 4 встроенный сериализатор / десериализатор JSON?
- JSON.NET Parser * кажется * будет двойной сериализацией моих объектов
- Удаление десериализации полиморфных classов json без информации о типе с использованием json.net
Однако у меня есть требование автоматически перевести все значения 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 и т. Д.
Итак, мой вопрос – не нужно ли конвертировать всю структуру данных вручную, есть ли какое-либо решение или обходное решение для этого?
- Как реализовать TypeAdapterFactory в Gson?
- Джерси + jackson JSON формат даты формат - как изменить формат или использовать пользовательские JacksonJsonProvider
- Исключение «Исключительная привязка к реляционной ссылке» с JSON.Net
- ShouldSerialize * () vs * Условный шаблон условной сериализации
- Как вы сериализуете объект на C ++?
- gwt - Использование списка в вызове RPC?
- Сериализовать компоненты JavaFX
- jQuery serializeArray не включает кнопку отправки, которая была нажата
Хорошо, я думаю, что я придумал решение (мое первое решение было не совсем правильным, но потом снова я был в поезде). Вам необходимо создать специальный контрактный преобразователь и настраиваемый 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
для выполнения большей части работы с обновленным тестом