我有一个API客户端,该客户端从ASP.NET Web API接收JSON序列化的数据。序列化和反序列化使用JSON.Net进行。序列化的JSON使用TypeNameHandling = TypeNameHandling.Objects
包含类型信息,在正常使用情况下可以正常工作。
问题来自域模型在服务器端经常更改,但使用者不一定立即提取这些更新。
发生这种情况时,使用者请求一个模型列表,但是序列化会引发错误,即使许多返回对象的列表中只有一个是未知的,使用者也根本看不到任何返回的模型。
我知道我可以用ErrorContext.Handled = true;
吞下该错误,但这可以吞并每个错误,我并不是很想要。
我想要将这些“无法识别”的项目序列化为明确定义的未知项目。如果更现实的话,我也愿意将它们序列化为最基本的类(尽管现在是抽象的)。
我希望该解决方案具有灵活性,因为将新类型(在下面的域示例中为水果的新类型)添加到域中时,只需将其添加一次即可。我不想在序列化特定的类中手动注册水果。
最后,我希望能够保留对其他类型未处理错误的可见性,以防弹出窗口。
这是一个示例-在简化的示例域中请允许我使用
public class FruitResponse
{
public int ResponseId {get; set;}
public Fruit Fruit {get; set;}
}
public abstract class Fruit
{
public string Color {get; set;}
public decimal Weight {get; set;}
}
public class Banana : Fruit
{
public decimal Length {get; set;}
}
public class Apple : Fruit
{
public int WormCount {get; set;}
}
public class UnknownFruit : Fruit
{
//whatever
}
现在,假设客户端请求水果,服务器返回FruitResponse
对象的列表。只要这些对象中的水果是苹果和香蕉,就没有问题。但是,如果在服务器端添加了新的水果类型“ Orange”,并且返回的JSON的$type
属性反映了这一点,则请求将失败。
我希望将Orange(以及任何潜在的芒果,草莓,以及其他任何东西)反序列化为UnknownFruit。这样,消费者就知道还有多余的水果,可以看到那些对象(FruitId
)的FruitResponse属性以及基本的水果属性。结果,服务器端的新添加不会为客户端带来重大变化。来自服务器的任何种类的Orange特定属性都可以忽略。
很明显,我只希望这种行为适用于水果。它们都将从Fruit
基类继承,并且都将在Fruit命名空间中。
我知道所有新的水果都会添加到同一个名称空间中,因此我想知道反序列化时是否有一种方法可以在$type
字段中进行检查。
如果返回了Vegetable
或Foo
,那么序列化失败是可以的。
我尝试使用
在自定义ISerializationBinder
内捕获错误
public Type BindToType(string assemblyName, string typeName)
{
try
{
return binder.BindToType(assemblyName, typeName);
}
catch (Exception ex)
{
return typeof(UnknownFruit);
}
}
但是,这不仅显得有些古怪(使用控制流的异常),而且也不起作用;我收到此错误(忽略奇怪的命名空间,这是我在linqpad中玩耍的原因):
JSON'UserQuery + UnknownFruit,query_tykmkh,Version = 0.0.0.0,Culture = neutral,PublicKeyToken = null'中指定的类型与'UserQuery + Fruit,query_rraqao,Version = 0.0.0.0,Culture = neutral,PublicKeyToken不兼容= null”。路径“ Instrument。$ type”,第1行,位置124。
它也不允许我探查$type
来查看它是否在正确的水果名称空间中。
我还知道我可以反序列化为一个expando对象,但是我想使最终结果具有强类型。
这里正确的方法似乎是使用自定义JsonConverter
,但是我看到的使用该选项的方法涉及到我要避免的转换器手动注册模型。
我确实可以控制服务器和客户端代码,因此我可以在任何一方实施任何潜在的更改。
任何帮助将不胜感激。