Json.NET反序列化为接口实现

3
我正在尝试使用 Json.NET 对一个类进行序列化,以便通过http请求进行传输。这是一个客户端服务器测试程序,共享一些常见的类文件。这些文件包括接口(ITestCase)和该接口的实现(TestPictureTestVideo)。

在同一应用程序中对 testCase 进行序列化和反序列化工作正常,可能是因为 Json.NET 代码都包含在一个程序集中。但当我对 testCase 进行序列化、发送到服务器,然后尝试进行反序列化时,会出现以下错误:

"Error resolving type specified in JSON 'com.test.testcases.TestPicture, Graphics Tester'. Path '$type', line 2, position 61"

并附带一个类型为 JsonSerializationException 的内部异常消息:

"Could not load assembly 'Graphics Tester'."

Json.NET文档中,当生成Json时,$type值为"$type": "Newtonsoft.Json.Samples.Stockholder, Newtonsoft.Json.Tests"。第二个参数似乎是通过命名空间引用相关类,而不是像我的实例中那样通过项目名称(即Graphics Tester)引用。
鉴于两个项目都共享所需的类,并且文件位于同一个命名空间中,为什么Json.NET要查找程序集而不是类本身?
以下是我的代码框架;详细信息被省略,因为它们对问题没有帮助。显示的是接口以及该接口的两个实现。还显示了序列化和反序列化操作以及生成的Json。
ITestCase testCase = new TestPicture("test.jpg")

string json = JsonConvert.SerializeObject(testCase, Formatting.Indented, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Objects,
});

ITestCase instance = JsonConvert.DeserializeObject<ITestCase>(json, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Objects,
});


//value in variable json after serialisation
//{
//  "$type": "com.test.testcases.TestPicture, Graphics Tester",
//  "filename": "test.jpg",
//  "testPoints": null
//}

单独的类文件:

namespace com.test.testcases
{
    interface ITestCase
    {
        void Run();
        bool Verify();
    }
}

namespace com.test.testcases
{
    class TestPicture : ITestCase {}
}

namespace com.test.testcases
{
    class TestVideo : ITestCase {}
}

我的解决方案:

比我预期的要简单的解决方案存在。它并不是最优的,但肯定可行。我更倾向于将其归类为一种变通方法或者hack。通过修改项目属性并将程序集名称设置为两个项目中相同的名称,它将找到已经包含在项目中的类,从而创建由Json字符串指定的对象。

1个回答

3

$type编码了完全限定的类型名称,如果客户端和服务器没有使用相同的项目定义这些类型(即每个人都在自己的地方定义类型,而不是引用一个公共程序集),那么它们之间将会有所不同。

在您的情况下,在客户端中完全限定名称为com.test.testcases.TestPicture, Graphics Tester,但当它到达服务器时,服务器找不到名为Graphics Tester的任何程序集。

您可以通过以下两种方式解决此问题:

  1. 为可序列化类型定义一个公共程序集,并从客户端和服务器引用它。
  2. 在客户端和服务器上分别定义类型,并通过提供自定义的SerializationBinderJsonSerializerSettings.Binder来自定义 Json.NET 的类型解析,其中您可以实现自己的逻辑。

谢谢。我尝试避免使用另一个二进制文件,因为我处于一种特殊情况下,我正在使用Mono来使我的应用程序跨平台。请参见上面的编辑以获取我的解决方案。 - sabrehagen
1
我强烈建议您使用自定义的SerializationBinder,而不是更改程序集名称。编写一个SerializationBinder来仅基于类型名称查找类型(例如,“在当前程序集中查找具有此名称的类型”)相对容易,它不需要另一个二进制文件,并且比更改一个项目的名称以匹配另一个项目更少地被视为一种hack。 - Avish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接