0

I have this:

public class PagedResults<T>
{
    public int a { get; set; }

    public string b { get; set; }

    public int c { get; set; }

   ....
   ....
   public IEnumerable<T> Results { get; set; }
}

When this gets serialised into JSON, I get:

{"a":1, "b":"string", "c":2, ....,"Results":[{...},{....},....] }

but I want:

when <T> = TypeA:

{"a":1, "b":"string", "c":2, ...,"TypeA":[{...},{....},....] }

when <T> = TypeB;

 {"a":1, "b":"string", "c":2, ...,"TypeB":[{...},{....},....] }

Tried this with help from here: How to get the name of <T> from generic type and pass it into JsonProperty()?

 public class PagedResults<T> : Newtonsoft.Json.Linq.JObject
 {
    private static string TypeName = (typeof(T)).Name;

    public int a { get; set; }

    public string b { get; set; }

    public int c { get; set; }
    ....

    private IEnumerable<T> _Results { get; set; }

    public IEnumerable<T> Results
    {
        get { return _Results; }
        set
        {
            _Results = value;
            this[TypeName] = Newtonsoft.Json.Linq.JToken.FromObject(_Results);
        }
    }

 }

Now, I get the Results array with specific class name, but all the other members (i.e., a, b, c are lost).

I now get when <T> = TypeA:

{"TypeA":[{...},{....},....] }

when <T> = TypeB;

 {"TypeB":[{...},{....},....] }

but I want:

when <T> = TypeA:

{"a":1, "b":"string", "c":2, ...,"TypeA":[{...},{....},....] }

when <T> = TypeB;

 {"a":1, "b":"string", "c":2, ...,"TypeB":[{...},{....},....] }

Any help is highly appreciated.

2
  • In what context are you using this? Are you returning the PagedResults object from an API and then trying to deserialize the results in a client? Commented Aug 9, 2017 at 9:30
  • It's in the server side API itself. Our client is going to consume it and hence we need to serialize it exactly like how they want it. Commented Aug 9, 2017 at 9:51

1 Answer 1

1

I've used almost this exact class before and left it as

public class PagedResults<T>
{
    public int a { get; set; }

    public string b { get; set; }

    public int c { get; set; }

    ....
    ....
    public IEnumerable<T> Results { get; set; }
}

I wouldn't change the name of the Results property depending on the type of object inside it. Results are results. And I wouldn't return a different data structure from the same endpoint. Each endpoint really should only return a predefined data structure so that you have a contract between you and the client. If you start changing the type of data returned then it's to easy for you to create a breaking change.

For instance if I have a url of /api/house I know that the objects inside the results property are of type House. If I have a url of /api/car then my client knows that the objects inside of results are of type Car etc.

UPDATE

ok so he's an answer that'll actually do what you need! I'm assuming you're using Newtonsoft.Json.

First create a JsonConverter

public class PagedResultsConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(PagedResults<>);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Type type = value.GetType();

        string dataPropertyName = "Results";
        if (type.GetGenericArguments().Any())
        {
            var genericType = type.GetGenericArguments()[0];
            dataPropertyName = genericType.Name;
        }

        JObject jo = new JObject();
        if (type.GetProperty("Results")?.GetValue(value) != null)
        {
            jo.Add(dataPropertyName, JArray.FromObject(type.GetProperty("Results")?.GetValue(value)));
        }else
        {
            jo.Add(dataPropertyName, null);
        }

        foreach (PropertyInfo prop in type.GetProperties().Where(p => !p.Name.StartsWith("Results")))
        {
            jo.Add(prop.Name, new JValue(prop.GetValue(value)));
        }
        jo.WriteTo(writer);
    }

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Then decorate you PagedResults class like:

[JsonConverter(typeof(PagedResultsConverter))]
public class PagedResults<T>
{
    public IEnumerable<T> Results { get; set; }
}

Then sit back and watch is go! This should just start working. Notice I've removed the inheritance from Newtonsoft.Json.Linq.JObject as it's not needed and you don't need the _Results backing field either.

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

4 Comments

Actually these are 4 different end-points. As all of them were fairly similar I encapsulated all of them in one class. Everything is perfect except for this last piece. Hope you are clear about the rationale behind this requirement.
I've no idea about the rationale or requirement behind this! Lol. Do you want to update the question with the reasons behind this decision? Personally I can't see what's wrong with using the property name Results! Also do you want to expose internal class names externally? Sounds like tight coupling.... which is bad!
Thanks very much for your code. It's put me on the right track now. Currently I'm getting null value at type.GetProperty("Results"). Will debug and see what's going wrong.
I've updated again with the bug fix. It is because Results was null. The bug fix though doesn't return the property if its null, if you want to handle different that it's only a small change

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.