我有这样的类型:
class Foo<T>
{
public string Text { get; set; }
public T Nested { get; set; }
public static string ToJson(Foo<T> foo) { [...] }
}
ToJson
以一种无法通过调整Foo<Bar>
无法实现的方式将JsConfig
实例序列化为JSON。此外,ToJson
依赖ServiceStack.Text来序列化Nested
,Foo<Baz>
可以是JsConfig
的实例。
不幸的是,实施JsConfig<T>
的方式意味着Foo<Bar>
的{{1}}静态变量和Foo<Baz>
的其他静态变量都有JsConfig.Add(typeof(Foo<>), config)
个。此外,AFAIK,ServiceStack.Text无法为开放泛型类型配置JSON序列化(即:Foo<T>
之类的东西)。我试图通过为static Foo() {
JsConfig<Foo<T>>.RawSerializeFn = ToJson;
}
:
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
这并不是一直有效。它取决于运行时调用静态构造函数的顺序。显然,ServiceStack.Text缓存序列化函数,有时在调用静态构造函数之前执行此操作,具体取决于API中调用的顺序操作,因此:
JsConfig<Foo<T>>
我无法预先在{{1}}中设置所有序列化程序,因为T几乎可以是任何类型,甚至是其他类型的。
是否可以在ServiceStack.Text中为开放泛型类型(可以嵌套)定义自定义序列化例程?
答案 0 :(得分: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
准备实际的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
方法解析对象,然后查找Value
或value
(取决于区分大小写)并显式转换{ {1}}使用我之前创建的动态JsonObject
。
最终结果是我可以在这里传递所有不同类型的设置,作为我的DTO的一部分。
这暂时满足了我的目的,但看看这个例子我可以看到它有两种改进:
method
转换为SetSettingItem
,这样我就可以在代码中将其用作强类型对象。请记住,这个例子只是让DTO通过网络获得它。SettingItem<T>
以确定它应该是哪种类型并进行相应的解析,而不是要求此人传递type
以供我检查。在我的示例中,即使我检查设置的主列表及其类型,我仍然可能要求他们传递Key
作为预防措施,并且如果他们没有这样做则会抛出异常。 t match。