JsonConverter - WebApi - 区分大小写 - 多态
JsonConverter - WebApi - Case Sensitivity - Polymorphic
我正在使用 Json转换器来处理多态集合:
class ItemBatch
{
List<ItemBase> Items { get; set; }
}
// For type discrimination of ItemBase
class ItemTypes
{
public int Value { get; set; }
}
[JsonConverter(typeof(ItemConverter))]
abstract class ItemBase
{
public abstract ItemTypes Type { get; set; }
}
class WideItem : ItemBase
{
public override ItemTypes Type => 1;
public decimal Width { get; set; }
}
class HighItem : ItemBase
{
public override ItemTypes Type => 2;
public decimal Height { get; set; }
}
class ItemConverter : JsonConverter<ItemBase>
{
public override ItemBase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using (JsonDocument jsonDoc = JsonDocument.ParseValue(ref reader));
int type = jsonDoc.RootElement.GetProperty("Type").GetProperty("Value").GetInt32();
// deserialize depending on type.
}
}
我正在使用 Blazor 并将 ItemBatch 存储在 IndexedDb 中,然后稍后再次检索并发送到 WebAPI。
序列化到 IndexedDb 和从 IndexedDb 反序列化工作正常。
但是,当我尝试将 ItemBatch 发送到 WebAPI 时,出现错误:
Exception thrown: 'System.Collections.Generic.KeyNotFoundException' in
System.Text.Json.dll An exception of type
'System.Collections.Generic.KeyNotFoundException' occurred in
System.Text.Json.dll but was not handled in user code The given key
was not present in the dictionary.
通过查看各种值,我怀疑是区分大小写的问题。的确,如果我改变:
int type = jsonDoc.RootElement.GetProperty("Type").GetProperty("Value").GetInt32();
到
int type;
try
{
type = jsonDoc.RootElement.GetProperty("Type").GetProperty("Value").GetInt32();
}
catch (Exception)
{
type = jsonDoc.RootElement.GetProperty("type").GetProperty("value").GetInt32();
}
然后我克服了这个错误并调用了我的 WebAPI。
我错过了什么允许我对 IndexedDb 进行序列化/反序列化,但是 WebApi Json 转换存在区分大小写的问题。
Newtonsoft 不区分大小写。
有了 System.Text.Json 你必须再拉动一些杠杆。
Case-insensitive deserialization During deserialization,
Newtonsoft.Json does case-insensitive property name matching by
default. The System.Text.Json default is case-sensitive, which gives
better performance since it's doing an exact match. For information
about how to do case-insensitive matching, see Case-insensitive
property matching.
另见下面 URL:
https://makolyte.com/csharp-case-sensitivity-in-json-deserialization/
这里有一个可能的处理方法:
https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-character-casing
above url is ::: How to enable case-insensitive property name matching with
System.Text.Json
Remarks
Property name matching is performed as an ordinal, case-sensitive comparison.
我不认为你可以通过“自己动手”来克服这种行为。
但也许你可以追这个:
但这似乎没有“自己动手”。
我的结论是,使用默认序列化,JsonSerializerOptions 不会为 PropertyNamingPolicy 设置值“CamelCase”,但 HttpClient 和 WebApi 请求管道会:
PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
这意味着当我序列化和反序列化到 IndexedDb 时,默认序列化将 Json 保留为 Pascal Case。
我的自定义 JsonConverter 使用传递到 Read 和 Write 方法的选项参数,因此在使用 IndexedDb 时在客户端上使用 Pascal 大小写。
但是,当 HttpClient 和 WebApi 请求管道调用同一个 JsonConverter 时,选项设置为:
PropertyNamePolicy = JsonNamingPolicy.CamelCase.
并且在尝试解析为 Json 文档时,内容现在采用驼峰式大小写,然后我使用 Pascal 大小写假设阅读 Json 文档然后失败了。
我的问题的答案是更新写入方法如下:
public override void Write(Utf8JsonWriter writer, SaleCommandBase value, JsonSerializerOptions options)
{
JsonSerializerOptions newOptions = new JsonSerializerOptions(options) { PropertyNamingPolicy = null };
JsonSerializer.Serialize(writer, (object)value, newOptions);
}
这会强制序列化在所有情况下都使用 Pascal 大小写,无论是客户端上的本地序列化(写入 IndexedDb 时)还是发送到 WebAPI 时在 HttpClient 中序列化。
同理,在read方法中:
using (JsonDocument jsonDoc = JsonDocument.ParseValue(ref reader))
{
int type = jsonDoc.RootElement.GetProperty("Type").GetProperty("Value").GetInt32();
newOptions = new JsonSerializerOptions(options) { PropertyNamingPolicy = null };
return type switch
{
1 => jsonDoc.RootElement.Deserialize<WideItem>(newOptions),
2 => jsonDoc.RootElement.Deserialize<HighItem>(newOptions),
_ => throw new InvalidOperationException($"Cannot convert type '{type}'."),
};
}
通过复制提供的任何选项,然后覆盖命名策略以使用 Pascal 大小写 (PropertyNamingPolicy = null),然后我可以确信解析的 Json 文档将始终采用 Pascal 大小写,无论框架提供的选项如何。
我正在使用 Json转换器来处理多态集合:
class ItemBatch
{
List<ItemBase> Items { get; set; }
}
// For type discrimination of ItemBase
class ItemTypes
{
public int Value { get; set; }
}
[JsonConverter(typeof(ItemConverter))]
abstract class ItemBase
{
public abstract ItemTypes Type { get; set; }
}
class WideItem : ItemBase
{
public override ItemTypes Type => 1;
public decimal Width { get; set; }
}
class HighItem : ItemBase
{
public override ItemTypes Type => 2;
public decimal Height { get; set; }
}
class ItemConverter : JsonConverter<ItemBase>
{
public override ItemBase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using (JsonDocument jsonDoc = JsonDocument.ParseValue(ref reader));
int type = jsonDoc.RootElement.GetProperty("Type").GetProperty("Value").GetInt32();
// deserialize depending on type.
}
}
我正在使用 Blazor 并将 ItemBatch 存储在 IndexedDb 中,然后稍后再次检索并发送到 WebAPI。
序列化到 IndexedDb 和从 IndexedDb 反序列化工作正常。
但是,当我尝试将 ItemBatch 发送到 WebAPI 时,出现错误:
Exception thrown: 'System.Collections.Generic.KeyNotFoundException' in System.Text.Json.dll An exception of type 'System.Collections.Generic.KeyNotFoundException' occurred in System.Text.Json.dll but was not handled in user code The given key was not present in the dictionary.
通过查看各种值,我怀疑是区分大小写的问题。的确,如果我改变:
int type = jsonDoc.RootElement.GetProperty("Type").GetProperty("Value").GetInt32();
到
int type;
try
{
type = jsonDoc.RootElement.GetProperty("Type").GetProperty("Value").GetInt32();
}
catch (Exception)
{
type = jsonDoc.RootElement.GetProperty("type").GetProperty("value").GetInt32();
}
然后我克服了这个错误并调用了我的 WebAPI。
我错过了什么允许我对 IndexedDb 进行序列化/反序列化,但是 WebApi Json 转换存在区分大小写的问题。
Newtonsoft 不区分大小写。
有了 System.Text.Json 你必须再拉动一些杠杆。
Case-insensitive deserialization During deserialization, Newtonsoft.Json does case-insensitive property name matching by default. The System.Text.Json default is case-sensitive, which gives better performance since it's doing an exact match. For information about how to do case-insensitive matching, see Case-insensitive property matching.
另见下面 URL:
https://makolyte.com/csharp-case-sensitivity-in-json-deserialization/
这里有一个可能的处理方法:
https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-character-casing
above url is ::: How to enable case-insensitive property name matching with System.Text.Json
Remarks
Property name matching is performed as an ordinal, case-sensitive comparison.
我不认为你可以通过“自己动手”来克服这种行为。
但也许你可以追这个:
但这似乎没有“自己动手”。
我的结论是,使用默认序列化,JsonSerializerOptions 不会为 PropertyNamingPolicy 设置值“CamelCase”,但 HttpClient 和 WebApi 请求管道会:
PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
这意味着当我序列化和反序列化到 IndexedDb 时,默认序列化将 Json 保留为 Pascal Case。
我的自定义 JsonConverter 使用传递到 Read 和 Write 方法的选项参数,因此在使用 IndexedDb 时在客户端上使用 Pascal 大小写。
但是,当 HttpClient 和 WebApi 请求管道调用同一个 JsonConverter 时,选项设置为:
PropertyNamePolicy = JsonNamingPolicy.CamelCase.
并且在尝试解析为 Json 文档时,内容现在采用驼峰式大小写,然后我使用 Pascal 大小写假设阅读 Json 文档然后失败了。
我的问题的答案是更新写入方法如下:
public override void Write(Utf8JsonWriter writer, SaleCommandBase value, JsonSerializerOptions options)
{
JsonSerializerOptions newOptions = new JsonSerializerOptions(options) { PropertyNamingPolicy = null };
JsonSerializer.Serialize(writer, (object)value, newOptions);
}
这会强制序列化在所有情况下都使用 Pascal 大小写,无论是客户端上的本地序列化(写入 IndexedDb 时)还是发送到 WebAPI 时在 HttpClient 中序列化。
同理,在read方法中:
using (JsonDocument jsonDoc = JsonDocument.ParseValue(ref reader))
{
int type = jsonDoc.RootElement.GetProperty("Type").GetProperty("Value").GetInt32();
newOptions = new JsonSerializerOptions(options) { PropertyNamingPolicy = null };
return type switch
{
1 => jsonDoc.RootElement.Deserialize<WideItem>(newOptions),
2 => jsonDoc.RootElement.Deserialize<HighItem>(newOptions),
_ => throw new InvalidOperationException($"Cannot convert type '{type}'."),
};
}
通过复制提供的任何选项,然后覆盖命名策略以使用 Pascal 大小写 (PropertyNamingPolicy = null),然后我可以确信解析的 Json 文档将始终采用 Pascal 大小写,无论框架提供的选项如何。