如何使用Newtonsoft.Json序列化Castle动态代理接口
问题
为应用的IConfig接口生成了多个Castle DynamicProxy代理对象(类型为Castle.Proxies.IConfigProxy),且已填充正确值。使用Newtonsoft.Json序列化这些代理时,期望得到匹配IConfig属性的JSON,但实际得到的是代理内部结构,未包含IConfig的数据。用Moq在LINQPad中复现了该问题,环境为.NET Framework 4.8及最新版Json.NET、Moq。
复现代码
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; } }
错误序列化结果
{ "__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" }
原因分析
Newtonsoft.Json默认会序列化对象的实际类型而非接口类型。Moq(底层基于Castle DynamicProxy)生成的代理对象本质是继承自Castle.Proxies.IConfigProxy的动态类型,这个类型包含大量代理内部的字段(比如__interceptors、__target),而接口IConfig的属性是通过拦截器动态实现的,并非代理对象的真实字段,所以默认序列化会直接输出代理的内部结构,而非接口属性值。
解决方法
方案1:序列化时指定接口类型
序列化时通过JsonConvert.SerializeObject的重载,指定要序列化的类型为IConfig,让Json.NET按照接口的属性来序列化:
var json = JsonConvert.SerializeObject(proxy, typeof(IConfig), Newtonsoft.Json.Formatting.Indented);
执行后得到期望结果:
{ "Key": "test" }
方案2:创建自定义JsonConverter
如果需要批量处理这类代理对象,可以自定义一个转换器,自动识别Castle代理并按接口序列化:
public class CastleProxyConverter : JsonConverter { public override bool CanConvert(Type objectType) { // 判断是否是Castle动态代理 return objectType.FullName != null && objectType.FullName.StartsWith("Castle.Proxies."); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // 反序列化场景可按需实现,此处暂略 throw new NotImplementedException("反序列化未实现"); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // 获取代理实现的目标接口 var targetInterface = value.GetType().GetInterfaces().First(); // 按接口类型序列化 serializer.Serialize(writer, value, targetInterface); } }
使用时注册转换器:
var settings = new JsonSerializerSettings(); settings.Converters.Add(new CastleProxyConverter()); var json = JsonConvert.SerializeObject(proxy, Formatting.Indented, settings);
方案3:转换为匿名对象序列化
如果不需要保留代理类型信息,可以手动将接口属性映射到匿名对象后序列化,适合属性较少的场景:
var configDto = new { Key = proxy.Key }; var json = JsonConvert.SerializeObject(configDto, Formatting.Indented);
内容的提问来源于stack exchange,提问作者Philip Atz
相关产品推荐
相关产品推荐

