System.Text.Json 自定义 JsonConverter<DateTime> .Read() 方法未被 .Deserialize() 使用

Posted

技术标签:

【中文标题】System.Text.Json 自定义 JsonConverter<DateTime> .Read() 方法未被 .Deserialize() 使用【英文标题】:System.Text.Json Custom JsonConverter<DateTime> .Read() method not being used by .Deserialize() 【发布时间】:2020-05-01 04:11:03 【问题描述】:

我在使用 System.Text.Json 和自定义日期时间格式时遇到问题。

我创建了一个自定义转换器,它在我的 api 项目中工作。 当我构建我的客户端时,我尝试使用相同的类,但是 .Deserialize() 方法不会在我的类的 DateTime 属性上调用 .Read()。

有人遇到过同样的问题吗?

public class DateTimeConverter : JsonConverter<DateTime>

    private static readonly string _format = "yyyy-MM-dd'T'HH:mm:ss.fff";

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    
        return DateTime.ParseExact(reader.GetString(), _format, System.Globalization.CultureInfo.InvariantCulture);
    

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    => writer.WriteStringValue(value.ToUniversalTime().ToString(_format));


public async Task<StockUnitInfo> GetStockUnitInfoAsync(Inbound inbound, CancellationToken token = default)

    var uri = Uri.EscapeUriString($"_httpClient.BaseAddress/Readers/inbound.Position/StockUnits/inbound.Barcode");

    var test = await _httpClient.GetStringAsync(uri);

    var response = await _httpClient.GetStreamAsync(uri);

    var _serializationOptions = new JsonSerializerOptions
    
        Converters =  new DateTimeConverter() 
    ;

    return await JsonSerializer.DeserializeAsync<StockUnitInfo>(response, _serializationOptions, token);


public class StockUnitInfo

    public string barcode  get; set; 
    public string wms_batch_id  get; set; 
    public string gtin  get; set; 

    //[JsonConverter(typeof(DateTimeConverter))]
    public DateTime sort_date  get; set; 
    public string talleys_batch_number  get; set; 
    public int expected_batch_size  get; set; 
    public string destination  get; set; 
    public bool fast_mover  get; set; 
    public int stackable  get; set; 
    public string message  get; set; 

要求的最小可重现示例:

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

class Program

    public class DateTimeConverter : JsonConverter<DateTime>
    
        //private static readonly string _format = "yyyy.MM.dd'T'HH:mm:ss.fff'Z'";  //formato api?
        private static readonly string _format = "yyyy-MM-dd'T'HH:mm:ss.fff";


        public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        
            return DateTime.ParseExact(reader.GetString(), _format, System.Globalization.CultureInfo.InvariantCulture);
        

        public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
        => writer.WriteStringValue(value.ToUniversalTime().ToString(_format));
    
    public class StockUnitInfo
    
        public string barcode  get; set; 
        public string wms_batch_id  get; set; 
        public string gtin  get; set; 

        //[JsonConverter(typeof(DateTimeConverter))]
        public DateTime sort_date  get; set; 
        public string talleys_batch_number  get; set; 
        public int expected_batch_size  get; set; 
        public string destination  get; set; 
        public bool fast_mover  get; set; 
        public int stackable  get; set; 
        public string message  get; set; 
    
    public static void Main(string[] args)
    
        string json = "\"barcode\":\"201930823\",\"wmsBatchId\":\"c3e4d921-8779-46f7-ac28-b1f523845329\",\"gtin\":\"01080\",\"sortDate\":\"2019-09-20T11:59:59.999\",\"talleysBatchNumber\":\"STK0233\",\"expectedBatchSize\":1,\"destination\":\"5A05\",\"fastMover\":false,\"stackable\":0,\"message\":\"Pallet directed to CS4\"";

        var _serializationOptions = new JsonSerializerOptions
        
            Converters =  new DateTimeConverter() 
        ;
        var result = JsonSerializer.Deserialize<StockUnitInfo>(json, _serializationOptions);

        Console.WriteLine(result.sort_date);
        Console.Read();
    

【问题讨论】:

能否提供一个不反序列化的Json示例? .. 或(更好)准备minimal reproducible example 输入说明问题的内容。顺便说一句,在准备过程中您可以自己解决问题。 谢谢你的建议,现在清楚了吗? @stuartd 样本够吗? 【参考方案1】:

这是因为类属性的名称和Json属性的名称不同:

"sortDate": "2019-09-20T11:59:59.999"

public DateTime sort_date get; set;

为了反序列化该 json,您需要使名称相同,或者通过添加一个属性来告诉序列化程序要做什么来告诉等效名称是什么:

// For NewtonSoft.Json
// [JsonProperty("sortDate")]

// For System.Text.Json
[JsonPropertyName("sortDate")]
public DateTime sort_date  get; set; 

添加后,您的示例代码将正确运行:

【讨论】:

除了添加JsonPropertyName 属性,您还可以考虑实现自己的JsonNamingPolicy 以支持snake_case 并将其设置为JsonSerializerOptions 中的PropertyNamingPolicy 或等待SnakeCase支持添加到框架中:github.com/dotnet/runtime/issues/782 天哪,我怎么没看到

以上是关于System.Text.Json 自定义 JsonConverter<DateTime> .Read() 方法未被 .Deserialize() 使用的主要内容,如果未能解决你的问题,请参考以下文章

System.Text.Json 自定义 Conveter

System.Text.Json 自定义Converter实现时间转换

System.Text.Json 自定义 JsonConverter<DateTime> .Read() 方法未被 .Deserialize() 使用

使用 System.Text.Json 时,如何处理 Dictionary 中 Key 为自定义类型的问题

System.Json - 属性序列化跳过的自定义规则

ASP Web API:将对象序列化为 JSON 时指定自定义字段名称