【问题标题】:C# System.Text.Json The JSON value could not be converted to Class structureC# System.Text.Json JSON 值无法转换为类结构
【发布时间】:2020-12-08 11:27:41
【问题描述】:

我正在使用我无法更改的第三方 API,它有时会返回标准 JSON 结构,而有时则不会。我怀疑原因是第 3 方应用程序中的数据在 UI 中是可选的。如果数据存在,它将如下所示:

{
    "contactID": "1234",
    :
    <some other attributes>
    :
    "Websites": {
        "ContactWebsite": {
            "url": "www.foo.com"
        }
    }
}

当它不存在时,它看起来像这样......

{
    "id": "1234",
    :
    <some other attributes>
    :
    "Websites": ""
}

我的代码看起来像...

using System;
using System.Text.Json;

namespace TestJsonError
{
    class Program
    {
        static void Main(string[] args)
        {
            string content = "{ \"contactID\": \"217\", \"custom\": \"\", \"noEmail\": \"true\", \"noPhone\": \"true\", \"noMail\": \"true\", \"Websites\": \"\", \"timeStamp\": \"2020-10-13T19:21:38+00:00\" }";

            Console.WriteLine($"{content}");

            var response = JsonSerializer.Deserialize<ContactResponse>(content);

            Console.WriteLine($"contactID={response.contactID}");
        }
    }

    public class ContactResponse
    {
        public string contactID { get; set; }
        public string custom { get; set; }
        public string noEmail { get; set; }
        public string noPhone { get; set; }
        public string noMail { get; set; }
        public WebsitesResponse Websites { get; set; }
        public DateTime timeStamp { get; set; }
    }

    public class WebsitesResponse
    {
        public ContactWebsiteResponse ContactWebsite { get; set; }
    }

    public class ContactWebsiteResponse
    {
        public string url { get; set; }
    }
}

我收到以下错误...

> System.Text.Json.JsonException   HResult=0x80131500   Message=The JSON
> value could not be converted to TestJsonError.WebsitesResponse. Path:
> $.Websites | LineNumber: 0 | BytePositionInLine: 106.  
> Source=System.Text.Json   StackTrace:    at
> System.Text.Json.ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type
> propertyType)    at
> System.Text.Json.JsonPropertyInfoNotNullable`4.OnRead(ReadStack&
> state, Utf8JsonReader& reader)    at
> System.Text.Json.JsonPropertyInfo.Read(JsonTokenType tokenType,
> ReadStack& state, Utf8JsonReader& reader)    at
> System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions
> options, Utf8JsonReader& reader, ReadStack& readStack)    at
> System.Text.Json.JsonSerializer.ReadCore(Type returnType,
> JsonSerializerOptions options, Utf8JsonReader& reader)    at
> System.Text.Json.JsonSerializer.Deserialize(String json, Type
> returnType, JsonSerializerOptions options)    at
> System.Text.Json.JsonSerializer.Deserialize[TValue](String json,
> JsonSerializerOptions options)    at
> TestJsonError.Program.Main(String[] args) in
> C:\Users\simon\source\repos\TestJsonError\TestJsonError\Program.cs:line
> 14
> 
>   This exception was originally thrown at this call stack:
>     [External Code]
>     TestJsonError.Program.Main(string[]) in Program.cs

有什么建议吗?


编辑...

对于 DavidG 下面的优雅回应,我有点不好意思提出这种替代方法,但为了完整起见,我想我会分享。请注意...它很脏但有点自相矛盾。

似乎我正在调用的API,当它没有数据要返回时,它会发回一个空字符串(“”)。我在其他 API 中观察到,当出现这种情况时,API 返回 null。

如果我将"Websites": "" 替换为"Websites": null,然后再替换为JsonSerializer.Deserialize,它会很有效。我应该使用这种方法吗?好吧,我知道这很脏,但是因为我必须在 Json 类中添加很多行来适应这种情况,实际上代码似乎更容易理解。我预计会有性能开销,但我不会处理大量事务,所以在我的情况下可能没问题。

乐于接受其他意见。

【问题讨论】:

  • 看起来您可能需要custom converter。当然,问题在于“网站”可以具有类型字符串或类型对象,您无法使用 C# 对象模型对其进行编码
  • 在转换之前删除额外的:
  • @canton7...谢谢。作为比较,我尝试使用 Newtonsoft.Json... 没有问题。我的偏好是使用 Microsoft 的 System.Text.Json,但我没有时间了解自定义转换器......面对需要获得业务功能的情况。 :-(
  • 问题是“WebsitesResponse”是一个对象,但您发送的是字符串“”,想知道这个 JSON 是如何创建的,显然它不能是空字符串。要么修复你的模型或 json,写转换会产生更多的复杂性。
  • @MichaelMao...你指的是哪个额外的:?

标签: c# json .net-core deserialization


【解决方案1】:

您可以使用custom converter 来完成此操作。例如:

public class WebsitesConverter : JsonConverter<WebsitesResponse>
{
    public override WebsitesResponse Read(ref Utf8JsonReader reader, Type typeToConvert, 
        JsonSerializerOptions options)
    {
        if(reader.TokenType == JsonTokenType.String)
        {
            // You can either return this, or a null object if you prefer
            return new WebsitesResponse
            {
                ContactWebsite = new ContactWebsiteResponse
                {
                    url = reader.GetString()
                }
            };
        }
        
        return JsonSerializer.Deserialize<WebsitesResponse>(ref reader);            
    }

    public override void Write(Utf8JsonWriter writer, WebsitesResponse value, 
        JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

并更改您的模型以注册转换器:

public class ContactResponse
{
    public string contactID { get; set; }
    public string custom { get; set; }
    public string noEmail { get; set; }
    public string noPhone { get; set; }
    public string noMail { get; set; }
    [JsonConverter(typeof(WebsitesConverter))]
    public WebsitesResponse Websites { get; set; }
    public DateTime timeStamp { get; set; }
}

奖金!

如果你想让它更通用一点,这会起作用:

public class StringObjectConverter<T> : JsonConverter<T> where T : class
{
    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if(reader.TokenType == JsonTokenType.String)
        {
            return null;
        }
        
        return JsonSerializer.Deserialize<T>(ref reader);
        
    }

    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

您的模型将更改为:

[JsonConverter(typeof(StringObjectConverter<WebsitesResponse>))]
public WebsitesResponse Websites { get; set; }

【讨论】:

  • 我得到了WebsitesResponse 没有定义Dump()
  • @MichaelMao 对不起,我把调试代码放在那里了 :)
  • @DavidG...天才! :-) 对于奖励积分,第三方 API 会针对不同的嵌套 json 对象多次执行此操作。是否可以使其通用或至少在自定义转换器中列出其中的每一个,这样我就不必为每次出现单独的自定义转换器?你得到我的最佳答案投票!
  • @MashedSpud 为您添加了通用版本
  • @DavidG...再次感谢! :-) 有趣的是,当我尝试 Newtonsoft.Json 时,它似乎适用于我上面帖子中的示例,但随后在更大的 json 有效负载中多次出现相同问题而失败。我想我可能不得不努力学习自定义转换器......在这种情况下他们似乎已经得到了回报。我欠你一杯啤酒! :-)
猜你喜欢
  • 2020-02-25
  • 1970-01-01
  • 2019-12-23
  • 2013-08-20
  • 2016-09-10
  • 2021-01-15
  • 2022-06-10
  • 2021-10-17
  • 2012-11-12
相关资源
最近更新 更多