Search code examples
c#jsonrest.net-6.0json-deserialization

Deserialize JSON from request body with dynamic keys


I have a Rest API that accepts a JSON model consisting of its fields are dynamic GUIDs.

I tried multiple models and in all of them, I got an error.

This is the JSON:

{
   "meta_data": {
       "name": "myname"
   },
   "1122413f-2887-4789-a112-31d003f2703a": {
       "ipadd": "1.1.1.1",
       "host": "host"
   },
   "4432313f-2887-4789-a222-31d41231233a": {
       "ipadd": "1.2.1.1",
       "host": "h3st"
   }
}

and this is the model:

public class RootObject
{
    [JsonProperty("meta_data")]
    public Metadata Metadata { get; set; }

    public Dictionary<Guid, DeviceInfo> Devices { get; set; }
}

public class DeviceInfo
{
    public string IpAdd { get; set; }
    public string Host { get; set; }
}

and the controller:

[FromBody] RootObject jsonObj

The jsonObj only contains the Metadata and the dynamic field is always empty.

What can I do to overcome this?

It accepts when I send it under Devices:

"Devices": { 
     "123...."
}

but this is not what I need.


Solution

  • If you are using .NET Core 3 and above, you can work with JsonExtensionDataAttribute from System.Text.Json.Serialization.

    You should add a new property for receiving the additional JSON element as Dictionary<string, JsonElement>.

    The dictionary's TKey value must be String, and TValue must be JsonElement or Object.

    And assign it to the Devices property with some data conversions.

    using System.Text.Json.Serialization;
    using System.Linq;
    
    public class RootObject
    {
        [JsonPropertyName("meta_data")]
        public Metadata Metadata { get; set; }
    
        [JsonExtensionData]
        public Dictionary<string, JsonElement> Extension { get; set; }
        
        [JsonIgnore]
        public Dictionary<Guid, DeviceInfo> Devices 
        { 
            get
            {
                if (Extension == null)
                    return null;
    
                return Extension.ToDictionary(x => Guid.Parse(x.Key), v => JsonSerializer.Deserialize<DeviceInfo>(JsonSerializer.Serialize(v.Value)));
            }
        }
    }
    
    public class DeviceInfo
    {
        [JsonPropertyName("ipadd")]
        public string IpAdd { get; set; }
        
        [JsonPropertyName("host")]
        public string Host { get; set; }
    }
    

    enter image description here


    If you are using Newtonsoft.Json as the default serialization library/tool in your API/MVC application:

    services.AddControllers()
        .AddNewtonsoftJson();
    

    You need to use the attributes/deserialization ways from Newtonsoft.Json.

    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    
    public class RootObject
    {
        [JsonProperty("meta_data")]
        public Metadata Metadata { get; set; }
    
        [JsonExtensionData]
        public Dictionary<string, JToken> Extension { get; set; }
    
        [JsonIgnore]
        public Dictionary<Guid, DeviceInfo> Devices
        {
            get
            {
                if (Extension == null)
                    return null;
    
                return Extension.ToDictionary(x => Guid.Parse(x.Key), 
                    v => JObject.FromObject(v.Value)
                        .ToObject<DeviceInfo>());
            }
        }
    }
    
    public class DeviceInfo
    {
        [JsonProperty("ipadd")]
        public string IpAdd { get; set; }
    
        public string Host { get; set; }
    }
    

    enter image description here