【问题标题】:How to deserialize nested timespan property with System.Text.Json?如何使用 System.Text.Json 反序列化嵌套的时间跨度属性?
【发布时间】:2021-02-21 09:27:28
【问题描述】:

我正在尝试通过 System.Text.Json 使用转换器反序列化 json 数据。

  • http 响应的原始内容显示 json 包含有效数据
  • 正在调用将内容反序列化为指定类型的可观察集合的转换器,并使用除 TIMESPAN 之外的所有数据生成正确的集合。
  • TimeSpan 转换器仅在它不是集合时才被调用,只有当它是单个对象时才被调用。
  • 问题似乎是“转换器”需要另一个用于嵌套对象的“转换器”。

在这方面有什么帮助或经验吗?

ObservableCollectionJsonConverter

public class ObservableCollectionJsonConverter<T> : JsonConverter<ObservableCollection<T>> where T : class
{
    public override ObservableCollection<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        ObservableCollection<T> collection = null;
        var startDepth = reader.CurrentDepth;
        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.EndObject && reader.CurrentDepth == startDepth) return collection;
            if (reader.TokenType == JsonTokenType.StartArray)
            {
                var deserialized = JsonSerializer.Deserialize<T[]>(ref reader, options);
                collection = new ObservableCollection<T>(deserialized);
            }
        }
        return collection;
    }

    public override void Write(Utf8JsonWriter writer, ObservableCollection<T> value, JsonSerializerOptions options) => writer.WriteStringValue(JsonSerializer.Serialize(value));
}

TimeSpanJsonConverter

public class TimeSpanJsonConverter : JsonConverter<TimeSpan>
{
    public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        long ticks = 0;
        var startDepth = reader.CurrentDepth;
        if (reader.TokenType == JsonTokenType.StartObject)
        {
            string propertyName = null;
            while (reader.Read())
            {
                switch (reader.TokenType)
                {
                    case JsonTokenType.EndObject when reader.CurrentDepth == startDepth:
                        return TimeSpan.FromTicks(ticks);
                    case JsonTokenType.PropertyName:
                        propertyName = reader.GetString();
                        break;
                }
                if (!string.IsNullOrWhiteSpace(propertyName) &&
                    propertyName.Equals("Ticks") &&
                    reader.TokenType == JsonTokenType.Number) ticks = reader.GetInt64();
            }
        }
        return TimeSpan.Zero;
    }

    public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) => writer.WriteStringValue(JsonSerializer.Serialize(value));
}

要反序列化的对象

public class Incident : ModelBase
{
    private string _uniqueId = default;
    private int _completion = default;
    private Status _status = default;
    private TimeSpan _estimated = default;
    private TimeSpan _actual = default;
    private DateTime _closed = default;
    private string _comments = default;
    private DateTime _opened = default;
    private DateTime _updated = default;
    private string _briefDescripion = default;
    private Project _project = default;

    /// <summary>
    /// The name of the project
    /// </summary>
    [JsonPropertyName("project")]
    public Project Project { get => _project; set => SetProperty(ref _project, value); }
    /// <summary>
    /// The incident's unique id
    /// </summary>
    [JsonPropertyName("uniqueId")]
    public string UniqueId { get => _uniqueId; set => SetProperty(ref _uniqueId, value); }
    /// <summary>
    /// The level of completion
    /// </summary>
    [JsonPropertyName("completion")]
    public int Completion { get => _completion; set => SetProperty(ref _completion, value); }
    /// <summary>
    /// The incident's state
    /// </summary>
    [JsonPropertyName("status")]
    public Status Status { get => _status; set => SetProperty(ref _status, value); }
    /// <summary>
    /// The expected time to resolve the incident
    /// </summary>
    [JsonPropertyName("estimated")]
    public TimeSpan Estimated { get => _estimated; set => SetProperty(ref _estimated, value); }
    /// <summary>
    /// The actual time on the incident
    /// </summary>
    [JsonPropertyName("actual")]
    public TimeSpan Actual { get => _actual; set => SetProperty(ref _actual, value); }
    /// <summary>
    /// The time when the incident was opened
    /// </summary>
    [JsonPropertyName("opened")]
    public DateTime Opened { get => _opened; set => SetProperty(ref _opened, value); }
    /// <summary>
    /// The time when the incident has been last updated
    /// </summary>
    [JsonPropertyName("updated")]
    public DateTime Updated { get => _updated; set => SetProperty(ref _updated, value); }
    /// <summary>
    /// The time when the incident has been closed
    /// </summary>
    [JsonPropertyName("closed")]
    public DateTime Closed { get => _closed; set => SetProperty(ref _closed, value); }
    /// <summary>
    /// The collection of reports assigned to the incident
    /// </summary>        
    [JsonPropertyName("reports")]
    [JsonInclude]
    public virtual ICollection<Report> Reports { get; set; } = new HashSet<Report>();
    /// <summary>
    /// The customer the incident is assigned to
    /// </summary>
    [JsonPropertyName("customer")]
    public virtual Customer Customer { get; set; }
    /// <summary>
    /// The supporter the incident is assigned to
    /// </summary>
    [JsonPropertyName("supporter")]
    public virtual Supporter Supporter { get; set; }
    /// <summary>
    /// Comments to document the case
    /// </summary>
    [JsonPropertyName("comments")]
    public string Comments { get => _comments; set => SetProperty(ref _comments, value); }
    /// <summary>
    /// Brief description about the incident
    /// </summary>
    [JsonPropertyName("briefDescripion")]
    public string BriefDescripion { get => _briefDescripion; set => SetProperty(ref _briefDescripion, value); }
}

