如何使用Newtonsoft JSON.NET根据枚举动态修改JSON属性名
嘿,这个需求我之前也碰到过,用Newtonsoft JSON.NET完全可以搞定,给你两种实用的实现思路,按需选就行:
1. 先准备基础定义
首先得把枚举和各个支付方式的详情模型定义好,强类型处理更安全:
// 支付方式枚举 public enum PaymentMethodEnum { Cash, Card, Debit, Check } // 各支付方式的详情类 public class CashDetails { } // Cash没有额外属性,空类即可 public class CardDetails { public string Authorization { get; set; } public string Bin { get; set; } public string Reference { get; set; } } public class DebitDetails { public string DocumentType { get; set; } public string DocumentNumber { get; set; } public string Account { get; set; } } public class CheckDetails { // 根据实际需求补充Check的属性,比如支票号、银行代码等 public string CheckNumber { get; set; } public string BankCode { get; set; } }
2. 方式一:手动构建JObject(简单直观,适合快速实现)
这种方式直接根据枚举值动态拼接JSON结构,不需要额外配置序列化器:
public static string GeneratePaymentJson(PaymentMethodEnum paymentType, object paymentDetails) { var rootObj = new JObject(); var paymentMethodObj = new JObject(); switch (paymentType) { case PaymentMethodEnum.Cash: // Cash可以传null,默认生成空对象 paymentMethodObj.Add("Cash", JObject.FromObject(paymentDetails ?? new CashDetails())); break; case PaymentMethodEnum.Card: // 校验传入的详情类型,避免错误 if (paymentDetails is not CardDetails cardDetails) throw new ArgumentException("Card支付必须传入CardDetails对象"); paymentMethodObj.Add("Card", JObject.FromObject(cardDetails)); break; case PaymentMethodEnum.Debit: if (paymentDetails is not DebitDetails debitDetails) throw new ArgumentException("Debit支付必须传入DebitDetails对象"); paymentMethodObj.Add("Debit", JObject.FromObject(debitDetails)); break; case PaymentMethodEnum.Check: if (paymentDetails is not CheckDetails checkDetails) throw new ArgumentException("Check支付必须传入CheckDetails对象"); paymentMethodObj.Add("Check", JObject.FromObject(checkDetails)); break; default: throw new NotImplementedException($"不支持的支付方式:{paymentType}"); } rootObj.Add("PaymentMethod", paymentMethodObj); // 格式化输出,方便阅读,不需要的话可以用Formatting.None return rootObj.ToString(Formatting.Indented); }
使用示例:
// Cash示例 var cashJson = GeneratePaymentJson(PaymentMethodEnum.Cash, null); // 输出结果:{"PaymentMethod": {"Cash": {}}} // Card示例 var cardInfo = new CardDetails { Authorization = "####", Bin = "####", Reference = "00#####" }; var cardJson = GeneratePaymentJson(PaymentMethodEnum.Card, cardInfo); // Debit示例 var debitInfo = new DebitDetails { DocumentType = "DNI", DocumentNumber = "##########3", Account = "xxxx-####" }; var debitJson = GeneratePaymentJson(PaymentMethodEnum.Debit, debitInfo);
3. 方式二:自定义JsonConverter(适合序列化完整请求对象)
如果你的业务是围绕一个包含支付类型和详情的主对象展开,自定义转换器可以让序列化过程更自动化:
首先定义主请求类:
public class PaymentRequest { public PaymentMethodEnum PaymentType { get; set; } public object PaymentDetails { get; set; } }
然后实现自定义转换器:
public class PaymentRequestConverter : JsonConverter<PaymentRequest> { // 序列化逻辑:把PaymentRequest转成目标JSON结构 public override void WriteJson(JsonWriter writer, PaymentRequest value, JsonSerializer serializer) { var rootObj = new JObject(); var paymentMethodObj = new JObject(); switch (value.PaymentType) { case PaymentMethodEnum.Cash: paymentMethodObj.Add("Cash", value.PaymentDetails == null ? new JObject() : JObject.FromObject(value.PaymentDetails)); break; case PaymentMethodEnum.Card: if (value.PaymentDetails is not CardDetails card) throw new JsonSerializationException("Card支付需要传入CardDetails类型的详情"); paymentMethodObj.Add("Card", JObject.FromObject(card)); break; case PaymentMethodEnum.Debit: if (value.PaymentDetails is not DebitDetails debit) throw new JsonSerializationException("Debit支付需要传入DebitDetails类型的详情"); paymentMethodObj.Add("Debit", JObject.FromObject(debit)); break; case PaymentMethodEnum.Check: if (value.PaymentDetails is not CheckDetails check) throw new JsonSerializationException("Check支付需要传入CheckDetails类型的详情"); paymentMethodObj.Add("Check", JObject.FromObject(check)); break; default: throw new JsonSerializationException($"不支持的支付方式:{value.PaymentType}"); } rootObj.Add("PaymentMethod", paymentMethodObj); rootObj.WriteTo(writer); } // 反序列化逻辑:把JSON转回PaymentRequest对象(可选,根据业务需求实现) public override PaymentRequest ReadJson(JsonReader reader, Type objectType, PaymentRequest existingValue, bool hasExistingValue, JsonSerializer serializer) { var rootObj = JObject.Load(reader); var paymentMethodObj = (JObject)rootObj["PaymentMethod"]; // 获取PaymentMethod下的唯一键,判断支付类型 var paymentTypeKey = paymentMethodObj.Properties().First().Name; var paymentType = Enum.Parse<PaymentMethodEnum>(paymentTypeKey); var request = new PaymentRequest { PaymentType = paymentType }; // 根据支付类型反序列化对应的详情 switch (paymentType) { case PaymentMethodEnum.Cash: request.PaymentDetails = paymentMethodObj["Cash"].ToObject<CashDetails>(); break; case PaymentMethodEnum.Card: request.PaymentDetails = paymentMethodObj["Card"].ToObject<CardDetails>(); break; case PaymentMethodEnum.Debit: request.PaymentDetails = paymentMethodObj["Debit"].ToObject<DebitDetails>(); break; case PaymentMethodEnum.Check: request.PaymentDetails = paymentMethodObj["Check"].ToObject<CheckDetails>(); break; } return request; } }
使用示例:
var paymentRequest = new PaymentRequest { PaymentType = PaymentMethodEnum.Debit, PaymentDetails = new DebitDetails { DocumentType = "DNI", DocumentNumber = "##########3", Account = "xxxx-####" } }; var serializerSettings = new JsonSerializerSettings { Converters = new List<JsonConverter> { new PaymentRequestConverter() }, Formatting = Formatting.Indented }; var json = JsonConvert.SerializeObject(paymentRequest, serializerSettings); // 输出就是你要的Debit对应的JSON结构
小提示
- 如果后续要扩展Check的属性,只需要修改
CheckDetails类,然后在switch分支里对应处理就行,扩展性很好 - 手动构建JObject的方式适合简单场景,自定义Converter适合需要序列化/反序列化完整业务对象的场景
- 可以根据业务需求添加更多的参数校验,避免非法输入导致的异常
内容的提问来源于stack exchange,提问作者Leo Jim
相关产品推荐
相关产品推荐

