Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Json.NET deserialize or serialize json string and map properties to different property names defined at runtime

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);            
like image 775
Avi K. Avatar asked Jun 29 '16 22:06

Avi K.


People also ask

What is the difference between serialize and deserialize JSON?

JSON is a format that encodes objects in a string. Serialization means to convert an object into that string, and deserialization is its inverse operation (convert string -> object).

What is JSON serialization and deserialization in C#?

Json structure is made up with {}, [], comma, colon and double quotation marks and it includes the following data types: Object, Number, Boolean, String, and Array. Serialize means convert an object instance to an XML document. Deserialize means convert an XML document into an object instance.

What is JSON serialization and deserialization in Java?

Serialization is a mechanism of converting the state of an object into a byte stream. Deserialization is the reverse process where the byte stream is used to recreate the actual Java object in memory. This mechanism is used to persist the object. The byte stream created is platform independent.


1 Answers

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

like image 178
Brian Rogers Avatar answered Oct 13 '22 10:10

Brian Rogers