提问人:Philip Atz 提问时间:10/5/2023 更新时间:10/5/2023 访问量:66
如何使用 Newtonsoft.Json 序列化 castle 动态代理接口
How to serialize a castle dynamic proxy interface using Newtonsoft.Json
问:
我的应用程序接口有许多代理(我们称之为 ),它们是使用 Castle DynamicProxy 生成的,并填充了所有正确的值。代理对象的类型为 。当我尝试使用 Newtonsoft.Json 将这些代理序列化为 JSON 时,我希望得到一个与为以下定义定义的属性匹配的 JSON:IConfig
Castle.Proxies.IConfigProxy
IConfig
var json = JsonConvert.SerializeObject(proxy);
但是,我得到的是一个字符串,其中包含代理的内部工作原理,并且没有包含在以下数据中:IConfig
{
"__interceptors": [{}],
"__target": null,
"__interfaces": [],
"__baseType": "System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"__proxyGenerationOptions": {
"hook": {},
"selector": null,
"mixins": null,
"baseTypeForInterfaceProxy.AssemblyQualifiedName": "System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
},
"__proxyTypeId": "interface.without.target",
"__targetFieldType": "System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"__theInterface": "MyNamespace.IConfig, MyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
}
我遇到的最初问题是使用 Config.Net NuGet 包生成的动态代理。但是,我现在使用 Moq 在 LINQPad 中重新创建了一个最小的可重现样本。下面是一些生成类似结果的示例代码:
void Main()
{
var config = new Mock<IConfig>();
config.Setup(x => x.Key).Returns("test");
var proxy = config.Object;
var json = JsonConvert.SerializeObject(proxy, Newtonsoft.Json.Formatting.Indented);
json.Dump();
}
// Define other methods and classes here
public interface IConfig
{
string Key { get; set; }
}
对于这个特定问题,我将 .NET Framework 4.8 与最新版本的 Json.NET 一起使用(对于示例代码 Moq)。
有人可以帮忙解释这种行为并告诉我一种将这些对象转换为 JSON 的方法吗?
答:
问题的原因是,当您将声明类型的引用序列化为具有某种具体类型的对象时,Json.NET:IConfig
JsonConvert.SerializeObject<IConfig>(IConfig proxy);
Json.NET 将通过调用 来确定对象的实际具体类型,然后序列化该具体类型的所有属性 -- 并且可能比引用的声明类型多得多。(即声明的类型实际上没有使用)。proxy.GetType()
IConfig
若要对此进行调试,可以使用 Json.NET 的 DefaultContractResolver
来转储实际将序列化的属性:
var contract = (new DefaultContractResolver()).ResolveContract(proxy.GetType());
Console.WriteLine(proxy.GetType());
if (contract is JsonObjectContract objContract)
foreach (var property in objContract.Properties)
Console.WriteLine(" {0}: {1}", property.UnderlyingName, property.PropertyType);
这导致:
Castle.Proxies.IConfigProxy
Key: System.String
Mock: Moq.Mock
Moq.IMocked`1[IConfig].Mock: Moq.Mock`1[IConfig]
Interceptor: System.Object
正是这些额外的属性导致了问题。
在这里演示小提琴 #1。
作为解决方法,您可以切换到内置序列化程序 System.Text.Json,它仅序列化引用的声明类型的属性,除非声明的类型为 [1]:object
var json = System.Text.Json.JsonSerializer.Serialize<IConfig>(
proxy,
new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
在这里演示小提琴 #2。
或者,您可以使用“仅将接口属性序列化为 JSON Json.net”中的答案之一进行调查,以序列化代理 - 但使用内置序列化程序似乎更简单。
[1] 若要确认,请参阅如何使用 System.Text.Json 序列化派生类的属性。
评论
object
JsonSerializer.Serialize((object)proxy)