0

I am working on a market data processor. I am currently ingesting responses from the Bureau of Labor Statistics. I have just hit an interesting snag that I am not 100% sure how to get around.

I am using Newtonsoft to deserialize my objects and in the Calculations portion of the response it looks like this

"calculations": {
    "net_changes": {
        "1": "133",
        "3": "220",
        "6": "1985",
        "12": "2949"
     },
     "pct_changes": {
        "1": "0.1",
        "3": "0.1",
        "6": "1.2",
        "12": "1.8"
      }
}

So I created my objects like this

 public class Calculations
    {
        public Net_Changes net_changes { get; set; }
        public Pct_Changes pct_changes { get; set; }
    }

    public class Net_Changes
    {
        public string _1 { get; set; }
        public string _3 { get; set; }
        public string _6 { get; set; }
        public string _12 { get; set; }
    }

    public class Pct_Changes
    {
        public string _1 { get; set; }
        public string _3 { get; set; }
        public string _6 { get; set; }
        public string _12 { get; set; }
    }

However when I go to deserialize the root object everything seems fine until I look at the calculations portion of the object and it is null. Looking at postman data is definitely coming back. Is it possible for me to use NewtonSoft to deserialize this object with it mapping "1" to _1 (pun intended) ?

Resources I accessed
this one seemed like it might have been the ticket. Maybe I didn't understand it correctly.
Deserialize JSON with numbers as property names

Thanks for any and all help!

Adam

Adam Schneider
  • 163
  • 1
  • 12
  • 4
    Reading the post you linked, did you try making the properties public Dictionary net_changes { get; set; } or you might need public Dictionary net_changes { get; set; } because of the quotes around the numbers – Scott Mildenberger Jul 14 '23 at 20:43

3 Answers3

1

you have to fix your data structure by adding an extra class.

The easiest way is to add property names

Calculations calculations = JsonConvert.DeserializeObject<Calculations>(json);

public class Calculations
{
    public Changes calculations { get; set; }
}

public class Changes
{
    public Net_Changes net_changes { get; set; }
    public Pct_Changes pct_changes { get; set; }
}

public class Net_Changes
{
    [JsonProperty("1")]
    public string _1 { get; set; }
    [JsonProperty("3")]
    public string _3 { get; set; }
    // and so on
}

public class Pct_Changes
{
    [JsonProperty("1")]
    public string _1 { get; set; }
    [JsonProperty("3")]
    public string _3 { get; set; }
    //and so on
}

but if for some reasons you can not add property names, the second way (more messy) is to use custom IContractResolver

var settings = new JsonSerializerSettings
    {
        ContractResolver = new NumberNameContractResolver()
    };

Calculations calculations = JsonConvert.DeserializeObject<Calculations>(json,settings);

public class NumberNameContractResolver : DefaultContractResolver
{
    public static readonly NumberNameContractResolver Instance = new NumberNameContractResolver();

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);
        if (property.PropertyName.StartsWith("_"))
            property.PropertyName = property.PropertyName.Substring(1);
        return property;
    }
}

the third way is to use this code that is quite messy too

    var jObj = JObject.Parse(json);

    foreach (var jO in jObj.DescendantsAndSelf())
    {
        if (jO is JObject obj)
            foreach (var prop in obj.Properties().ToList())
                if (prop.Value.Type != JTokenType.Object)
                {
                    prop.AddAfterSelf(new JProperty("_" + prop.Name, prop.Value));
                    prop.Remove();
                }
    }
    
    Calculations  calculations = jObj.ToObject<Calculations>()
Serge
  • 40,935
  • 4
  • 18
  • 45
-1

If the structure of objects with properties with names as numbers is relatively static and simple, you can just use JsonProperty("1") attribute to map names. Example in this fiddle.

using System;
using Newtonsoft.Json;
                    
public class Program
{
    public static void Main()
    {
        var json = "{\"calculations\":{\"net\":{\"1\":\"0.1\"}}}";
        var deserialized = JsonConvert.DeserializeObject<Json>(json);
        Console.WriteLine(deserialized.Calculations.Net._1);
    }
    public record Json(Calculation Calculations);
    public record Calculation(Net Net);
    public record Net([property:JsonProperty("1")]string _1);
}
Ihor Korotenko
  • 846
  • 1
  • 13
  • 29
-2

Yes, it is possible to configure Newtonsoft.Json to handle the mapping between property names in JSON and the corresponding property names in your C# classes.

[JsonProperty("1")]
public string _1 { get; set; }

[JsonProperty("3")]
public string _3 { get; set; }

[JsonProperty("6")]
public string _6 { get; set; }

[JsonProperty("12")]
public string _12 { get; set; }

By using the JsonProperty attribute and specifying the corresponding JSON property names, you can ensure that the deserialization process correctly maps the JSON properties to the C# class properties