序列化非可空类型,就好像它们可以为空,使用单独的属性来指示是否存在值

时间:2016-11-16 11:28:44

标签: c# serialization json.net

我有一堆我无法控制的类,它们是在可空类型之前创建的。这些类中使用的约定如下:

Id
IdSpecified = false

换句话说,对于名为“Abc”的给定非可空属性,有一个名为“AbcSpecified”的单独布尔属性,它指示第一个属性是否具有值。

有没有办法将非可空属性视为可空,并在序列化和反序列化期间排除以Specified结尾的所有属性?我希望我能够做到这一点,因为有超过100个这样的类。

1 个答案:

答案 0 :(得分:0)

我能够使用自定义IContractResolverIValueProvider为此设置通用解决方案。合同解析器负责识别属性对" Xyz"和" XyzSpecified"在每个班级(我分别称这些"目标"和"指示符"属性),并确保每对都确保指标属性被排除,同时还附加一个值提供程序实例到目标属性。反过来,值提供程序处理为每个对象实例读取或写入的内容的决定。在序列化时,如果指标属性设置为true,它只会写出目标属性。相反,在反序列化时,它根据JSON中是否存在值来设置指标属性,但只有在存在值时才设置对象的目标属性。

以下是自定义解析程序和值提供程序的代码:

> fmt = re.split(r'\[(-?[0-9.]+,\s?-?[0-9.]+).\s*\d\s*(\d{4}-\d{1,2}-\d{1,2}\s+\d{2}:\d{2}:\d{2})',example)
> fmt # watch what happens when I change the grouping.
['', '27.994195699999999, -82.569434900000005', '2011-08-28 19:02:36', ' text text text text']
> location = literal_eval('(' + fmt[1] + ')')
> time = fmt[2]
> text = fmt[3]

要使用解析程序,请创建一个新的public class CustomResolver : DefaultContractResolver { const string IndicatorKeyword = "Specified"; protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { IList<JsonProperty> props = base.CreateProperties(type, memberSerialization); Dictionary<string, JsonProperty> dict = props.ToDictionary(p => p.UnderlyingName); foreach (JsonProperty prop in props) { string name = prop.UnderlyingName; if (name.Length > IndicatorKeyword.Length && name.EndsWith(IndicatorKeyword)) { // We have an indicator property; ignore it for serialization purposes prop.Ignored = true; // Find the corresponding target property, e.g. "XyzSpecified" => "Xyz" string targetName = name.Substring(0, name.Length - IndicatorKeyword.Length); JsonProperty coProp = null; if (dict.TryGetValue(targetName, out coProp)) { // Create a value provider for the property pointing to the // "real" target and indicator properties from the containing type PropertyInfo realTarget = type.GetProperty(targetName); PropertyInfo realIndicator = type.GetProperty(name); coProp.ValueProvider = new CustomValueProvider(realTarget, realIndicator); } } } return props; } class CustomValueProvider : IValueProvider { PropertyInfo targetProperty; PropertyInfo indicatorProperty; public CustomValueProvider(PropertyInfo targetProperty, PropertyInfo indicatorProperty) { this.targetProperty = targetProperty; this.indicatorProperty = indicatorProperty; } // GetValue is called by Json.Net during serialization. // The target parameter has the object from which to read the value; // the return value is what gets written to the JSON public object GetValue(object target) { bool isSpecified = (bool)indicatorProperty.GetValue(target); return isSpecified ? targetProperty.GetValue(target) : null; } // SetValue gets called by Json.Net during deserialization. // The value parameter has the value read from the JSON; // target is the object on which to set the value. public void SetValue(object target, object value) { bool isSpecified = value != null; indicatorProperty.SetValue(target, isSpecified); if (isSpecified) targetProperty.SetValue(target, value); } } } 实例,然后将JsonSerializerSettings属性设置为解析程序的新实例。将设置传递给ContractResolverJsonConvert.SerializeObject()方法,一切正常。

这是一个完整的往返演示:https://dotnetfiddle.net/i39c8d