【问题标题】:Using JsonConvert.DeserializeObject to deserialize Json to a C# POCO class使用 JsonConvert.DeserializeObject 将 Json 反序列化为 C# POCO 类
【发布时间】:2012-06-22 23:37:22
【问题描述】:

这是我的简单User POCO 类:

/// <summary>
/// The User class represents a Coderwall User.
/// </summary>
public class User
{
    /// <summary>
    /// A User's username. eg: "sergiotapia, mrkibbles, matumbo"
    /// </summary>
    public string Username { get; set; }

    /// <summary>
    /// A User's name. eg: "Sergio Tapia, John Cosack, Lucy McMillan"
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// A User's location. eh: "Bolivia, USA, France, Italy"
    /// </summary>
    public string Location { get; set; }

    public int Endorsements { get; set; } //Todo.
    public string Team { get; set; } //Todo.

    /// <summary>
    /// A collection of the User's linked accounts.
    /// </summary>
    public List<Account> Accounts { get; set; }

    /// <summary>
    /// A collection of the User's awarded badges.
    /// </summary>
    public List<Badge> Badges { get; set; }

}

我用来将 JSON 响应反序列化为 User 对象的方法(这个实际的 JSON call is here):

private User LoadUserFromJson(string response)
{
    var outObject = JsonConvert.DeserializeObject<User>(response);
    return outObject;
}

这会引发异常:

无法反序列化当前 JSON 对象(例如 {"name":"value"}) 输入类型 'System.Collections.Generic.List`1[CoderwallDotNet.Api.Models.Account]' 因为该类型需要一个 JSON 数组(例如 [1,2,3])来反序列化 正确。

