Search code examples
json-deserialization

Deserializing a badly formed result


I'm in the process of making a RESTful client (consumption side) and so far I have all my requests and responses fairly consistent. I have something like this:

public class RESTClient
{
    public T Execute<T>(RESTRequest request);
}

My RESTRequest contains things like the base URI, authentication, resource URI and any object(s) to serialize. So far so good, until I came across a situation where the result can't be deserialized to a concrete type. This result is a collection of, for example, SomeClass with (at the same level) the number of results in the collection. So let's say SomeClass looks like this:

public class SomeClass
{
    public int Id { get; set; }
    public string Name { get; set; }
}

If I request the specific resource, then it deserializes perfectly fine - but I can't do that unless I have the ID, so for that I need to get it from a list that when requested returns JSON that looks like this:

{
    "-1":
    {
        "id": -1,
        "name": "There is *always* a -1 in the collection as default"
    },
    "3":
    {
        "id": 3,
        "name": "This would be one I've added later"
    },
    "recordsCount": 2
}

So as you can see the ID of the object is the name of the object... and at the same level we have recordsCount which is just an int.

First Attempt

My first attempt was (as I'm working with JSON.Net) was to go through the result as JObject first, check if the name of the current object matched my selector and then do another request for SomeClass by ID to get the nicely deserialized concrete class. Basically trawling through the JSON until I find something I can deserialize to my target class.

Second Attempt

After refactoring the first attempt (and decoupling my serializer from JSON.Net) I decided to pass in a custom serializer (say SomeClassSerializer) via the RESTRequest itself, so in most cases I can use the basic JSON <--> Concrete version, and when I need to I can override it with this specific one.

My actual questions are:

  1. Am I alone in thinking this particular JSON response is just a horrible mess? What were they thinking?!
  2. Is there a better way of handling this (either in JSON.Net or not)... because the names of the objects are integers (and just so happen to be the ID) I can't exactly return them as IEnumerable<TWeirdType>.

Cheers for any help.


Solution

  • It was a lot simpler (and more obvious) than I initially thought... I wanted a solution that would keep my RESTClient and JSON.Net loosely coupled, and I can do just that with JsonConverter (which is part of JSON.Net but the logic is outside of my client and all happens through type).

    What I've got instead is:

    public interface IMySerializer
    {
        string Serialize<T>(T target);
        T Deserialize<T>(string json);
    }
    

    Which is used by my RESTClient with a concrete type of BasicJsonSerializer (which has JSON.Net's serializer inside it) and this custom converter (passed through JsonSerializerSettings).

    public IEnumerableSomeClassConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType) {}
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            // Handle the weirdness here and return my IEnumerable
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {}
    }
    

    Handling it very well.