3

我有这样的类型:

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

  public T Nested { get; set; }

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

ToJsonFoo<Bar>一种无法通过调整JsConfig. 此外,ToJson依赖 ServiceStack.Text 来序列化Nested,它可以是Foo<Baz>.

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

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<Foo<T>>预先设置所有的序列化器,因为 T 几乎可以是任何类型,甚至是其他泛型类型。

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

4

1 回答 1

2

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

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;
            };

此方法首先反序列化为简单的基础。因此,Value在反序列化时会忽略从 DTO 传入的内容baseSettings。然后我打电话MapTo准备实际的SetSettingItemDTO。 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方法解析对象,然后寻找Valueor value(取决于大小写敏感度)并JsonObject使用我之前创建的动态显式转换它method

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

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

  1. 解析后,我可以将 my 转换SetSettingItem为 a SettingItem<T>,以便在我的代码中将其用作强类型对象。请记住,此示例仅用于 DTO 将其通过网络传输。
  2. 我可以检查设置以了解它应该是哪种类型并相应地解析,而不是要求该人通过type我来检查。Key在我的示例中,即使我检查了设置及其类型的主列表,我仍然可能会要求它们传入type作为预防措施,如果它们不匹配则抛出异常。
于 2014-05-20T13:11:28.207 回答