簡體   English   中英

JSON 反序列化:使用自定義 JsonConverter 處理缺失的屬性

[英]JSON Deserialization: handling missing properties with a custom JsonConverter

我為我正在構建的項目創建了一個 JSON Web Token 類:

[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類有問題,但是我在網上看到的幾乎所有示例都在談論將類轉換為字符串,而不是將字符串轉換為類,因此很難理解我做錯了什么。 有人能幫我嗎?

我嘗試了NullValueHandlingDefaultValueHandling不同組合,所以我認為問題出在JsonConverter類中,但我可能是錯的。

問題是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)而不是JsonReader.Value確保讀取器將位於當前值的末尾,以防它是您期望的更復雜的對象。

順便說一下,如果您將 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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM