Skip to content

Instantly share code, notes, and snippets.

@lucd
Last active June 11, 2021 07:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lucd/cdd57a2602bd975ec0a6 to your computer and use it in GitHub Desktop.
Save lucd/cdd57a2602bd975ec0a6 to your computer and use it in GitHub Desktop.
This code shows how to specify a navigation path in the JsonProperty attribute. It will traverse the JSON node and obtain the desired value to according to the path. Example [JsonProperty(parent/child)] will map to the value "a" from the JSON { "parent":{ "child": "a"} }. http://stackoverflow.com/questions/35628318/deserialize-nested-json-to-a-f…
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
/// <summary>
/// This code shows how to specify a navigation path in the JsonProperty attribute.
/// It will traverse the JSON node and obtain the desired value to according to the path.
/// Example
/// [JsonProperty(parent/child)] will map to the value "a" from the JSON { "parent":{ "child": "a"} }.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
string s = @"
{
""id"": 100,
""fields"":{
""issuetype"": {
""name"": ""I am a Jira issue""
}
}
}";
var settings = new JsonSerializerSettings();
settings.Converters.Add(new ConventionBasedConverter());
var o = JsonConvert.DeserializeObject<JiraIssue>(s, settings);
Console.WriteLine("Id: " + o.Id);
Console.WriteLine("Type: " + o.Type);
}
}
[JsonObject]
public class JiraIssue
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("fields/issuetype/name")]
public string Type { get; set; }
}
/// <summary>
/// Custom converter that allows mapping a JSON value according to a navigation path.
/// Credits to: http://stackoverflow.com/users/3887840/amit-kumar-ghosh
/// </summary>
class ConventionBasedConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(JiraIssue).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var daat = JObject.Load(reader);
var issue = new JiraIssue();
foreach (var prop in issue.GetType().GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance))
{
var attr = prop.GetCustomAttributes(false).FirstOrDefault();
if (attr != null)
{
var propName = ((JsonPropertyAttribute)attr).PropertyName;
if (!string.IsNullOrWhiteSpace(propName))
{
//split by the delimiter, and traverse recursevly according to the path
var conventions = propName.Split('/');
object propValue = null;
JToken token = null;
for (var i = 0; i < conventions.Length; i++)
{
if (token == null)
{
token = daat[conventions[i]];
}
else {
token = token[conventions[i]];
}
if (token == null)
{
//silent fail: exit the loop if the specified path was not found
break;
}
else
{
//store the current value
if (token is JValue)
{
propValue = ((JValue)token).Value;
}
}
}
if (propValue != null)
{
//workaround for numeric values being automatically created as Int64 (long) objects.
if (propValue is long && prop.PropertyType == typeof(Int32))
{
prop.SetValue(issue, Convert.ToInt32(propValue));
}
else
{
prop.SetValue(issue, propValue);
}
}
}
}
}
return issue;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
}
}
@zipben
Copy link

zipben commented Apr 29, 2020

Thank you for this. I spent 20 minutes looking at answers on SO that danced around this simple solution.

@stevemerckel
Copy link

stevemerckel commented Nov 18, 2020

@MinnowTman - thank you! That gist really helped me out.

@SerhiyBalan
Copy link

I've posted a solution with classes support here (based on @MinnowTman code)
https://stackoverflow.com/a/67932675/4601817

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment