簡體   English   中英

System.Text.Json - 屬性的多態序列化不起作用

[英]System.Text.Json - Polymorphic serialization for property is not working

KrakenSubscribeRequest.Details應該是多態的,但它不是,並且間隔屬性被拋在后面。 如果KrakenSubscribeRequest是多態的,它可以使用下面的代碼,但在這種情況下KrakenSubscribeRequest.Details是。

var request = new KrakenSubscribeRequest("ohlc", 1, "XBT/USD") { Details = new KrakenOhlcSubscriptionDetails(15) };

// Polymorphic serialization https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-polymorphism
var options = new JsonSerializerOptions
{
    WriteIndented = true
};
var json = JsonSerializer.Serialize(request, request.GetType(), options);

實際的

{
  "event": "subscribe",
  "reqid": 1,
  "pair": [
    "XBT/USD"
  ],
  "subscription": {
    "name": "ohlc"
  }
}

預期的

{
  "event": "subscribe",
  "reqid": 1,
  "pair": [
    "XBT/USD"
  ],
  "subscription": {
    "interval": 15,
    "name": "ohlc"
  }
}

對象

public record KrakenSubscribeRequest
{
    public KrakenSubscribeRequest(string topic, long requestId, params string[] symbols)
    {
        RequestId = requestId;
        Details = new KrakenSubscriptionDetails(topic);
        if (symbols.Any())
        {
            Symbols = symbols;
        }
    }

    [JsonPropertyName("event")]
    public string Event { get; init; } = "subscribe";

    [JsonPropertyName("reqid")]
    public long RequestId { get; init; }

    [JsonPropertyName("pair")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public string[]? Symbols { get; init; }

    [JsonPropertyName("subscription")]
    public KrakenSubscriptionDetails Details { get; init; }

    [JsonIgnore]
    public int? ChannelId { get; set; }
}

public record KrakenSubscriptionDetails
{
    public KrakenSubscriptionDetails(string topic)
    {
        Topic = topic;
    }

    [JsonPropertyName("name")]
    public string Topic { get; init; }

    [JsonIgnore]
    public virtual string ChannelName => Topic;
}

public record KrakenOhlcSubscriptionDetails : KrakenSubscriptionDetails
{
    public KrakenOhlcSubscriptionDetails(int interval) : base("ohlc")
    {
        Interval = interval;
    }

    [JsonPropertyName("interval")]
    public int Interval { get; init; }

    [JsonIgnore]
    public override string ChannelName => $"ohlc-{Interval}";
}

編輯

這解決了這個問題,但我想得到一個更通用的方法。

public class KrakenSubscriptionDetailsConverter : JsonConverter<KrakenSubscriptionDetails>
{
    /// <inheritdoc />
    public override bool CanConvert(Type objectType)
    {
        return typeof(KrakenSubscriptionDetails).IsAssignableFrom(objectType);
    }

    /// <inheritdoc />
    public override KrakenSubscriptionDetails Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }

    /// <inheritdoc />
    public override void Write(Utf8JsonWriter writer, KrakenSubscriptionDetails value, JsonSerializerOptions options)
    {
        JsonSerializer.Serialize(writer, value, value.GetType(), options);
    }
}

一種適用於任何類型的更簡單的方法是將對象轉換為 System.Object。 這利用了序列化 System.Object 時使用運行時類型這一事實。

JsonSerializer.Serialize((object)request, options);

但是,這只會將多態序列化應用於根對象。 如果多態對象不是根,而是存儲為根的屬性,則使用自定義轉換器很有用。 為了使該轉換器更通用,您可以給它一個通用類型。

class PolymoprphicConverter<T> : JsonConverter<T> {}

其余部分保持不變,但將 KrakenSubscriptionDetails 替換為 T。當您想要序列化時,只需將類型指定為泛型

class KrakenSubscriptionDetailsParent
{
    public KrakenSubscriptionDetails Details { get; set; }
}

KrakenSubscriptionDetailsParent parent = ...
options.Converters.Add(new PolymoprphicConverter<KrakenSubscriptionDetails>());
JsonSerializer.Serialize(parent, options);

這種方法的一個缺點是它不適用於父類型是子類型或從子類型派生的樹結構。 如果將選項傳遞給 JsonSerializer.Serialize,它將嘗試使用相同的轉換器來序列化對象,從而導致無限循環。 為防止這種情況,您必須確保在序列化之前從選項中刪除轉換器,以防止將轉換器應用於子級。

暫無
暫無

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

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