Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automapper:Converting JSON to list of objects

Source Object (JSON, using JSON.NET if it matters):

{
    "conum" : 1001,
    "name" : "CLN Industries Corporation",
    "agencyName" : "Murphy, Holmes & Associates, LLC",
    "sAA" : [{
            "code" : 247,
            "description" : "Mechanic\u0027s lien - Bond to Discharge - Fixed penalty - where principal has posted Performance and Pa"
        }, {
            "code" : 277,
            "description" : "Mechanic\u0027s lien - Bond to Discharge - Open Penalty - where principal has posted Performance and Paym"
        }, {
            "code" : 505,
            "description" : "Indemnity Bonds - Contractor\u0027s Indemnity Against Damages where there is a performance bond and addit"
        }
    ]
}

Destination Object (C#):

public class CorporateRatesInfo
{
    public string Conum { get; set; }
    public string Name { get; set; }
    public string AgencyName { get; set; }
    public List<SaaCode> SaaCodes { get; set; }
}

public class SaaCode
{
    public string Code { get; set; }
    public string Description { get; set; }
}

Automapper config and mappings:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<JObject, CorporateRatesInfo>();
    cfg.AddProfile<CorporateRatesProfile>();
});

//config.AssertConfigurationIsValid();
_mapper = config.CreateMapper();


public class CorporateRatesProfile : Profile
{
    protected override void Configure()
    {
        CreateMap<JObject, CorporateRatesInfo>()
            .ForMember("SaaCodes", cfg => { cfg.MapFrom(jo => jo["sAA"]); })
            .ForMember("Conum", cfg => { cfg.MapFrom(jo => jo["conum"]); })
            .ForMember("Name", cfg => { cfg.MapFrom(jo => jo["name"]); })
            .ForMember("AgencyName", cfg => { cfg.MapFrom(jo => jo["agencyName"]); });
    }
}

Everything works except for the SaaCodes conversion, where Automapper converts each entry into an empty SaaCode object (all properties set to null).

How/where do I tell automapper how to convert items in that JSON field into its destination type?

like image 813
SvdSinner Avatar asked Jun 29 '16 18:06

SvdSinner


People also ask

Is AutoMapper fast?

Automapper is slower when mapping a single object and collections but there is one exception. Automapper is considerably faster when mapping a List<T> of objects on . NET Core (It's still slower on full .

Can AutoMapper map collections?

AutoMapper supports polymorphic arrays and collections, such that derived source/destination types are used if found.

How does AutoMapper work in C#?

AutoMapper in C# is a library used to map data from one object to another. It acts as a mapper between two objects and transforms one object type into another. It converts the input object of one type to the output object of another type until the latter type follows or maintains the conventions of AutoMapper.


2 Answers

Try this out -

public class CorporateRatesInfo
{
    public string Conum { get; set; }
    public string Name { get; set; }
    public string AgencyName { get; set; }
    public List<SaaCode> SaaCodes { get; set; }
}

public class SaaCode
{
    public string Code { get; set; }
    public string Description { get; set; }
}

public class CorporateRatesProfile : Profile
{
    protected override void Configure()
    {
        CreateMap<JObject, SaaCode>()
            .ForMember("Code", cfg => { cfg.MapFrom(jo => jo["code"]); })
            .ForMember("Description", cfg => { cfg.MapFrom(jo => jo["description"]); });

        CreateMap<JObject, CorporateRatesInfo>()
            .ForMember("SaaCodes", cfg => { cfg.MapFrom(jo => jo["sAA"]); })
            .ForMember("Conum", cfg => { cfg.MapFrom(jo => jo["conum"]); })
            .ForMember("Name", cfg => { cfg.MapFrom(jo => jo["name"]); })
            .ForMember("AgencyName", cfg => { cfg.MapFrom(jo => jo["agencyName"]); });
        

    }
}

class Program
{
    static void Main(string[] args)
    {

        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<JObject, CorporateRatesInfo>();
            cfg.AddProfile<CorporateRatesProfile>();
        });

        //config.AssertConfigurationIsValid();
        var mapper = config.CreateMapper();

        var jsonText =  @"
                            {
                                ""conum"" : 1001,
                                ""name"" : ""CLN Industries Corporation"",
                                ""agencyName"" : ""Murphy, Holmes & Associates, LLC"",
                                ""sAA"" : [{
                                        ""code"" : 247,
                                        ""description"" : ""Mechanic\u0027s lien - Bond to Discharge - Fixed penalty - where principal has posted Performance and Pa""
                                    }, {
                                        ""code"" : 277,
                                        ""description"" : ""Mechanic\u0027s lien - Bond to Discharge - Open Penalty - where principal has posted Performance and Paym""
                                    }, {
                                        ""code"" : 505,
                                        ""description"" : ""Indemnity Bonds - Contractor\u0027s Indemnity Against Damages where there is a performance bond and addit""
                                    }
                                ]
                            }
                        ";

        var jsonoObj = JObject.Parse(jsonText);

        CorporateRatesInfo dto = mapper.Map<CorporateRatesInfo>(jsonoObj);

        

    }
}

