Mapping JSON to objects using Automapper

In one of my recent project we had a situation where we had a huge JSON array which was supposed to be mapped to different model objects. We did some investigation and decided to go with combination of Jpath and AutoMapper to achieve this task and it worked pretty well for us.

Prerequisites

In this blog I will not explain basics of automapper.If you are new to automapper you can refer automapper documentation to get through the basics.

Scenario

We get an array of employees as JSON coming from an API and we need to map some of these to a destination object. Although below JSON is a much simplified version of the actual JSON but I guess it conveys the point.

Employee array :

{
	'Items': [{
		'Id': '1',
		'EmployeeNo': '1001',
		'FirstName': 'Rajat',
		'LastName': 'Kumar',
		'Gender': {
			'Id': '1',
			'ShortName': 'M',
			'FullName': 'Male'
		}
	}, {
		'Id': '2',
		'EmployeeNo': '1003',
		'FirstName': 'Lisa',
		'LastName': 'David',
		'Gender': {
			'Id': '2',
			'ShortName': 'F',
			'FullName': 'Female'
		}
	}]
}

And this needs to be mapped to a model like …

public class User
{
public int Id { get; set; }
public string DisplayName { get; set; }
public string Gender { get; set; }
 }

We need to map this array to get a List of users.
Whole idea here is to make auto mapper understand how JSON would be converted to a List. For this we used the concept of TypeConverter in Automapper.
Automapper configuration looks something like…

public UserProfile()
        {
            CreateMap<JObject, List<User>>().ConvertUsing<JObjectToUserListConverter>();
            var employeeMap = CreateMap<JToken, User>();
   
            employeeMap.ForMember(x => x.Id, y => y.MapFrom(j => j.SelectToken(".Id")));
            employeeMap.ForMember(x => x.DisplayName, y => y.MapFrom(j => j.SelectToken(".LastName").ToString() + ", " + j.SelectToken(".FirstName").ToString()));
            employeeMap.ForMember(x => x.Gender, y => y.MapFrom(j => j.SelectToken(".Gender.FullName")));

            employeeMap.ForMember(x => x.Login, y => y.MapFrom(j => j.SelectToken(".Login")));

        }

and here’s how the TypeConverter looks like…

public class JObjectToUserListConverter : ITypeConverter<JObject, List<User>>
    {
        public List<User> Convert(JObject source, List<User> destination, ResolutionContext context)
        {
            
            var userList = new List<User>();
            var tokenCountItems = source.SelectTokens("Items").Children().Count();
            for (int i = 0; i < tokenCountItems; i++)
                {
                    var token = source.SelectToken($"Items[{i}]");
                    var result = new User();

                    if (token != null)
                    {
                        result = context.Mapper.Map<JToken, User>(token);
                    }
                    userList.Add(result);
                }
            }
            
            return userList;
        }
    }
}

So the question is what is the need of a TypeConverter ?.

In current situation automapper doesn’t understand how a JSON array of employee nodes would be mapped to a List of C# POCO and this is what we use a TypeConverter for. In type converter we are just iterating through the JSON array and then converting individual nodes to User objects (using automapper mapping between a JToken and User object) and adding it to a List. And for specifying which attribute of the JSON maps to properties of the User object we are using JPath expressions which can query Json and map it to properties.

Result is a fully mapped list of User object from JSON array nodes.

image

Tagged on: , , ,

One thought on “Mapping JSON to objects using Automapper

  1. Stéphane

    Hi
    Thanks for this articles. I follow your sample but It does not want to Create an instance of JObjectToUserListConverter . I’m in Asp.net Core 3.1.

    An ideas ?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.