簡體   English   中英

使用 System.Text.Json 將 Object 有條件地序列化為單個字符串

[英]use System.Text.Json to serialize an Object as a single String conditionally

我正在研究 c# 中的 ActivityPub 實現,有時鏈接是“字符串”,如 url 鏈接,有時鏈接是具有鏈接子類型的對象。 (鏈接:實體)

我想知道是否有一種可能的方法可以使用 System.Text.Json 將鏈接 object 序列化為字符串,如果一組條件為真(只需向編寫器寫入一個字符串),並將整個默認 ZA8CFDE63931BD59EB62ACZF4B 寫入如果條件不成立,請編寫器。

我試過遵循這個解決方案: How to use default serialization in a custom System.Text.Json JsonConverter? ,它仍然適用於代碼小提琴,但不適用於我的實施,我不太清楚為什么。

有誰知道我可以如何調試它,或者 go 的更好方法是確保Link: Entity對象有時可以序列化為字符串?

有了這個我得到以下錯誤: 在此處輸入圖像描述

(在這種情況下,我什至嘗試將獲取的默認 ctor 添加到 modifiedOptions) Reguardless,它說沒有為鏈接 class 映射的數據。 我還嘗試將 JsonSerializeable 屬性直接添加到鏈接 class。

錯誤: Metadata for type 'ActivityPub.Types.Link' was not provided to the serializer. The serializer method used does not support reflection-based creation of serialization-related type metadata. If using source generation, ensure that all root types passed to the serializer have been indicated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically. Metadata for type 'ActivityPub.Types.Link' was not provided to the serializer. The serializer method used does not support reflection-based creation of serialization-related type metadata. If using source generation, ensure that all root types passed to the serializer have been indicated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically.

我的基本代碼庫: https://github.com/Meep-Tech/ActivityHub.Net/tree/collapse_links_during_serialization

測試代碼:

static void Main(string[] args) {
      Settings.DefaultContext = new Link("ActivityPub.Net.Testing");

      var testObject = new Object {
        Type = "Test",
        At = new Link("/terry") {
          Rels = new string[] {
            "test",
            "test2"
          }
        },
        Attribution = "/meep",
        Audience = new Link("/all") {
          Rel = "test"
        }
      };

      string json = testObject
        .Serialize();

      System.IO.File.WriteAllLines(
        "test.json",
        new[] { json }
      );

      Object @object = json.DeSerializeEntity<Object>();
      System.IO.File.WriteAllLines(
        "test1.json",
        new[] { @object.ToString() }
      );
    }

In my original version of DefaultConverterFactory<T> , I cached the default converter because, in its documentation How to write custom converters for JSON serialization (marshalling) in .NET , Microsoft recommends, when serializing a complex object, to cache any required converters for performance原因:

 public DictionaryEnumConverterInner(JsonSerializerOptions options) { // For performance, use the existing converter if available. _valueConverter = (JsonConverter<TValue>)options.GetConverter(typeof(TValue)); // Cache the key and value types. _keyType = typeof(TKey); _valueType = typeof(TValue); }

然而,由於以下幾個原因,這已被證明是有問題的:

  1. 當序列化聲明類型為object的多態值時, GetConverter()返回非功能轉換器。

  2. 序列化數值時,返回的轉換器忽略NumberHandling設置。

  3. 現在看來您遇到了第三個問題:使用compile-time serializer source generation時,返回的轉換器可能無法正常工作。

這足以證明無視微軟的建議。 簡單的DefaultConverter<T>如下:

public abstract class DefaultConverterFactory<T> : JsonConverterFactory
{
    class DefaultConverter : JsonConverter<T>
    {
        readonly JsonSerializerOptions modifiedOptions;
        readonly DefaultConverterFactory<T> factory;

        public DefaultConverter(JsonSerializerOptions options, DefaultConverterFactory<T> factory)
        {
            this.factory = factory;
            this.modifiedOptions = options.CopyAndRemoveConverter(factory.GetType());
        }

        public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => factory.Write(writer, value, modifiedOptions);

        public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => factory.Read(ref reader, typeToConvert, modifiedOptions);
    }

    protected virtual T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions)
        => (T)JsonSerializer.Deserialize(ref reader, typeToConvert, modifiedOptions);

    protected virtual void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions modifiedOptions) 
        => JsonSerializer.Serialize(writer, value, modifiedOptions);

    public override bool CanConvert(Type typeToConvert) => typeof(T) == typeToConvert;

    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new DefaultConverter(options, this);
}

public static class JsonSerializerExtensions
{
    public static JsonSerializerOptions CopyAndRemoveConverter(this JsonSerializerOptions options, Type converterType)
    {
        var copy = new JsonSerializerOptions(options);
        for (var i = copy.Converters.Count - 1; i >= 0; i--)
            if (copy.Converters[i].GetType() == converterType)
                copy.Converters.RemoveAt(i);
        return copy;
    }
}

然后,在從DefaultConverterFactory<T>派生的任何類中,例如這里的這個,從Read()Write()中刪除最后一個參數JsonConverter<T> defaultConverter ,您的代碼現在應該可以工作了。

暫無
暫無

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

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