如何用Newtonsoft.Json实现C#对象的双规则序列化适配多客户端
针对内外客户端实现不同的Newtonsoft.Json序列化/反序列化规则
针对内部、外部客户端需要不同JSON字段名的需求,用Newtonsoft.Json可以通过两种常用方案实现,同时保留双向序列化能力:
方案一:自定义契约解析器(不修改原类)
如果不能修改现有C#类的代码,或者需要批量管理属性名映射,推荐用这种方式。核心是创建自定义ContractResolver,在序列化时动态替换指定属性名。
实现步骤
- 定义原类:
public class MyClass { public string Customer { get; set; } }
- 编写外部客户端专用的契约解析器:
public class ExternalContractResolver : DefaultContractResolver { // 维护类-属性名映射表,按需扩展 private readonly Dictionary<Type, Dictionary<string, string>> _propertyMappings = new() { { typeof(MyClass), new Dictionary<string, string> { { nameof(MyClass.Customer), "Mandant" } } } }; protected override string ResolvePropertyName(string propertyName) { // 检查当前处理的类型是否有属性映射,有则返回外部字段名,否则用原属性名 if (_propertyMappings.TryGetValue(CurrentType, out var mappings) && mappings.TryGetValue(propertyName, out var newName)) { return newName; } return base.ResolvePropertyName(propertyName); } }
- 序列化/反序列化调用:
var myObj = new MyClass { Customer = "MyCustomer" }; // 内部客户端:用默认配置,直接输出原属性名 var internalSettings = new JsonSerializerSettings(); string internalJson = JsonConvert.SerializeObject(myObj, Formatting.Indented, internalSettings); // 输出: // { // "Customer": "MyCustomer" // } // 外部客户端:使用自定义契约解析器替换字段名 var externalSettings = new JsonSerializerSettings { ContractResolver = new ExternalContractResolver() }; string externalJson = JsonConvert.SerializeObject(myObj, Formatting.Indented, externalSettings); // 输出: // { // "Mandant": "MyCustomer" // } // 反序列化示例 string internalInput = "{\"Customer\": \"NewCustomer\"}"; MyClass internalResult = JsonConvert.DeserializeObject<MyClass>(internalInput, internalSettings); string externalInput = "{\"Mandant\": \"NewCustomer\"}"; MyClass externalResult = JsonConvert.DeserializeObject<MyClass>(externalInput, externalSettings);
方案二:JsonProperty标记+内部专用解析器(修改原类)
如果允许修改C#类的代码,这种方式更简洁。给属性标记外部用的字段名,然后用内部解析器强制使用原属性名。
实现步骤
- 给类属性标记外部字段名:
public class MyClass { [JsonProperty("Mandant")] public string Customer { get; set; } }
- 编写内部客户端专用的契约解析器:
public class InternalContractResolver : DefaultContractResolver { protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var property = base.CreateProperty(member, memberSerialization); // 忽略JsonProperty指定的名称,强制使用属性本身的名称 property.PropertyName = member.Name; return property; } }
- 序列化/反序列化调用:
var myObj = new MyClass { Customer = "MyCustomer" }; // 内部客户端:用自定义解析器输出原属性名 var internalSettings = new JsonSerializerSettings { ContractResolver = new InternalContractResolver() }; string internalJson = JsonConvert.SerializeObject(myObj, Formatting.Indented, internalSettings); // 输出:{"Customer": "MyCustomer"} // 外部客户端:直接用默认配置,输出JsonProperty标记的名称 var externalSettings = new JsonSerializerSettings(); string externalJson = JsonConvert.SerializeObject(myObj, Formatting.Indented, externalSettings); // 输出:{"Mandant": "MyCustomer"} // 反序列化同样适用 string internalInput = "{\"Customer\": \"NewCustomer\"}"; MyClass internalResult = JsonConvert.DeserializeObject<MyClass>(internalInput, internalSettings); string externalInput = "{\"Mandant\": \"NewCustomer\"}"; MyClass externalResult = JsonConvert.DeserializeObject<MyClass>(externalInput, externalSettings);
方案选择
- 若原类不可修改,或需统一管理大量属性映射,选方案一
- 若原类可修改,且仅少数属性需要重命名,选方案二,代码更简洁
内容的提问来源于stack exchange,提问作者mathisplewa
相关产品推荐
相关产品推荐

