【问题标题】:JsonConvert.DeserializeObject not converting a response object from JSON as expectedJsonConvert.DeserializeObject 未按预期从 JSON 转换响应对象
【发布时间】:2019-10-31 04:39:29
【问题描述】:

我正在为 C#.NET 创建一个 ActiveCampaign V1.1 包装器,并创建一个使用 (https://www.activecampaign.com/api/example.php?call=list_list) 列出联系人列表的类

JsonConvert.DeserializeObject<BasicListResponse>(JSON) 之后返回的响应对象确实填充了非列表属性,但 JSON 的列表部分未转换。

我尝试了以下BasicListResponse 实现:

public class Result
{
    [JsonProperty("result_code")]
    public int ResultCode { get; set; }

    [JsonProperty("result_message")]
    public string ResultMessage { get; set; }

    [JsonProperty("result_output")]
    public string ResultOutput { get; set; }
}

public class BasicListResponse : Result
{      
    public Dictionary<string, BasicList> list { get; set; }
}

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

    [Required]
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("cdate")]
    public DateTime CreatedOn { get; set; }

    [JsonProperty("private")]
    public bool Private { get; set; }

    [JsonProperty("userid")]
    public string UserId { get; set; }

    [JsonProperty("subscriber_count")]
    public int SubscriberCount { get; set; }
}

我也尝试过使用json2csharp 或 VS > 选择性粘贴,但输出有编号的类而不是对象列表。

JSON 响应如下:

{
    "0": {
        "id": "1",
        "name": "xxxxx xxxxxx xxxxxxx",
        "cdate": "2019-10-27 22:43:23",
        "private": "0",
        "userid": "1",
        "subscriber_count": 1
    },
    "1": {
        "id": "2",
        "name": "yyyyy yyyyy yyyyy",
        "cdate": "2019-10-27 22:44:03",
        "private": "0",
        "userid": "1",
        "subscriber_count": 0
    },
    "result_code": 1,
    "result_message": "Success: Something is returned",
    "result_output": "json"
}

我在包装器中提取了Dictionary&lt;T,T&gt;,但列表始终为 NULL。

如果我重新序列化BasicListResponse,结果如下

{ "list": null, "result_code": 1, "result_message": "Success: Something is returned", "result_output": "json" }

我希望 BasicListResponse 用作根对象,其中包含具有三个字符串 result_* 属性的列表/数组。

感谢您为解决此问题提供的任何帮助。

【问题讨论】:

标签: c# .net json json.net deserialization


【解决方案1】:

您的 JSON 基本上代表了一个由 string-BasicList 对组成的字典,并与其他一些关于整体结果的属性混合在一起。这种格式使其更难以使用。 (如果字典数据在 JSON 的子对象中会更好。)

处理这种情况的最简单方法是利用 Json.Net 的“Extension Data”功能。

这是您需要做的:

  1. 在您的BasicListResponse 类中创建一个私有Dictionary&lt;string, JToken&gt; 属性并使用[JsonExtensionData] 属性对其进行标记。这将在反序列化期间捕获字典数据。
  2. list 属性更改为List&lt;BasicList&gt; 而不是Dictionary&lt;string, BasicList&gt;。 (我也会用适当的大写对其进行重命名,以与您的其他属性保持一致。)
  3. 添加如下所示的私有OnDeserialized 方法,以便在反序列化过程结束时从扩展字典中填充List&lt;BasicList&gt;

所以你的BasicListResponse 类应该是这样的:

public class BasicListResponse : Result
{
    public List<BasicList> List { get; set; }

    [JsonExtensionData]
    private Dictionary<string, JToken> Data { get; set; }

    [OnDeserialized]
    private void OnDeserialized(StreamingContext context)
    {
        List = Data?.OrderBy(kvp => kvp.Key)
                    .Select(kvp => kvp.Value.ToObject<BasicList>())
                    .ToList();
    }
}

我们还需要解决另一个小问题:在您的BasicList 类中,您将Private 属性定义为bool,但在JSON 中它是一个包含数字的字符串。由于类型不兼容,这将导致反序列化失败。要使其工作,您可以将 Private 更改为字符串,然后在代码中的其他位置处理值的解释,或者您可以执行以下操作:

