AutoMapper能否映射属性不全的对象并区分null的两种成因?
区分AutoMapper映射中显式Null与未设置属性的Null
AutoMapper本身无法直接区分这两种Null情况——它仅处理对象的当前属性值,无法获取属性是否被显式赋值的元数据。要实现需求,必须结合C#的反序列化跟踪或动态对象解析,记录属性的赋值状态后再在映射逻辑中利用这些状态区分。
方案一:反序列化时跟踪已设置属性,结合AutoMapper处理
步骤1:修改Foo类,添加属性跟踪集合
给Foo类添加一个忽略序列化的集合,用于记录哪些属性在反序列化时被赋值:
class Foo { public string Property1 { get; set; } public string Property2 { get; set; } public string Property3 { get; set; } [JsonIgnore] public HashSet<string> SetProperties { get; } = new HashSet<string>(); }
步骤2:自定义Json.NET解析器,跟踪属性赋值
创建自定义的ContractResolver,在反序列化时记录每个被赋值的属性:
public class TrackingContractResolver : DefaultContractResolver { protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var property = base.CreateProperty(member, memberSerialization); if (member.DeclaringType == typeof(Foo) && property.PropertyName != nameof(Foo.SetProperties)) { var originalSetter = property.ValueProvider; property.ValueProvider = new TrackingValueProvider(originalSetter, member.Name); } return property; } private class TrackingValueProvider : IValueProvider { private readonly IValueProvider _originalProvider; private readonly string _propertyName; public TrackingValueProvider(IValueProvider originalProvider, string propertyName) { _originalProvider = originalProvider; _propertyName = propertyName; } public void SetValue(object target, object value) { _originalProvider.SetValue(target, value); if (target is Foo foo) { foo.SetProperties.Add(_propertyName); } } public object GetValue(object target) { return _originalProvider.GetValue(target); } } }
步骤3:使用自定义解析器反序列化
var settings = new JsonSerializerSettings { ContractResolver = new TrackingContractResolver() }; var toMap = JsonConvert.DeserializeObject<Foo>("{Property1: \"myproperty\", Property2: null }", settings);
步骤4:配置AutoMapper映射,区分两种Null情况
可以通过AfterMap添加标记,或者直接在映射时设置不同的默认值:
// 方案A:给Bar添加显式设置标记 CreateMap<Foo, Bar>() .ForMember(dest => dest.Item1, opt => opt.MapFrom(src => src.Property1)) .ForMember(dest => dest.Item2, opt => opt.MapFrom(src => src.Property2)) .ForMember(dest => dest.Item3, opt => opt.MapFrom(src => src.Property3)) .AfterMap((src, dest) => { if (src.SetProperties.Contains(nameof(Foo.Property1))) dest.ExplicitlySetItems.Add(nameof(Bar.Item1)); if (src.SetProperties.Contains(nameof(Foo.Property2))) dest.ExplicitlySetItems.Add(nameof(Bar.Item2)); if (src.SetProperties.Contains(nameof(Foo.Property3))) dest.ExplicitlySetItems.Add(nameof(Bar.Item3)); }); // 方案B:给未设置的属性指定非Null默认值 CreateMap<Foo, Bar>() .ForMember(dest => dest.Item1, opt => opt.MapFrom(src => src.Property1)) .ForMember(dest => dest.Item2, opt => opt.MapFrom(src => src.Property2)) .ForMember(dest => dest.Item3, opt => opt.MapFrom(src => src.SetProperties.Contains(nameof(Foo.Property3)) ? src.Property3 : string.Empty));
方案二:直接使用JObject动态映射
如果不需要强类型的Foo,可以直接用JObject解析JSON,通过检查键是否存在来区分:
步骤1:解析JSON为JObject
var jObject = JObject.Parse("{Property1: \"myproperty\", Property2: null }");
步骤2:配置AutoMapper映射JObject到Bar
CreateMap<JObject, Bar>() .ForMember(dest => dest.Item1, opt => opt.MapFrom(src => src["Property1"]?.Value<string>())) .ForMember(dest => dest.Item2, opt => opt.MapFrom(src => src["Property2"]?.Value<string>())) .ForMember(dest => dest.Item3, opt => opt.MapFrom(src => src.ContainsKey("Property3") ? src["Property3"]?.Value<string>() : string.Empty)) .AfterMap((src, dest) => { dest.ExplicitlySetItems.Add(nameof(Bar.Item1)); // Property1在JSON中存在 if (src.ContainsKey("Property2")) dest.ExplicitlySetItems.Add(nameof(Bar.Item2)); if (src.ContainsKey("Property3")) dest.ExplicitlySetItems.Add(nameof(Bar.Item3)); });
内容的提问来源于stack exchange,提问作者Esben Rune Hansen
相关产品推荐
相关产品推荐