要修复此错误,请将 JSON 更改为 JSON 数组 (例如 [1,2,3])或更改反序列化类型,使其成为正常的 .NET 类型(例如,不是像整数这样的原始类型,也不是集合 可以从 JSON 反序列化的数组或列表等类型 目的。 JsonObjectAttribute 也可以添加到类型中来强制它 从 JSON 对象反序列化。路径“accounts.github”,第 1 行, 位置 129。

以前从未使用过这种 DeserializeObject 方法,我有点卡在这里。

我已确保 POCO 类中的属性名称与 JSON 响应中的名称相同。

我可以尝试将 JSON 反序列化到这个 POCO 类中吗?

【问题讨论】:

  • 看来我来晚了,但请看我的回答。使用JsonProperty 比编写JsonConverter 更容易(并且可读)
  • 那么您可能正在查看类似的内容。 stackoverflow.com/questions/25672338/…

标签: c# json serialization json.net poco


【解决方案1】:

这是一个工作示例。

关键点是:

  • Accounts 的声明
  • JsonProperty 属性的使用

.

using (WebClient wc = new WebClient())
{
    var json = wc.DownloadString("http://coderwall.com/mdeiters.json");
    var user = JsonConvert.DeserializeObject<User>(json);
}

-

public class User
{
    /// <summary>
    /// A User's username. eg: "sergiotapia, mrkibbles, matumbo"
    /// </summary>
    [JsonProperty("username")]
    public string Username { get; set; }

    /// <summary>
    /// A User's name. eg: "Sergio Tapia, John Cosack, Lucy McMillan"
    /// </summary>
    [JsonProperty("name")]
    public string Name { get; set; }

    /// <summary>
    /// A User's location. eh: "Bolivia, USA, France, Italy"
    /// </summary>
    [JsonProperty("location")]
    public string Location { get; set; }

    [JsonProperty("endorsements")]
    public int Endorsements { get; set; } //Todo.

    [JsonProperty("team")]
    public string Team { get; set; } //Todo.

    /// <summary>
    /// A collection of the User's linked accounts.
    /// </summary>
    [JsonProperty("accounts")]
    public Account Accounts { get; set; }

    /// <summary>
    /// A collection of the User's awarded badges.
    /// </summary>
    [JsonProperty("badges")]
    public List<Badge> Badges { get; set; }
}

public class Account
{
    public string github;
}

public class Badge
{
    [JsonProperty("name")]
    public string Name;
    [JsonProperty("description")]
    public string Description;
    [JsonProperty("created")]
    public string Created;
    [JsonProperty("badge")]
    public string BadgeUrl;
}

【讨论】:

  • 这导致代码更加简洁。我已经实现了一个自定义序列化器,但我更喜欢这种方法,因为它更精简。再次感谢!
  • 您是否忘记了帐户上的列表,还是我遗漏了什么?在我看来,原始问题将 Accounts 作为列表,但此解决方案将 Accounts 作为单个 Account 对象......不是数组,也不是列表。
  • @huntharo 看起来更改了 Accounts 声明——从 List&lt;Account&gt;Account——是因为示例 JSON(在问题中链接)有一个单独的 JSON 对象——而不是数组——对于accounts"accounts": {"github": "sergiotapia"}
  • 这是一个没有任何解释的有效答案吗?如果名称相同,我们为什么要使用 JsonProperty。如果返回的对象的名称类似于徽章而不是徽章,则使用 JsonProperty。不列出帐户也不是解决方案。因为帐户应该是列表,但在某些情况下会引发异常。
  • 但我们可以写成 [JsonProperty("Username")]
【解决方案2】:

将骆驼大小写的 JSON 字符串反序列化为帕斯卡大小写的 POCO 对象的另一种更简化的方法是使用 CamelCasePropertyNamesContractResolver

它是 Newtonsoft.Json.Serialization 命名空间的一部分。这种方法假定 JSON 对象和 POCO 之间的唯一区别在于属性名称的大小写。如果属性名称拼写不同,则需要使用 JsonProperty 属性来映射属性名称。

using Newtonsoft.Json; 
using Newtonsoft.Json.Serialization;

. . .

private User LoadUserFromJson(string response) 
{
    JsonSerializerSettings serSettings = new JsonSerializerSettings();
    serSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    User outObject = JsonConvert.DeserializeObject<User>(jsonValue, serSettings);

    return outObject; 
}

【讨论】:

    【解决方案3】:

    您可以创建一个JsonConverter。请参阅here 以获取与您的问题类似的示例。

    【讨论】:

    • 正是我希望的存在。我一定会看看这个。谢谢!
    • 没问题,很高兴我能帮上忙。
    【解决方案4】:

    accounts 属性定义如下:

    "accounts":{"github":"sergiotapia"}
    

    您的 POCO 声明如下:

    public List<Account> Accounts { get; set; }
    

    尝试使用这个 Json:

    "accounts":[{"github":"sergiotapia"}]
    

    项目数组(将被映射到列表)总是用方括号括起来。

    编辑:Account Poco 将是这样的:

    class Account {
        public string github { get; set; }
    }
    

    也许还有其他属性。

    编辑 2: 如果没有数组,请使用如下属性:

    public Account Accounts { get; set; }
    

    类似于我在第一次编辑中发布的示例类。

    【讨论】:

    • 只定义属性而不是列表。我正在编辑我的答案 (Edit2)
    【解决方案5】:

    按照已接受的答案,如果您有 JSON 文本示例,可以将其插入 this converter,选择您的选项并生成 C# 代码。

    如果您在运行时不知道类型,那么这个主题看起来很合适。

    dynamically deserialize json into any object passed in. c#

    【讨论】:

    • 2021 年 7 月更新 - 转换器仍然存在于网络上并且运行良好。
    【解决方案6】:
    to fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the
    deserialized type so that it is a normal .NET type (e.g. not a primitive type like
    integer, not a collection type like an array or List) that can be deserialized from a
    JSON object.`
    

    整个消息表明可以序列化为 List 对象,但输入必须是 JSON 列表。 这意味着您的 JSON 必须包含

    "accounts" : [{<AccountObjectData}, {<AccountObjectData>}...],
    

    其中 AccountObject 数据是 JSON,代表您的 Account 对象或 Badge 对象

    目前看来是

    "accounts":{"github":"sergiotapia"}
    

    accounts 是 JSON 对象(用大括号表示),而不是 JSON 对象数组(数组用括号表示),这正是您想要的。试试

    "accounts" : [{"github":"sergiotapia"}]
    

    【讨论】:

    • 实际的 JSON 在问题中。
    • 糟糕,现在编辑编辑:完成。很抱歉没有注意到这一点;略读并不总是最佳的;P
    【解决方案7】:

    这并不是我的想法。如果你有一个只能在运行时知道的泛型类型,你会怎么做?

    public MyDTO toObject() {
      try {
        var methodInfo = MethodBase.GetCurrentMethod();
        if (methodInfo.DeclaringType != null) {
          var fullName = methodInfo.DeclaringType.FullName + "." + this.dtoName;
          Type type = Type.GetType(fullName);
          if (type != null) {
            var obj = JsonConvert.DeserializeObject(payload);
          //var obj = JsonConvert.DeserializeObject<type.MemberType.GetType()>(payload);  // <--- type ?????
              ...
          }
        }
    
        // Example for java..   Convert this to C#
        return JSONUtil.fromJSON(payload, Class.forName(dtoName, false, getClass().getClassLoader()));
      } catch (Exception ex) {
        throw new ReflectInsightException(MethodBase.GetCurrentMethod().Name, ex);
      }
    }
    

    【讨论】:

      【解决方案8】:

      可能会迟到,但使用 QuickType 是最简单的方法:

      https://app.quicktype.io/

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      【解决方案9】:

      对于遇到此问题的任何人,我都没有正确看到 json 值。 https://jsonutils.com/ 在那里,您可以检查应生成的类,并在您读取代码中的 json 后仅返回其中一个类。

      例如,我需要一个 booklist 对象,所以我的代码应该只读取一个

      res = await response.Content.ReadAsAsync<BookList>();
      

      书单的样子

          public class BookList
          {
      
              [JsonProperty("data")]
              public IList<Datum> Data { get; set; }
           }
      

      并且在该列表中有比转换器命名为 Datum 的更小的书类(只是书)

          public class Datum
          {
      
              [JsonProperty("id")]
              public string Id { get; set; }
      
              [JsonProperty("isbn")]
              public string Isbn { get; set; }
          }
      

      再次,如果您有疑问https://jsonutils.com/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-09-24
        • 2019-07-15
        • 1970-01-01
        • 2021-06-02
        相关资源
        最近更新 更多