Search code examples
c#jsonjson.netjson-deserialization

CustomConverter doesn't works to get names of objects


I receive a json like this :

{
    "Bryan": {
        "age": 25,
        "city": "Miami"
    },
    "Jeff": {
        "age": 24,
        "city": "Tokyo"
    }
}

I would like deserialize to get the name of the persons in a list<String>. So I have a CustomJsonConverter:

public class JsonObjectsToListConverter : JsonConverter
{

    public JsonObjectsToListConverter()
    {

    }

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(HashSet<String>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken jtoken = JToken.Load(reader);
        JObject jObjectCast = jtoken.Value<JObject>();

        List<String> listPers = (from prop in jObjectCast.Properties()
                                select prop.Name).ToList();
        return listPers;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

and a Person class with List member :

public class Persons
    {
        [JsonConverter(typeof(JsonObjectsToListConverter))]
        public List<String> listPers { get; set; }
    }

The problem is that the CustomConverter return null except if I add a parent object into json like :

{"listP":
    {
    "Bryan":{
....

and [JsonProperty("listP")] in Persons Class.

I don't understand what happens during deserialization and why it doesn't works properly without PropertyName Attribute.


Solution

  • The description below is very simplified, but pretty close to the actual process of deserialization.

    Once you supply the JsonConvert.DeserializeObject method with a string and the target type, it starts to compare the structure of the type you specified with the structure of the string. It looks for the public properties of the supplied types and searches the string for the same property occurrences. It doesn't know that the json array you specify in string should go into the property named "listP" until you link between json and the type using name for the json array.

    Now, you don't necessary have to specify the "listP" property name with the JsonProperty attribute. It's enough to just the json array the same name the class property has: "listPers".

    If you'd like to simplify the process of deserialization, however, without additional overhead in the json itself and unnecessary properties decoration, it would be easier to write a code similar to the below:

    public class Persons
    {
        public List<String> ListPers { get; set; }
    }
    
    public class JsonObjectsToPersonsConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(Persons));
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            Persons value = new Persons();
            JToken jtoken = JToken.Load(reader);
            JObject jObjectCast = jtoken.Value<JObject>();
    
            List<String> listPers = (from prop in jObjectCast.Properties()
                                     select prop.Name).ToList();
            return new Persons { ListPers = listPers};
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    And deserialize the data like this:

    var persons = JsonConvert.DeserializeObject<Persons>(
        @"{
            'Bryan': {
                'age': 25,
                'city': 'Miami'
            },
            'Jeff': {
                'age': 24,
                'city': 'Tokyo'
            }
        }", 
        new JsonObjectsToPersonsConverter());