-1

我正在使用 c# 开发 ActivityPub 实现,有时链接是像 url 链接一样的“字符串”,有时链接是具有 Link 子类型的对象。(链接:实体)

我想知道如果一组条件为真(只需将一个字符串写入编写器),是否有可能使用 System.Text.Json 将 Link 对象序列化为字符串,并将整个默认对象写入如果条件不成立,请编写器。

我试过遵循这个解决方案:How to use default serialization in a custom System.Text.Json JsonConverter? ,它仍然适用于代码小提琴,但不适用于我的实施,我不太清楚为什么。

有谁知道我可以如何调试它,或者更好的方法来确保Link : Entity对象有时可以序列化为字符串?

有了这个我得到以下错误:在此处输入图像描述

(在这种情况下,我什至尝试将获取的默认 ctor 添加到 modifiedOptions) Reguardless,它表示没有为 Link 类映射数据。我还尝试将 JsonSerializeable 属性直接添加到 Link 类。

错误: 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() }
      );
    }
4

1 回答 1

2

在我的原始版本中DefaultConverterFactory<T>,我缓存了默认转换器,因为在其文档How to write custom converters for JSON serialization (marshalling) in .NET 中,Microsoft 建议在序列化复杂对象时出于性能原因缓存任何所需的转换器:

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. 现在看来您可能遇到了第三个问题:使用编译时序列化程序源生成时,返回的转换器可能无法正常工作。

这足以证明无视微软的建议。简单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>,从and中删除最后一个参数,您的代码现在应该可以工作了。JsonConverter<T> defaultConverterRead()Write()

(顺便说一句,您似乎使用[JsonSerializable(typeof(Link))]错误。您正在将其应用于您的模型类Link,但根据文档,它应该应用于JsonSerializerContext您的模型的某个子类——而不是模型本身。)

于 2021-12-10T17:46:11.813 回答