1

I’m receiving JSON from a service which is a set of search results. Each object in the “results” array could be one of two types of objects (cat/dog I my example). Here’s some example JSON:

{
  "total": 2,
  "results": [
    {
      "type": "cat",
      "id": "1",
      "name": "Kitty"
    },
    {
      "type": "dog",
      "id": "2",
      "name": "Rover",
      "cat": {
        "id": "3",
        "name": "Missy"
      }
    }
  ]
} 

I’m deserializing the JSON root object to “SearchResult”. Each item in the "results" is a “SearchResultItem”.

Here’s what my models like for the items:

public class Cat : SearchResultItem
{
    public string id {get; set;}
    public string name {get; set;}  
}

public class Dog : SearchResultItem
{
    public string id {get; set;}
    public string name {get; set;}
    public Cat {get; set;}  
}

I’ve created a custom converter to convert to the correct type for each object:

public class SearchResultConverter : Newtonsoft.Json.Converters.CustomCreationConverter<SearchResultItem>
    {
        public override SearchResultItem Create(Type objectType)
        {
            throw new NotImplementedException();
        }

        public SearchResultItem Create(Type objectType, JObject jObject)
        {
            var type = (string)jObject.Property("type");
            switch (type)
            {
                case "cat":
                    return new Cat();
                case "dog":
                    return new Dog();
            }

            return null;

            //throw new ApplicationException(String.Format("The given vehicle type {0} is not supported!", type));
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            // Load JObject from stream
            JObject jObject = JObject.Load(reader);

// Create target object based on JObject
            var target = Create(objectType, jObject);

// Populate the object properties
            serializer.Populate(jObject.CreateReader(), target);

            return target;
        }
    }

The issue I’m having is that Dog happens to have a child item which is a Cat. My converter is catching this Cat object and trying to convert it. Only the results object have a “type” property, any instance of a child Cat or Dog won’t have this “type” property. The absence of this property is causing an Null Reference exception.

How do I restrict the converter to only the top level items in the “results” array?

1 Answer 1

2

Probably the easiest way to do this is to override JsonConverter.CanConvert and have it return true when typeof(SearchResultItem) == objectType, rather than when it is assignable from objectType:

public class SearchResultConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(SearchResultItem);
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public SearchResultItem Create(Type objectType, JObject jObject)
    {
        // As before
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // As before
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException("SearchResultConverter should only be used while deserializing.");
    }
}

Now the converter will be called only when attempting to deserialize an instance of SearchResultItem. For members already declared to be of type Dog or Cat its ReadJson() method will not get called.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.