'C# Map Json Dictionary to nested class

I have a Dicionary with key values.

It can be dictionary or json string like below:

[
    {
        "Key": "UserName",
        "Text": "Jack"
    },
    {
        "Key": "UserAge",
        "Text": 30
    },
    {
        "Key": "Address",
        "Text": "Address Location"
    },
    {
        "Key": "Id",
        "Text": 10
    }
]

I want to map above json to class below:

public class Record
{
    public User User { get; set; }
    public Location Location { get; set; }
    [Description("Id")]
    [JsonProperty("Id")]
    public int Id { get; set; }
}

public class User
{
    [Description("UserName")]
    [JsonProperty("UserName")]
    public string Name { get; set; }
    [Description("UserAge")]
    [JsonProperty("UserAge")]
    public int Age { get; set; }
}

public class Location
{
    [Description("Address")]
    [JsonProperty("Address")]
    public string Address { get; set; }
}

I am able to define the attribute with Description or JsonProperty.

Is there any way to achieve this?



Solution 1:[1]

You can do this in 3 steps:

1. Read the data into a collection a records

First define the record type:

record PropertyNameAndValue([property:JsonProperty("Key")]string Name, [property: JsonProperty("Text")] object Value);

I've used the [property:JsonProperty(...)] annotations to be able to use arbitrary names for the record's properties.

Let's read the json

var propertyNameAndValues = JsonConvert.DeserializeObject<PropertyNameAndValue[]>(json);

2. Populate a flattened structure

Let's define a class with 4 properties:

class Model
{
    public long Id { get; set; }
    public string UserName { get; set; }
    public long UserAge { get; set; }
    public string Address { get; set; }
}

I've defined Id and UserAge as long instead of int since Json.NET will parse these numbers as System.Int64.

Let's populate with data

var model = new Model();
foreach (var property in propertyNameAndValues)
{
    typeof(Model).GetProperty(property.Name).SetValue(model, property.Value);
}

3. Define an AutoMapper rule

Since I'm not using AutoMapper on a daily basis I can't show you right now you how the mapping rule should look like. Later this week I might have enough time to play with it a bit :D


UPDATE #1

Thanks for the suggestion for Lucian Bargaoanu here is the mapping:

var config = new MapperConfiguration(cfg =>
    cfg.CreateMap<Record, Model>()
    .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.Location.Address))
    .ForMember(d => d.UserAge, opt => opt.MapFrom(src => src.User.Age))
    .ForMember(d => d.UserName, opt => opt.MapFrom(src => src.User.Name))
    .ReverseMap()
);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1