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.
- 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;}
}
- 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;
}
}
}
- 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));
}
}
}
}