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

如何使用Newtonsoft.JSON将嵌套对象属性提取加前缀转为顶层属性

实现方案

推荐直接自定义 ContractResolver 即可满足需求,不需要额外搭配自定义JsonWriter。该方案可以完美兼容你已有的蛇形命名配置,完全不侵入原有业务类,也不需要创建额外包装类。

实现代码

首先实现自定义合约解析器和对应的值提供器:

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Reflection;

public class FlattenAddressContractResolver : DefaultContractResolver
{
    public FlattenAddressContractResolver()
    {
        // 复用你原有的蛇形命名策略,保持全局命名规则统一
        NamingStrategy = new SnakeCaseNamingStrategy();
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);

        // 仅针对Order类做属性打平处理
        if (type == typeof(Order))
        {
            var resultProps = new List<JsonProperty>();
            foreach (var prop in properties)
            {
                // 非Address类型的普通属性直接保留
                if (prop.PropertyType != typeof(Address))
                {
                    resultProps.Add(prop);
                    continue;
                }

                // 匹配Address属性,生成对应前缀
                var prefix = prop.UnderlyingName switch
                {
                    nameof(Order.FromAddress) => "from_",
                    nameof(Order.ToAddress) => "to_",
                    _ => string.Empty
                };

                if (string.IsNullOrEmpty(prefix))
                {
                    resultProps.Add(prop);
                    continue;
                }

                // 读取Address类的所有属性,生成带前缀的新属性映射
                var addressProps = base.CreateProperties(typeof(Address), memberSerialization);
                foreach (var addressProp in addressProps)
                {
                    var flattenProp = new JsonProperty
                    {
                        // 属性名加前缀后走蛇形命名处理,直接生成from_country这类符合要求的名称
                        PropertyName = NamingStrategy.GetPropertyName($"{prefix}{addressProp.UnderlyingName}", false),
                        PropertyType = addressProp.PropertyType,
                        ValueProvider = new NestedAddressValueProvider(prop.ValueProvider, addressProp.ValueProvider),
                        Readable = true,
                        Writable = true
                    };
                    resultProps.Add(flattenProp);
                }
            }
            return resultProps;
        }

        return properties;
    }
}

// 嵌套属性值提供器,用来读写Address对象的属性值
public class NestedAddressValueProvider : IValueProvider
{
    private readonly IValueProvider _parentPropProvider;
    private readonly IValueProvider _childPropProvider;

    public NestedAddressValueProvider(IValueProvider parentProvider, IValueProvider childProvider)
    {
        _parentPropProvider = parentProvider;
        _childPropProvider = childProvider;
    }

    // 序列化时读取值
    public object GetValue(object target)
    {
        var addressObj = _parentPropProvider.GetValue(target);
        return addressObj == null ? null : _childPropProvider.GetValue(addressObj);
    }

    // 反序列化时赋值,仅序列化需求可删除该方法实现
    public void SetValue(object target, object value)
    {
        var addressObj = _parentPropProvider.GetValue(target);
        if (addressObj == null)
        {
            addressObj = Activator.CreateInstance(typeof(Address));
            _parentPropProvider.SetValue(target, addressObj);
        }
        _childPropProvider.SetValue(addressObj, value);
    }
}

使用方法

序列化时直接指定该合约解析器即可:

var order = new Order
{
    FromAddress = new Address { Country = "US", Zip = "10001", State = "NY", City = "New York", Street = "1st St" },
    ToAddress = new Address { Country = "US", Zip = "90001", State = "CA", City = "Los Angeles", Street = "2nd St" },
    Amount = 10.00m
};

var serializeSettings = new JsonSerializerSettings
{
    ContractResolver = new FlattenAddressContractResolver(),
    Formatting = Formatting.Indented
};

// 序列化结果完全符合要求
string json = JsonConvert.SerializeObject(order, serializeSettings);

// 也支持反序列化
var deserializedOrder = JsonConvert.DeserializeObject<Order>(json, serializeSettings);

注意事项

  • 如果你原有项目已经存在自定义的ContractResolver,直接把上述CreateProperties的逻辑合并到你现有的解析器中即可,不需要重复定义
  • 若仅需要序列化能力,可删除NestedAddressValueProvider中SetValue方法的实现逻辑,减少冗余代码

内容的提问来源于stack exchange,提问作者James Parsons

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 13:24:02