如何在使用JsonConvert.DeserializeObject时避免需要JSON对象的根类 &



给定这个JSON,

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

我可以让DeserializeObject工作,如果我定义模型为这个

    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; }
    }

并使用这个调用:

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

我调用的第三方API具有返回类似JSON响应的所有方法,因为它具有包含特定类型的单个对象的头对象,例如:

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

需要一个像这样的模型:

    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; }
    }

我不想为每个JSON响应定义不同的Root类。有办法避免这种情况吗?

编辑14/11。

另一个JSON响应如下:

{
  "provider": {
    "TOTAL": {
      "count": 0
    }
  }
}

还是一个"空"根对象,包含我需要的特定对象。

zaitsman 在他的评论中指出,通过输入DeserializeObject调用来使用<Dictionary, T>,其中T是我所追求的实际对象(例如TokenUserProvider),它绕过了对根对象的需要。

编辑15/11。

再举一个例子;

{
    "provider": [
        {
            "accountType": [],
            "loginHelp": "string",
            "baseUrl": "string",
            "loginUrl": "string",
            "name": "string",
            "id": 0,
            "lastModified": "string",
            "status": "Supported"
        }
    ]
}

在这种情况下,要反序列化的 c# 代码看起来像这样:

providers = JsonConvert.DeserializeObject<Dictionary<string, List<Provider>>>(apiResponse)["provider"];

使用Dictionary<string,T>消除了定义根类的需要,并且T必须定义为List<T>,因为内容是一个数组。

有很多方法可以达到这个目的。

其中之一是:

JsonConvert.DeserializeObject<Dictionary<string, User>>()["user"] 

这将给你一个Dictionary<string, User>,所以你可以使用任何其他合适的类型。

我认为定义Root对象是更好的方法,因为它使预期的JSON结构明确定义,但如果你想,你可以使用Newtonsoft的LINQ API通过JObject (docs):

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

类似的可以通过JsonNode API为System.Text.Json(可从。net 6开始)完成。

解决这个问题的一种方法是使用包装器定义所有可能的"内容";名称作为属性。然后有一个属性返回反序列化后填充的值。

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;
}

那么,只要有一个像这样的反序列化方法

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

还有一种更动态的方法,如果你需要检索太多不同的内容,但这将需要覆盖一些更多的反射,并深入到反序列化过程。

最新更新