C# JSON反序列化:使用自定义JsonConverter处理缺少的属性

C# JSON反序列化:使用自定义JsonConverter处理缺少的属性,c#,json.net,json-deserialization,C#,Json.net,Json Deserialization,我为正在构建的项目创建了一个JSON Web令牌类: [JsonObject] public class JwtPayload { [JsonProperty("iss", NullValueHandling = NullValueHandling.Ignore)] public string Issuer { get; set; } = "https://mycompany.com/myIdentityServices"; [JsonProperty("sub", N

我为正在构建的项目创建了一个JSON Web令牌类:

[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令牌并尝试执行
JsonConvert.Deserialize(“我的JSON”)
,其中缺少“nbf”值,那么当我的反序列化尝试将“iat”解析为Unix时间戳时,它就会崩溃,这一点做得不太好

显然,我的
JsonConverter
类中存在一个问题,但我在网上看到的几乎所有示例都谈到将类转换为字符串,而不是将字符串转换为类,因此很难理解我做错了什么。有人能帮我吗

我尝试了
NullValueHandling
DefaultValueHandling
的不同组合,因此我认为问题出在
JsonConverter
类中,但我可能错了。

问题是将流中的下一个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();
    }
使用instead of可确保读取器位于当前值的末尾,以防它是您期望的更复杂的对象

顺便说一句,如果要手动解析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();
    }
使用


好极了,这就成功了。顺便说一句,我的理解是unix时间戳是一个32位整数,但您引用它的时间很长。我的理解不正确吗?@JeremyHolovacs-在.NET方面,为了安全起见,我总是将时间戳表示为
long
,因为1)有时unix时间戳以毫秒为单位,而不是秒,这需要
long
,2)。
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);
    }
}