【问题讨论】:

  • 能否请edit 分享minimal reproducible example 的问题? 1) JSON 不包含在您的问题中; 2) 您的Incident 模型由于缺少其他类的定义而无法编译,请参阅dotnetfiddle.net/oIM2Nd; 3) 未显示对JsonSerializer.Deserialize() 的调用和使用的序列化选项。
  • 在您的转换器中,您的阅读量可能过多或过少,或者您的数据模型可能与您的 JSON 不匹配。很难说没有minimal reproducible example。顺便说一句,您的 Incident 模型甚至不包括 ObservableCollection
  • 使用虚构的数据模型,我只发现您的转换器存在一些问题。 1) ObservableCollectionJsonConverter&lt;T&gt; 在起始标记是数组时读取过多。 2) 两个转换器都使用错误的技术在Write() 方法中生成默认序列化。为此,请参阅How to use default serialization in a custom System.Text.Json JsonConverter?。演示小提琴here。我们需要看到minimal reproducible example 来进一步回答您的问题。
  • 如果您在 .Net 3.0 中工作,那么这可能就是问题所在:[System.Text.Json] JsonSerializer ignores MaxDepth option #882: 此错误不仅针对转换器(也不针对 MaxDepth),并且只要调用者将 Utf8JsonReader 传递给 JsonSerializer.Deserialize 方法,并设置了非默认 JsonReaderOptions ......这是问题所在(注意新的 Utf8JsonReader 是在没有传入用户定义的选项的情况下创建的)......我们应该考虑在 3.1 中修复这个问题。
  • 其实是Net 5.0

标签: c# json .net-5 system.text.json


【解决方案1】:

出于某种原因,对时间跨度转换器的“小”更改完成了这项工作:

public class TimeSpanJsonConverter : JsonConverter<TimeSpan>
{
    public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        long ticks = 0;
        var startDepth = reader.CurrentDepth;
        if (reader.TokenType == JsonTokenType.StartObject)
        {
            string propertyName = null;
            while (reader.Read())
            {
                switch (reader.TokenType)
                {
                    case JsonTokenType.EndObject when reader.CurrentDepth == startDepth:
                        return TimeSpan.FromTicks(ticks);
                    case JsonTokenType.PropertyName:
                        propertyName = reader.GetString();
                        break;
                }
                if (!string.IsNullOrWhiteSpace(propertyName) && propertyName.Equals("Ticks") && reader.TokenType == JsonTokenType.Number) ticks = reader.GetInt64();
            }
        }
        else if (reader.TokenType == JsonTokenType.Number) return TimeSpan.FromTicks(reader.GetInt64());
        return TimeSpan.Zero;
    }

    public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) => writer.WriteNumberValue(value.Ticks);
}

【讨论】:

    猜你喜欢
    • 2021-07-22
    • 1970-01-01
    • 2020-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-09
    • 2021-12-08
    • 1970-01-01
    相关资源
    最近更新 更多