public class BasicList
{
    ...

    [JsonProperty("private")]
    private string PrivateAsString { get; set; }

    [JsonIgnore]
    public bool Private
    {
        get { return PrivateAsString != "0"; }
        set { PrivateAsString = value ? "1" : "0"; }
    }

    ...
}

通过这些更改,您可以反序列化为您的 BasicListResponse 类,并且一切都应该正常工作。

var result = JsonConvert.DeserializeObject<BasicListResponse>(json);

这里的工作演示:https://dotnetfiddle.net/9MmSuG

【讨论】:

  • 罗杰斯 - 我要放弃了
  • 罗杰斯,解决方案奏效了。感谢您的帮助。` [JsonExtensionData]` 将成为一个很棒的工具,因为我正在处理大量没有遵循良好结构的 JSON。
  • 没问题;很高兴您发现该解决方案很有用。
【解决方案2】:

不是最优雅的解决方案,但您可以像这样构建您的类:

public class Results
{
    [JsonExtensionData]
    public IDictionary<string, JToken> Items;

    [JsonProperty("result_code")]
    public long ResultCode { get; set; }

    [JsonProperty("result_message")]
    public string ResultMessage { get; set; }

    [JsonProperty("result_output")]
    public string ResultOutput { get; set; }
}

public class Result
{
    public long Id { get; set; }

    public string Name { get; set; }

    public DateTime Cdate { get; set; }

    public long Private { get; set; }

    public long UserId { get; set; }

    public long SubscriberCount { get; set; }

    public override string ToString()
    {
        return $"Id={Id},Name={Name},CDate={Cdate.ToString()},Private={Private},UserId={UserId},SubscriberCount={SubscriberCount}";
    }
}

然后您可以反序列化您的 json 并将您的 IDictionary&lt;string, JToken&gt; Items 映射到 IEnumerable&lt;Result&gt;。请注意,在这种情况下,JsonExtensionData 需要为 IDictionary&lt;string, JToken&gt;

var deserializedJson = JsonConvert.DeserializeObject<Results>(json);

var results = deserializedJson
    .Items
    .Select(entry => new Result
    {
        Id = (long)entry.Value["id"],
        Name = (string)entry.Value["name"],
        Cdate = DateTime.TryParse((string)entry.Value["cdate"], out var cdate) ? cdate : DateTime.MinValue,
        Private = (long)entry.Value["private"],
        UserId = (long)entry.Value["userid"],
        SubscriberCount = (long)entry.Value["subscriber_count"]
    });

Console.WriteLine($"result_code={deserializedJson.ResultCode},result_message={deserializedJson.ResultMessage},result_output={deserializedJson.ResultOutput}");
foreach (var result in results)
{
    Console.WriteLine(result.ToString());
}

输出:

result_code=1,result_message=Success: Something is returned,result_output=json
Id=1,Name=xxxxx xxxxxx xxxxxxx,CDate=27/10/2019 10:43:23 PM,Private=0,UserId=1,SubscriberCount=1
Id=2,Name=yyyyy yyyyy yyyyy,CDate=27/10/2019 10:44:03 PM,Private=0,UserId=1,SubscriberCount=0

【讨论】:

    【解决方案3】:

    试试

    "list":{
        "0": {
            "id": "1",
            "name": "xxxxx xxxxxx xxxxxxx",
            "cdate": "2019-10-27 22:43:23",
            "private": "0",
            "userid": "1",
            "subscriber_count": 1
        },
        "1": {
            "id": "2",
            "name": "yyyyy yyyyy yyyyy",
            "cdate": "2019-10-27 22:44:03",
            "private": "0",
            "userid": "1",
            "subscriber_count": 0
        },
        "result_code": 1,
        "result_message": "Success: Something is returned",
        "result_output": "json"
    }
    

    【讨论】:

    • 这并不能解决 OP 的问题。 JSON 来自第三方。
    • @shan,不会采用 JSON 操作路径来解决问题。收到的 JSON 是固定的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-24
    • 2017-10-17
    • 2019-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多