Home > Enterprise >  How to avoid needing a root class for a JSON object when using JsonConvert.DeserializeObject
How to avoid needing a root class for a JSON object when using JsonConvert.DeserializeObject

Time:11-07

Given this JSON,

{
    "token": {
        "accessToken": "scrciFyGuLAQn6XgKkaBWOxdZA1",
        "issuedAt": "2022-11-06T22:54:27Z",
        "expiresIn": 1799
    }
}

I can get the DeserializeObject to work if I define the model as this

    public class Root
    {
        public Token Token { get; set; }
    }

    public class Token
    {
        public string AccessToken { get; set; }
        public DateTime IssuedAt { get; set; }
        public int ExpiresIn { get; set; }
    }

And use this call:

Root myRoot = JsonConvert.DeserializeObject<Root>(apiResponse);

The third-party API I am calling has all methods returning a similar JSON response, in that it has a header object containing a single object of a specific type, such as:

{
  "user": {
    "preferences": {},
    "address": {},
    "name": {},
    "email": "string",
    "segmentName": "string"
  }
}

which requires a model looking like this:

    public class Address
    {
    }

    public class Name
    {
    }

    public class Preferences
    {
    }

    public class Root
    {
        public User user { get; set; }
    }

    public class User
    {
        public Preferences preferences { get; set; }
        public Address address { get; set; }
        public Name name { get; set; }
        public string email { get; set; }
        public string segmentName { get; set; }
    }

I do not want to be having to define a different Root class for every one of the JSON responses. Is there a way to avoid this?

CodePudding user response:

I would argue that defining Root objects is better approach cause it makes expected json structure defined explicitly but if you want you can use Newtonsoft's LINQ API via JObject (docs):

var jObject = JObject.Parse(json);
var user = jObject["user"].ToObject<User>();

Similar can be done via JsonNode API for System.Text.Json (available starting .NET 6).

CodePudding user response:

One way to solve it by having a wrapper defining all possible "content" names as properties. Then having one property to return the value which is populated after de-serialization.

public class Response<T>
{
   public User User { get; set; }
   public Token Token { get; set; }
   public Token AuthToken { get; set; }
   public XYZ XYZ { get; set; }

   public T Content => GetType().GetProperties().Where(p => p.Name != nameof(Content)).Select(p => p.GetValue(this)).Single(v => v != null) as T;
}

Then, just having a method for deserializing like this

public T DeserializeResponse<T>(string json)
{
  return Deserialize<Response<T>>(json).Content;
}

There is also a more dynamic way possible, if there are too many different contents you need to retrieve, but that would require overriding some more reflection and going deeper into deserialization process.

  • Related