You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 10:36:31