EDIT: Per https://docs.automapper.org/en/stable/Configuration.html, Automapper version 6 and above does not support Configure() method. So I've adjusted my answer slightly to follow the constructor approach.

public class CorporateRatesInfo
{
    public string Conum { get; set; }
    public string Name { get; set; }
    public string AgencyName { get; set; }
    public List<SaaCode> SaaCodes { get; set; }
}

public class SaaCode
{
    public string Code { get; set; }
    public string Description { get; set; }
}

public class CorporateRatesProfile : Profile
{
    public CorporateRatesProfile()
    {
        CreateMap<JToken, SaaCode>()
            .ForMember("Code", cfg => { cfg.MapFrom(jo => jo["code"]); })
            .ForMember("Description", cfg => { cfg.MapFrom(jo => jo["description"]); });

        CreateMap<JToken, CorporateRatesInfo>()
            .ForMember("SaaCodes", cfg => { cfg.MapFrom(jo => jo["sAA"]); })
            .ForMember("Conum", cfg => { cfg.MapFrom(jo => jo["conum"]); })
            .ForMember("Name", cfg => { cfg.MapFrom(jo => jo["name"]); })
            .ForMember("AgencyName", cfg => { cfg.MapFrom(jo => jo["agencyName"]); });
    }
}

class Program
{
    static void Main(string[] args)
    {
        var config = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile<CorporateRatesProfile>();
        });
        
        var mapper = config.CreateMapper();
        var jsonText =  @"{""conum"":1001,""name"":""CLN Industries Corporation"",""agencyName"":""Murphy, Holmes & Associates, LLC"",""sAA"":[{""code"":247,""description"":""Mechanic's lien - Bond to Discharge - Fixed penalty - where principal has posted Performance and Pa""},{""code"":277,""description"":""Mechanic's lien - Bond to Discharge - Open Penalty - where principal has posted Performance and Paym""},{""code"":505,""description"":""Indemnity Bonds - Contractor's Indemnity Against Damages where there is a performance bond and addit""}]}";
        var jsonoObj = JObject.Parse(jsonText);
        CorporateRatesInfo dto = mapper.Map<CorporateRatesInfo>(jsonoObj);
    }
}
like image 88
Bookamp Avatar answered Oct 05 '22 21:10

Bookamp


A report, I wrote simple solution if the field names match you can use Newtonsoft's deserializer inside the mapper extension helper method. I wrote a generic ResolveJson extension method.

public static class MapperExtensions
{
    public static T ResolveJson<T>(this JObject jobj, string target)
    {
        return JsonConvert.DeserializeObject<T>(jobj.SelectToken(target).ToString());
    }
}

which you can then call by using the below

CreateMap<JObject, CorporateRatesInfo>()
    .ForMember(x => x.SaaCodes,m => { m.MapFrom(s => s.ResolveJson<SaaCode>("sAA"));});
like image 23
Joshua Duxbury Avatar answered Oct 05 '22 20:10

Joshua Duxbury