Search code examples
c#automapperautomapper-6

AutoMapper - Map Json string property to interface based object


I have a source class like this:

public partial class Source
{
    ...
    public int ScheduleBaseId { get; set; }
    public int ScheduleIncrement { get; set; }
    public int SubscriptionTypeId { get; set; } // <- Determines the concrete class to map to
    public string SubscriptionCriteriaJson { get; set; } // <- Map to interface class
    ...
}

Which I'm mapping to this destination class:

public class Dest
{
    ...
    public Schedule Schedule { get; set; }
    public ISubscriptionCriteria SubscriptionCriteria { get; set; }
    ...
}

I'd like to map the Source.SubscriptionCriteriaJson property to Dest.SubscriptionCriteria which uses an interface. The concrete class for the interface can be determined using Source.SubscriptionTypeId. There's two issues in tandem I'm trying to resolve here for mapping to SubscriptionCriteria:

  1. De-serializing the json string to a ISubscriptionCriteria object.
  2. Mapping to the correct concrete type for ISubscriptionCriteria based on SubscriptionTypeId.

Any ideas / pointers how I achieve this in AutoMapper? I'm new to AutoMapper so am still feeling my way around.

This is what I have so far for the rest of the mapping:

var config = new MapperConfiguration(
    cfg => cfg.CreateMap<Source, Dest>()
        .ForMember(dest => dest.Schedule, opt => { opt.MapFrom(src => new Schedule((ScheduleBaseEnum)src.ScheduleBaseId, src.ScheduleIncrement)); })
    );

Solution

  • I solved this in the following way:

    public class AutoMapperProfileConfiguration : Profile
    {
        public AutoMapperProfileConfiguration()
            : this("MyProfile")
        {
        }
    
        protected AutoMapperProfileConfiguration(string profileName)
            : base(profileName)
        {
            CreateMap<Source, Dest>()
                .ForMember(dest => dest.Schedule, opt =>
                {
                    opt.MapFrom(src => new Schedule((ScheduleBaseEnum)src.ScheduleBaseId, src.ScheduleIncrement));
                })
                .ForMember(dest => dest.SubscriptionCriteria, opt =>
                {
                    opt.MapFrom(src => (ISubscriptionCriteria)JsonConvert.DeserializeObject(src.SubscriptionCriteriaJson, GetSubscriptionCriteriaType((SubscriptionTypeEnum)src.SubscriptionTypeId)));
                });
        }
    
        private Type GetSubscriptionCriteriaType(SubscriptionTypeEnum type)
        {
            switch (type)
            {
                case SubscriptionTypeEnum.SomeType1:
                    return typeof(SomeType1);
                case SubscriptionTypeEnum.SomeType2:
                    return typeof(SomeType2);
                ...
                default:
                    throw new NotImplementedException(string.Format("SubscriptionType of {0} is not implemented.", Enum.GetName(typeof(SubscriptionTypeEnum), type)));
            }
        }
    }
    

    If there's a more elegant solution you can think of please share I'm keen to learn!