12

I have the following JSON string:

{
    "values": {
        "details": {
            "property1": "94",
            "property2": "47",
            "property3": "32",
            "property4": 1
        },
        count: 4
    }
}     

I am going to map this to the following model:

public class Details
{
    public string property1 { get; set; }
    public string property2 { get; set; }
    public string property3 { get; set; }
    public int property4 { get; set; }
}

public class Values
{
    public Details details { get; set; }
    public int count { get; set; }
}

public class RootObject
{
    public Values values { get; set; }
}

I want to be able to map the these property names to different names at runtime when deserializing this JSON string like this:

JsonConvert.DeserializeObject<RootObject>(jsonString);

For example, in the deserialization process, I want the deserialize the name of "property1" to "differen_property_name1" or "differen_property_name2" or "differen_property_name3". Because I am choosing the new name at runtime (the new name to which I will change the "property1" name to), I can't use the solution using JsonPropertyAttribute, as suggested here:

.NET NewtonSoft JSON deserialize map to a different property name

One of the answers of the above question (Jack's answer) uses inheritance of DefaultContractResolver but it doesn't seem to work in that case.

Update

Later on, I needed to serialize the object I got from the deserialization and map the properties to different property names, defined at runtime. I used the same method as Brian proposed to do the serialization:

I used the dictionary to map my new property names:

var map = new Dictionary<Type, Dictionary<string, string>>
{
    { 
        typeof(Details), 
        new Dictionary<string, string>
        {
            {"property1", "myNewPropertyName1"},
            {"property2", "myNewPropertyName2"},
            {"property3", "myNewPropertyName3"},
            {"property4", "myNewPropertyName4"}
        }
    }
};    

and then I used Brian's DynamicMappingResolver to serialize the object like this:

var settings = new JsonSerializerSettings
{
    ContractResolver = new DynamicMappingResolver(map)
};

var root = JsonConvert.SerializeObject(myObjectInstance, settings);            
Community
  • 1
  • 1
Avi K.
  • 1,734
  • 2
  • 18
  • 28
  • Because I want to decide in runtime which new name I want to give to the property and with JsonPropertyAttribute you have to use a "hard coded" name. – Avi K. Jun 29 '16 at 22:56
  • One way (though it could be ugly) would be to deserialize the JSON first, and then map the deserialized object to the object with the new names. There may be a more elegant way, though. – Tim Jun 29 '16 at 22:57
  • @Tim, how would you map the deserialized object to an object with the new names when the new names need to be defined at runtime? – Avi K. Jun 29 '16 at 23:04
  • How will the targets be defined? CodeDOM? Anonymous types? Will you just have many, many classes in the app that you map to? I can think of one halfway measure, but it might help to know what you are trying to do. – Ňɏssa Pøngjǣrdenlarp Jun 30 '16 at 00:42

4 Answers4

16

You could use a custom ContractResolver to do this. Basically it is the same idea as putting a [JsonProperty] attribute on each class member for which you want to map to a different JSON property name, except you do it programmatically via the resolver. You can pass a dictionary of your desired mappings to the resolver when setting it up just before deserializing.

Here is what the custom resolver code might look like:

class DynamicMappingResolver : DefaultContractResolver
{
    private Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap;

    public DynamicMappingResolver(Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap)
    {
        this.memberNameToJsonNameMap = memberNameToJsonNameMap;
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);
        Dictionary<string, string> dict;
        string jsonName;
        if (memberNameToJsonNameMap.TryGetValue(member.DeclaringType, out dict) && 
            dict.TryGetValue(member.Name, out jsonName))
        {
            prop.PropertyName = jsonName;
        }
        return prop;
    }
}

To use the resolver, first construct a Dictionary<Type, Dictionary<string, string>> containing your mappings. The outer dictionary's key is the the class type(s) whose properties you want to map; the inner dictionary is a mapping of the class property names to JSON property names. You only need to provide a mapping for the properties whose names don't already match the JSON.

So, for example, if your JSON looked like this (notice the changed names of the properties inside the details object)...

{
    "values": {
        "details": {
            "foo": "94",
            "bar": "47",
            "baz": "32",
            "quux": 1
        },
        count: 4
    }
}

...and you wanted to map it to the classes in your question, you would create the dictionary like this:

var map = new Dictionary<Type, Dictionary<string, string>>
{
    { 
        typeof(Details), 
        new Dictionary<string, string>
        {
            {"property1", "foo"},
            {"property2", "bar"},
            {"property3", "baz"},
            {"property4", "quux"}
        }
    }
};

The last step is to set up the serializer settings with a new resolver instance, giving it the mapping dictionary you just constructed, and then pass the settings to JsonConvert.DeserializeObject().

var settings = new JsonSerializerSettings
{
    ContractResolver = new DynamicMappingResolver(map)
};

var root = JsonConvert.DeserializeObject<RootObject>(json, settings);

Here is a demo: https://dotnetfiddle.net/ULkB0J

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
3

If you don't want to use a custom ContractResolver to do this. Use [JsonProperty("")] to look for different variations of the property name and return with another property like this:

public class Details
{
    private string _property1;
    private string _property2;

    [JsonProperty("property1")]
    public string prop1 {get;set;}

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

    public string getProperty1 
    {
        get {_property1=prop1??foo;return _property1;}
        set{prop1=value;foo=value;}
    }

    [JsonProperty("property2")]
    public string prop2 {get;set;}

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

    public string getProperty2
    {
        get {_property2=prop2??bar;return _property2;}
        set {prop2=value;bar=value;}
    }
}

Demo here: https://dotnetfiddle.net/V17igc

ismael.
  • 418
  • 2
  • 7
  • This is EXACTLY what I have searched! I work with VB.NET and have to access various web services that have DEFINED fields that are RESERVED words in VB.NET. So I can generate a vb.net object from Json, but have to change the fieldnames. If I do that, the deserialization principle works, but the changed field names are empty. For VB.NET users: just add in your object definition before the changed field name (in my case Public Property ende As DateTime. As "end" is a reserved word, I had to change the name in my definition to endE. This works like a charm. Thanks @ismael – FredyWenger Apr 06 '23 at 13:03
1

Why do this in one step? Why not deserialize into your standard object and then map them over dynamically using Automapper?

something like:

Mapper.Initialize(c =>
{
    c.ReplaceMemberName("property1 ", "differen_property_name1");
});
Ian Mercer
  • 38,490
  • 8
  • 97
  • 133
0

I don't believe that JSON.net has any support for what you are looking for. You should instead deserialize the JSON into JObject if you don't know the format or some generic one if you do (for instance if the JSON always says property1 you can use a generic object to represent it).

Once you have your generic object then next you need to translate the fields. Any that aren't changeable can be done directly, but for anything else you will need to use Reflection.

Basically it involves getting the type (typeof(Details) or obj.GetType()) and then searching for the Property you want to update. Finally you should be able to find the setter method and call it supplying the original value out of your generic object.

Guvante
  • 18,775
  • 1
  • 33
  • 64