如何使用 System.Text.Json 反序列化嵌套时间跨度 属性?
How to deserialize nested timespan property with System.Text.Json?
我正在尝试使用转换器 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); }
}
出于某种原因,对时间跨度转换器的“小”改动就起到了作用:
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);
}
我正在尝试使用转换器 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); }
}
出于某种原因,对时间跨度转换器的“小”改动就起到了作用:
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);
}