C# System.Text.Json JSON 值无法转换为 Class 结构
C# System.Text.Json The JSON value could not be converted to Class structure
我正在使用第三方 API,我无法更改,有时 return 是标准 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,当它没有数据到 return 时,它会发回一个空字符串 ("")。当这种情况出现时,我在其他 APIs 中观察到 API returns null.
如果我将 "Websites": ""
替换为 "Websites": null
,然后再替换为 JsonSerializer.Deserialize
,效果会很好。我应该使用这种方法吗?好吧,我知道它很脏,但是因为我不得不在 Json 类 中添加这么多行来满足这种情况,实际上代码似乎更容易理解。我预计会有性能开销,但我不处理大量数据,所以在我的情况下可能没问题。
乐于接受不同意见。
您可以使用 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; }
我正在使用第三方 API,我无法更改,有时 return 是标准 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,当它没有数据到 return 时,它会发回一个空字符串 ("")。当这种情况出现时,我在其他 APIs 中观察到 API returns null.
如果我将 "Websites": ""
替换为 "Websites": null
,然后再替换为 JsonSerializer.Deserialize
,效果会很好。我应该使用这种方法吗?好吧,我知道它很脏,但是因为我不得不在 Json 类 中添加这么多行来满足这种情况,实际上代码似乎更容易理解。我预计会有性能开销,但我不处理大量数据,所以在我的情况下可能没问题。
乐于接受不同意见。
您可以使用 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; }