【问题标题】:Is it possible to set custom (de)serializers for open generic types in ServiceStack.Text?是否可以在 ServiceStack.Text 中为开放的泛型类型设置自定义(反)序列化器?
【发布时间】:2014-07-05 12:23:13
【问题描述】:

我有一个这样的类型:

class Foo<T>
{
  public string Text { get; set; }

  public T Nested { get; set; }

  public static string ToJson(Foo<T> foo) { [...] }
}

ToJsonFoo&lt;Bar&gt; 实例序列化为 JSON,这是通过调整 JsConfig 无法实现的。另外ToJson依赖ServiceStack.Text序列化Nested,可以是Foo&lt;Baz&gt;的一个实例。

不幸的是,JsConfig 的实现方式意味着JsConfig&lt;T&gt; 将有一组静态变量用于Foo&lt;Bar&gt; 和其他Foo&lt;Baz&gt;。此外,AFAIK,ServiceStack.Text 无法为开放的泛型类型配置 JSON 序列化(例如:JsConfig.Add(typeof(Foo&lt;&gt;), config) 之类的东西)。我试图通过为Foo&lt;T&gt; 创建这个静态构造函数来解决这个问题:

static Foo() {
  JsConfig<Foo<T>>.RawSerializeFn = ToJson;
}

这并不总是有效。这取决于运行时调用静态构造函数的顺序。显然,ServiceStack.Text 缓存了序列化程序函数,有时会在调用静态构造函数之前根据 API 中调用的顺序操作进行缓存,所以:

var outer = new Foo<Baz> { Text = "text" };
outer.ToJson(); // OK, because Nested is null

var inner = new Foo<Bar>();
inner.ToJson(); // OK, because JsConfig<Foo<Bar>>.RawSerializeFn is Foo<T>.ToJson

outer.Nested = inner;
outer.ToJson(); // NOT OK, because SS.Text uses the default serializer for Foo<T>, not Foo<T>.ToJson

我无法预先设置JsConfig&lt;Foo&lt;T&gt;&gt; 中的所有序列化程序,因为 T 几乎可以是任何类型,甚至是其他泛型类型。

是否可以在 ServiceStack.Text 中为开放的泛型类型(可以嵌套)定义自定义序列化例程?

【问题讨论】:

  • ServiceStack 的 G+ 组或 GitHub 问题是否比 StackOverflow 更适合提出这个问题?如果你这么认为,请告诉我。

标签: c# json generics serialization servicestack-text


【解决方案1】:

我使用包装器和自定义反序列化器以自己的方式解决了这个问题。我为所有抽象类型创建了一个基本类型。该基本类型告诉系统它是哪个类型

public class SetSettingItemBase
{
    public string Key { get; set; }
    public string ValueType { get; set; }
}

所以基础本质上是元数据——设置键+值类型。然后,对象 DTO 只需通过添加实际值来扩展它:

public class SetSettingItem : SetSettingItemBase
{
    public object Value { get; set; }
}

请注意,它只是一个object。这是 DTO,不是我的实际对象。我可以稍后再转换它,或者在序列化后将其转换为真实/通用类型。

然后我的自定义序列化是:

JsConfig<SetSettingItem>.RawDeserializeFn = str =>
            {
                var baseSettings = str.FromJson<SetSettingItemBase>();
                var ret = baseSettings.MapTo<SetSettingItem>();

                if(true) // actual condition removed here... unimportant to the example
                {
                    var dataType = Constants.KnownSettingsTypes[baseSettings.ValueType];

                    var method = typeof(JsonExtensions).GetMethod("JsonTo").MakeGenericMethod(dataType);

                    var key = "Value";
                    var parsed = JsonObject.Parse(str);

                    if(parsed.Object(key) == null)
                        key = "value";

                    ret.Value = method.Invoke(null, new object[] { parsed, key });
                }
                return ret;
            };

此方法首先反序列化为简单基类。所以在反序列化baseSettings时,从DTO传入的Value会被忽略。然后我打电话给MapTo 来准备实际的SetSettingItem DTO。 MapTo 只是 AutoMapper 的包装。您可以在这里轻松使用 SS 的内置映射器。

为了安全起见,我有一组我允许作为设置的类型列表。示例:

KnownSettingsTypes.Add("string", typeof(string));
KnownSettingsTypes.Add("int", typeof(int));
KnownSettingsTypes.Add("nullableint", typeof(int?));
KnownSettingsTypes.Add("nullablepercentage", typeof(double?));
KnownSettingsTypes.Add("feegrid", typeof(FeeGrid));

之后,我使用反射获取JsonTo方法,从KnownSettingsTypes字典中动态传入泛型类型参数。

然后完成所有操作,我使用通用 JsonObject.Parse 方法解析对象,然后查找 Valuevalue(取决于大小写敏感度)并使用动态 @ 显式转换 JsonObject 987654337@我之前创建的。

最终结果是我可以在这里传递所有不同类型的设置作为我的 DTO 的一部分。

这暂时符合我的目的,但看看这个例子,我可以看到它在两个方面有所改进:

  1. 解析后,我可以将我的SetSettingItem 转换为SettingItem&lt;T&gt;,这样我就可以在我的代码中将它用作强类型对象。请记住,此示例仅供 DTO 使用。
  2. 我可以检查设置Key 以了解它应该是哪种类型并相应地解析,而不是要求该人传递type 以供我检查。在我的示例中,即使我检查了设置及其类型的主列表,我仍然可能会要求它们传入 type 以防万一,如果它们不匹配则抛出异常。

【讨论】:

  • 您必须用 SetSettingItem 包装每个 SettingItem 吗?难道你不能使用 __type (JsConfig&lt;T&gt;.IncludeTypeInfo) 字段在没有包装器的情况下获得相同的结果吗?
  • 是的,但我想要更多的控制权。我不希望 Type 以 [System.Nullable``1[[System.Int32]]] 之类的形式出现。我想要一个漂亮、干净的类型。此外,正如我在回答中所说,我最终可能会从等式中取出类型并根据Key 查找它,从而使类型过时。
  • 我可以在每个具有Foo&lt;T&gt; 的DTO 中用Foo 包装每个Foo&lt;T&gt; 并设置JsConfig&lt;Foo&gt;.RawSerializeFn。这可能行得通,但是我不能使用强类型成员,这是拥有Foo&lt;T&gt; 而不仅仅是Foo 的目的。
  • 我会将其包装在 FooDto 而不是 Foo 中。对 DTO 进行序列化/反序列化后,您可以使用另一个进程将其转换为 Foo&lt;T&gt; 进行处理。我在这里试图做出的区别是,我描述的方法是为了帮助序列化过程,而 NOT 是为了在我的应用程序中处理数据。如果你同意这一点,但这仍然没有帮助,那么也许我不明白你想要做什么。
  • 请不要误会我的意思:我认为这个答案非常有用,但您不是在序列化Foo&lt;T&gt;,而是在序列化一个包装器。不涉及包装泛型类型的每个实例的答案会更加优雅,这就是我正在寻找的。​​span>
猜你喜欢
  • 1970-01-01
  • 2012-02-27
  • 2014-08-08
  • 2016-07-09
  • 2020-05-28
  • 2019-08-23
  • 2017-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多