Mohit S
Mohit S

Reputation: 14024

Deseralize JSON with same property different names

The JSON looks like this

{
    "123": {
        "Type": "IN",
        "OUTAgentMACID": "00-14-22-01-23-45",
        "PlateNumber": {
            "Image": "/poll/data/date0/img.png",
            "Number": "ABC1234",
            "TimeStamp": 5901291
        }
    },
    "124": {
        "Type": "OUT",
        "OUTAgentMACID": "00-14-22-01-31-45",
        "PlateNumber": {
            "Image": "/poll/data/date0/img.png",
            "Number": "ABC1234",
            "TimeStamp": 5991291
        }
    },
    "125": {
        "Type": "IN",
        "INAgentMACID": "00-14-22-01-63-45",
        "PlateNumber": {
            "Image": "/poll/data/date1/img.png",
            "Number": "ABC1234",
            "TimeStamp": 6001239
        }
    }
}

The probable class structure is

public class PlateNumber
{
    public string Image { get; set; }
    public string Number { get; set; }
    public int TimeStamp { get; set; }
}
public class Activity
{
    public string Type { get; set; }
    public string AgentMACID { get; set; }
    public PlateNumber PlateNumber { get; set; }
}
public class SessionActivity
{
    public Dictionary<int, Activity> Activities { get; set; }
}

Helper looks like this

public class helpers : DefaultContractResolver
{
    private Dictionary<string, string> PropertyMappings { get; set; }

    public helpers()
    {
        PropertyMappings = new Dictionary<string, string>
        {
            {"INAgentMACID", "AgentMACID"},
            {"OUTAgentMACID", "AgentMACID"},
        };
    }
    protected override string ResolvePropertyName(string propertyName)
    {
        string resolvedName = null;
        var resolved = this.PropertyMappings.TryGetValue(propertyName, out resolvedName);
        return (resolved) ? resolvedName : base.ResolvePropertyName(propertyName);
    }
}

Now when I try to deserializing it like this

        var settings = new JsonSerializerSettings();
        settings.ContractResolver = new helpers();
        var activities = JsonConvert.DeserializeObject<SessionActivity>("Some.json"), settings);

the activities is null.

The Problem is AgentMACID Since the JSON have either OUTAgentMACID or INAgentMACID depending on the Type

Please help me design the class for this JSON.

Upvotes: 1

Views: 491

Answers (2)

B&#233;ranger
B&#233;ranger

Reputation: 673

1 - Deserialize a Dictionary from JSON

Based on your json object :

{
    "123": {
        "Type": "IN",
        "OUTAgentMACID": "00-14-22-01-23-45",
        "PlateNumber": {
            "Image": "/poll/data/date0/img.png",
            "Number": "ABC1234",
            "TimeStamp": 5901291
        }
    },
    "124": {
        "Type": "OUT",
        "OUTAgentMACID": "00-14-22-01-31-45",
        "PlateNumber": {
            "Image": "/poll/data/date0/img.png",
            "Number": "ABC1234",
            "TimeStamp": 5991291
        }
    },
    "125": {
        "Type": "IN",
        "INAgentMACID": "00-14-22-01-63-45",
        "PlateNumber": {
            "Image": "/poll/data/date1/img.png",
            "Number": "ABC1234",
            "TimeStamp": 6001239
        }
    }
}

You can deserialize a dictionary using :

var activities = JsonConvert.DeserializeObject<Dictionary<int, Activity>>("Some.json"), settings);

2.1 - Manage multiple json property names in one C# property

For your second issue you can define your Activity class like this :

public class Activity
{
    public string Type { get; set; }
    public string AgentMACID { get; set; }
    // Optional: you can rename your property with this property attribute
    // [JsonProperty("INAgentMACID")]
    public string INAgentMACID { set { AgentMACID = value; } }
    // Optional: you can rename your property with this property attribute
    // [JsonProperty("OUTAgentMACID")]
    public string OUTAgentMACID { set { AgentMACID = value; } }
    public PlateNumber PlateNumber { get; set; }
}

2.2 - You can also inherit DefaultContractResolver as you were doing :

public class RenamePropertySerializerContractResolver : DefaultContractResolver
{
    private readonly Dictionary<Type, HashSet<string>> _ignores;
    private readonly Dictionary<Type, Dictionary<string, string>> _renames;

    public RenamePropertySerializerContractResolver()
    {
        _renames = new Dictionary<Type, Dictionary<string, string>>();
    }

    public void RenameProperty(Type type, string propertyName, string newJsonPropertyName)
    {
        if (!_renames.ContainsKey(type))
            _renames[type] = new Dictionary<string, string>();

        _renames[type][propertyName] = newJsonPropertyName;
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        if (IsRenamed(property.DeclaringType, property.PropertyName, out var newJsonPropertyName))
            property.PropertyName = newJsonPropertyName;

        return property;
    }

    private bool IsRenamed(Type type, string jsonPropertyName, out string newJsonPropertyName)
    {
        Dictionary<string, string> renames;

        if (!_renames.TryGetValue(type, out renames) || !renames.TryGetValue(jsonPropertyName, out newJsonPropertyName))
        {
            newJsonPropertyName = null;
            return false;
        }

        return true;
    }
}

And finally use you new ContractResolver :

var jsonResolver = new RenamePropertySerializerContractResolver();
jsonResolver.RenameProperty(typeof(Activity), "INAgentMACID", "AgentMACID");
jsonResolver.RenameProperty(typeof(Activity), "OUTAgentMACID", "AgentMACID");

var serializerSettings = new JsonSerializerSettings();
serializerSettings.ContractResolver = jsonResolver;

var activities = JsonConvert.DeserializeObject<IDictionary<int, Activity>>(json, serializerSettings);

sources : https://blog.rsuter.com/advanced-newtonsoft-json-dynamically-rename-or-ignore-properties-without-changing-the-serialized-class/

Upvotes: 1

Malior
Malior

Reputation: 1341

I think it's not directly possible that you have one Property, which represents two Properties of the json- (as I've read here Make JsonPropertyAttribute allow multiple usages on same property)

What I undertsand from this post, you would be forced to have another property, which just "forwards" the value to the one you want.

example:

    public class Activity
    {
        public string Type { get; set; }
        public string AgentMACID { get; set; }
        private string AgentMACID2 { set { AgentMACID = value; } } // used to map the other field of json
        public PlateNumber PlateNumber { get; set; }
    }

in the Contract Resolver you have to mape vias versa as you did. With the second field I added, it might looks as this:

   PropertyMappings = new Dictionary<string, string>
   {
       {"AgentMACID","OUTAgentMACID"},
       {"AgentMACID2","INAgentMACID"}
   };

And deserialize by this:

var activities = JsonConvert.DeserializeObject<Dictionary<int, Activity>>("json content", settings);

Upvotes: 1

Related Questions