Home > OS >  C# map (deserialize) JSON API result that contains the object's properties as a numbered list
C# map (deserialize) JSON API result that contains the object's properties as a numbered list

Time:11-13

I've searched SO and google for a similar question, but couldn't find the exact thing I'm searching.

I'm developing an application that queries an API that returns the following result:

{
    "Ticket": {
        "1": {
            "DisplayName": "Ticket ID",
            "Value": 117
        },
        "2": {
            "DisplayName": "Last Modified",
            "Value": "2022-10-05T18:09:32.1070000Z"
        },
        "3": {
            "DisplayName": "Last User",
            "Value": "SYSTEMACCOUNT"
        },
        "4": {
            "DisplayName": "Seq_Group",
            "Value": 1
        },
        ...
    }
}

And I want to deserialize it to an object like the following:

public class Ticket 
{
    public property int TicketID {get; set;}
    public property DateTime LastModified {get; set;}
    public property string LastUser {get; set;}
    public property int Seq_Group {get; set;}
}

(there are several more properties hidden here for brevity)

Can you point me to the direction on how to do that mapping the best way?

I know I could deserialize it to a Dictionary and then iterate the dictionary with several ifs and elses, but I think there must be a smarter way to solve it.

Thanks!

CodePudding user response:

you can try this

    var ticketObj = new JObject();

    foreach (var prop in ((JObject)JObject.Parse(json)["Ticket"]).Properties())
        ticketObj.Add(new JProperty((string)prop.Value["DisplayName"], prop.Value["Value"]));

    Ticket ticket = ticketObj.ToObject<Ticket>();


public class Ticket
{
    [JsonProperty("Ticket ID")]
    public int TicketID { get; set; }
    
    [JsonProperty("Last Modified")]
    public DateTime LastModified { get; set; }
    
    [JsonProperty("Last User")]
    public string LastUser { get; set; }
    
    public int Seq_Group { get; set; }
}

CodePudding user response:

You can work with JsonConstructorAttribute and System.Reflection for the property mapping.

  1. Use of the [DisplayName] attribute to define which property name should be mapped from the JSON.
using System.ComponentModel; 

public class Ticket 
{
    [DisplayName("Ticket ID")]
    public int TicketID {get; set;}
    [DisplayName("Last Modified")]
    public DateTime LastModified {get; set;}
    [DisplayName("Last User")]
    public string LastUser {get; set;}
    public int Seq_Group {get; set;}    
}
  1. An extension method to retrieve the property name from the [DisplayName] attribute (if applied) or its default property name.
using System.ComponentModel; 
using System.Reflection;

public static class ReflectionExtensions
{
    public static string ToName(this PropertyInfo propertyInfo)
    {
        try
        {
            object[] displayNameAttributes = propertyInfo
                .GetCustomAttributes(typeof(DisplayNameAttribute), false)
                .ToArray();

            if (displayNameAttributes != null && displayNameAttributes.Count() > 0)
                return ((DisplayNameAttribute)displayNameAttributes[0]).DisplayName;

            return propertyInfo.Name;
        }
        catch
        {
            return propertyInfo.Name;
        }
    }
}
  1. Implement the conversion logic in the constructor by applying the [JsonConstructor] attribute.
using System.ComponentModel; 
using System.Reflection;
using Newtonsoft.Json.Linq;
using System.Globalization;

public class Root
{
    public Ticket Ticket { get; set; }
    
    [JsonConstructor]
    public Root(Dictionary<string, JObject> ticket)
    {
        Ticket = new Ticket();
        
        foreach (KeyValuePair<string, JObject> kvp in ticket)
        {
            string propertyName = kvp.Value.SelectToken("DisplayName").ToString();
            
            PropertyInfo propInfo = typeof(Ticket).GetProperties().FirstOrDefault(x => x.ToName() == propertyName);
            if (propInfo != null)
            {
                JToken value = kvp.Value.SelectToken("Value");
                
                Type propType = propInfo.PropertyType;
                if (propType == typeof(DateTime))
                    propInfo.SetValue(Ticket, DateTime.Parse(value.ToString()));                
                else
                    propInfo.SetValue(Ticket, value.ToObject(propType));
            }
        }
    }
}

Demo @ .NET Fiddle

  • Related