JSON 反序列化:使用自定义 JsonConverter 处理缺失的属性
JSON Deserialization: handling missing properties with a custom JsonConverter
我为我正在构建的项目制作了一个 JSON 网络令牌 class:
[JsonObject]
public class JwtPayload
{
[JsonProperty("iss", NullValueHandling = NullValueHandling.Ignore)]
public string Issuer { get; set; } = "https://mycompany.com/myIdentityServices";
[JsonProperty("sub", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
public string Subject { get; set; }
. . .
[JsonProperty("nbf", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
[JsonConverter(typeof(JsonUnixTimestampConverter))]
public DateTime? NotBefore { get; set; } = null;
[JsonProperty("iat", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
[JsonConverter(typeof (JsonUnixTimestampConverter))]
public DateTime? IssuedAt { get; set; }
. . .
}
我有一个用于 Unix 时间戳转换的 JsonConverter,如下所示:
public class JsonUnixTimestampConverter : JsonConverter
{
public override bool CanRead => true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var dt = value as DateTime?;
if (dt.HasValue)
{
writer.WriteRawValue(dt.Value.ToUnixTimestamp().ToString());
}
else
{
writer.WriteNull();
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return GetDefault(objectType);
}
var text = reader.ReadAsString();
return text.FromUnixTimestamp();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof (DateTime) || objectType == typeof(DateTime?);
}
private object GetDefault(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
}
令牌的序列化工作顺利...我得到了我期待的 JSON。问题是,如果我使用 JSON 令牌并尝试在缺少 "nbf" 值的情况下执行 JsonConvert.Deserialize<JwtPayload>("my JSON")
,我的反序列化会在尝试将 "iat" 解析为一个 Unix 时间戳,它不太好。
显然我的 JsonConverter
class 有问题,但我在网上看到的几乎所有示例都在谈论将 class 转换为字符串,而不是将字符串转换为 class,所以很难理解我做错了什么。有人可以帮助我吗?
我尝试了 NullValueHandling
和 DefaultValueHandling
的不同组合,所以我认为问题出在 JsonConverter
class,但我可能是错的。
问题是 JsonReader.ReadAsString()
从流中读取下一个 JSON 标记作为字符串。 您想要解析 当前 JSON 标记为字符串。因此:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return GetDefault(objectType);
}
var text = (string)JToken.Load(reader);
return text.FromUnixTimestamp();
}
使用 (string)JToken.Load(reader)
instead of JsonReader.Value
可确保 reader 位于当前值的末尾,以防它是您期望的更复杂的对象。
顺便说一下,如果您手动将 UNIX 时间戳解析和格式化为字符串,您应该确保在不变区域性而不是当前区域性中这样做。或者更好的是,让 Json.NET 为您完成,将您的时间戳读写为 long
而不是 string
:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var dt = value as DateTime?;
if (dt.HasValue)
{
writer.WriteValue(dt.Value.ToUnixTimestamp());
}
else
{
writer.WriteNull();
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return GetDefault(objectType);
}
var stamp = (long)JToken.Load(reader);
return stamp.FromUnixTimestamp();
}
正在使用
public static class UnixTimeExtensions
{
const long SecondsToMilliseconds = 1000L;
const long MillisecondsToTicks = 10000L;
static readonly DateTime utcEpochStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
public static DateTime FromUnixTimestamp(this long stamp)
{
return utcEpochStart.AddSeconds(stamp);
}
public static long ToUnixTimestamp(this DateTime dateTime)
{
var span = dateTime.ToUniversalTime() - utcEpochStart;
return span.Ticks / (SecondsToMilliseconds * MillisecondsToTicks);
}
}
我为我正在构建的项目制作了一个 JSON 网络令牌 class:
[JsonObject]
public class JwtPayload
{
[JsonProperty("iss", NullValueHandling = NullValueHandling.Ignore)]
public string Issuer { get; set; } = "https://mycompany.com/myIdentityServices";
[JsonProperty("sub", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
public string Subject { get; set; }
. . .
[JsonProperty("nbf", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
[JsonConverter(typeof(JsonUnixTimestampConverter))]
public DateTime? NotBefore { get; set; } = null;
[JsonProperty("iat", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
[JsonConverter(typeof (JsonUnixTimestampConverter))]
public DateTime? IssuedAt { get; set; }
. . .
}
我有一个用于 Unix 时间戳转换的 JsonConverter,如下所示:
public class JsonUnixTimestampConverter : JsonConverter
{
public override bool CanRead => true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var dt = value as DateTime?;
if (dt.HasValue)
{
writer.WriteRawValue(dt.Value.ToUnixTimestamp().ToString());
}
else
{
writer.WriteNull();
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return GetDefault(objectType);
}
var text = reader.ReadAsString();
return text.FromUnixTimestamp();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof (DateTime) || objectType == typeof(DateTime?);
}
private object GetDefault(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
}
令牌的序列化工作顺利...我得到了我期待的 JSON。问题是,如果我使用 JSON 令牌并尝试在缺少 "nbf" 值的情况下执行 JsonConvert.Deserialize<JwtPayload>("my JSON")
,我的反序列化会在尝试将 "iat" 解析为一个 Unix 时间戳,它不太好。
显然我的 JsonConverter
class 有问题,但我在网上看到的几乎所有示例都在谈论将 class 转换为字符串,而不是将字符串转换为 class,所以很难理解我做错了什么。有人可以帮助我吗?
我尝试了 NullValueHandling
和 DefaultValueHandling
的不同组合,所以我认为问题出在 JsonConverter
class,但我可能是错的。
问题是 JsonReader.ReadAsString()
从流中读取下一个 JSON 标记作为字符串。 您想要解析 当前 JSON 标记为字符串。因此:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return GetDefault(objectType);
}
var text = (string)JToken.Load(reader);
return text.FromUnixTimestamp();
}
使用 (string)JToken.Load(reader)
instead of JsonReader.Value
可确保 reader 位于当前值的末尾,以防它是您期望的更复杂的对象。
顺便说一下,如果您手动将 UNIX 时间戳解析和格式化为字符串,您应该确保在不变区域性而不是当前区域性中这样做。或者更好的是,让 Json.NET 为您完成,将您的时间戳读写为 long
而不是 string
:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var dt = value as DateTime?;
if (dt.HasValue)
{
writer.WriteValue(dt.Value.ToUnixTimestamp());
}
else
{
writer.WriteNull();
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return GetDefault(objectType);
}
var stamp = (long)JToken.Load(reader);
return stamp.FromUnixTimestamp();
}
正在使用
public static class UnixTimeExtensions
{
const long SecondsToMilliseconds = 1000L;
const long MillisecondsToTicks = 10000L;
static readonly DateTime utcEpochStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
public static DateTime FromUnixTimestamp(this long stamp)
{
return utcEpochStart.AddSeconds(stamp);
}
public static long ToUnixTimestamp(this DateTime dateTime)
{
var span = dateTime.ToUniversalTime() - utcEpochStart;
return span.Ticks / (SecondsToMilliseconds * MillisecondsToTicks);
}
}