У вас есть три проблемы:
Как объяснено в этот ответ в Пользовательский преобразователь Json.NET не должен сериализовать свойство :
A custom JsonConverter
не может помешать сериализации его значения, так как ссылающееся на него имя свойства уже будет записано ко времениконвертер вызывается.В архитектуре Json.NET ответственность содержащего типа заключается в том, чтобы решить , какие его свойств следует сериализовать;Затем преобразователь значений решает как сериализовать записываемое значение.
NullValueHandling.Ignore
не работает, поскольку свойство Entity.Number
равно не нуль , он имеет значение, а именно выделенную структуру HasValue<int?>
с внутренним значением null
:
Number = new HasValue<int?>(number) // Not Number = null
Аналогично DefaultValueHandling.Ignore
не работает, потому что default(HasValue<int?>?)
имеет то же значение, что и нулевое значение, допускающее значение NULL, которое, как упоминалось выше, отличается от значения, присвоенного Number
.
Так что жеВаши варианты здесь?
Вы можете использовать условную сериализацию свойства , чтобы подавить сериализацию Number
, когда его значение не является нулевым, но имеет нулевое внутреннее значение:
public class Entity
{
[JsonConverter(typeof(NullJsonConverter))]
public HasValue<int?>? Number { get; set; }
public bool ShouldSerializeNumber() { return Number.HasValue && Number.Value.Value.HasValue; }
}
Демонстрационная скрипка # 1 здесь .
Однако этот дизайн выглядит немного слишком сложным - у вас есть nullable, содержащий структуру, которая инкапсулирует nullable, содержащий целое число- т.е. Nullable<HasValue<Nullable<int>>>
.Вам действительно нужны оба уровня Nullable?Если нет, вы можете просто удалить внешние Nullable<>
и DefaultValueHandling
теперь будут просто работать :
public class Entity
{
[JsonConverter(typeof(NullJsonConverter))]
public HasValue<int?> Number { get; set; }
}
Демо-скрипта # 2 здесь .
В обоих случаях я обобщил NullJsonConverter
для обработки всех возможных типов T
для HasValue<T>
следующим образом:
public struct HasValue<T> : IHasValue
{
// Had to convert to c# 4.0 syntax for dotnetfiddle
T m_value;
public HasValue(T value) { this.m_value = value; }
public T Value { get { return m_value; } set { m_value = value; } }
public object GetValue() { return Value; }
}
internal interface IHasValue
{
object GetValue();
}
public class NullJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType) { throw new NotImplementedException(); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var valueType = objectType.GetGenericArguments()[0];
var valueValue = serializer.Deserialize(reader, valueType);
return Activator.CreateInstance(objectType, valueValue);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, ((IHasValue)value).GetValue());
}
}
В частности:
- Изменение
Value
Свойство для ввода. - Добавление неуниверсального интерфейса для доступа к значению как объекту во время сериализации.
- Непосредственная десериализация внутреннего значения с последующим вызовом параметризованного конструктора во время десериализации.
Таким образом, вы можете применить [JsonConverter(typeof(NullJsonConverter))]
к HasValue<T>
, если хотите.
Вы также можете рассмотреть возможность сделать структуру HasValue<T>
неизменной по причинам, объясненным в Почему изменчивые структуры «злые»? .