Chris
Chris

Reputation: 1058

Dynamically deserializing to a property in RestSharp

I am playing with the Harvest API and I'm trying to automatically map the entities as easy as possible, unfortunately when I do a request like GET /projects it generates a result like so:

[{
    project: {
        name: "Test"
    }
},
{
    project: {
        name: "Test 2"
}]

In RestSharp, I can't directly do this:

client.Execute<List<Project>>(request)

Because it is going to look for a property called Project. So I have to make another class that has that property, and call it like this:

client.Execute<List<ProjectContainer>>(request)

I don't want to make a 'container' class for every entity, so I thought I found a clever solution to make one class I can use on all:

public class ListContainer<T> where T : IHarvestEntity
{
    public T Item { get; set; }
}

But, of course, the deserializer has no idea it needs to map the entity name (or "Project") to the property Item. In the restsharp documentation I found that I could use [DeserializeAs(Name = "CustomProperty")] to tell the deserializer which field to map to this property. However, attributes do only allow constants, which means I can't do:

[DeserializeAs(Name = typeof(T).FullName)]
public T Item { get; set; }

Does anyone know a clever solution to this? So i don't have to create 10 different container classes?

Upvotes: 26

Views: 12537

Answers (2)

user4864425
user4864425

Reputation:

I suggest you use the XPath equivalent for Json. With Json.NET you can parse the string and create a dynamic object.

With SelectToken you can query values, or using Linq.

The code looks something like this (I did not test it):

// execute the request
RestResponse response = client.Execute(request);
var content = response.Content; // raw content as string

JObject o = JObject.Parse(content);

IList<string> projectNames = o.SelectToken("project").Select(s => (string)s.name).ToList();

You can code the paths or configure the paths anyway you like.

--- Edit ---

Here's an example that I tested, converting the json string to a list of projects.

var projects = JArray.Parse(response.Content).Select(r => new Project(r["project"]["name"].Value<string>())).ToList();

Upvotes: 10

Mikko Viitala
Mikko Viitala

Reputation: 8404

To keep it really simple, you can use List<dynamic> and access the property/properties by name with a one-liner.

var names = client.Execute<List<dynamic>>(request).Data.Select(
              item => item["project"]["name"]).ToList(); // list of names

If this is not sufficient, then you could improvise your own mapper and extract a collection of e.g. Project instances:

var projects = client.Execute<List<dynamic>>(request).Data.Select(
                 item => Map<Project>(item)).ToList(); // list of Project instances

where Map method could be something like

public T Map<T>(dynamic item) where T : class
{
    // inline for clarity
    var mappings = new Dictionary<Type,Func<dynamic,object>>
        {
            { typeof(Project), map => new Project(map["project"]["name"]) }
        };

    return (T)mappings[typeof(T)].Invoke(item);
}

given Project is defined as

public class Project
{
    public Project(string name)
    {
        Name = name;
    }
    public string Name { get; set; }
}

Upvotes: 5

Related Questions