可否扩展Newtonsoft.Json的JsonProperty特性新增自定义序列化属性?
原生JsonPropertyAttribute是密封类,无法直接继承扩展新增自定义属性,但可以通过「自定义多场景名称特性+对应场景合约解析器」的方案实现你要的多场景序列化字段名需求,完全可以达到你期望的使用效果。
步骤1:自定义多场景序列化名称特性
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class JsonMultiSceneNameAttribute : Attribute { // Solver场景对应序列化字段名 public string SolverPropertyName { get; set; } // API场景对应序列化字段名 public string APIPropertyName { get; set; } // 后续新增场景只需在此处加对应属性即可 }
步骤2:实现不同场景的定制合约解析器
Solver场景解析器
序列化时优先使用标记的SolverPropertyName作为字段名:
public class SolverSceneContractResolver : DefaultContractResolver { protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { var properties = base.CreateProperties(type, memberSerialization); foreach (var prop in properties) { // 获取属性上标记的自定义多场景特性 var multiNameAttr = prop.AttributeProvider .GetAttributes(typeof(JsonMultiSceneNameAttribute), false) .Cast<JsonMultiSceneNameAttribute>() .FirstOrDefault(); if (multiNameAttr != null && !string.IsNullOrWhiteSpace(multiNameAttr.SolverPropertyName)) { prop.PropertyName = multiNameAttr.SolverPropertyName; } } return properties; } }
API场景解析器
序列化时优先使用标记的APIPropertyName作为字段名:
public class APISceneContractResolver : DefaultContractResolver { protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { var properties = base.CreateProperties(type, memberSerialization); foreach (var prop in properties) { var multiNameAttr = prop.AttributeProvider .GetAttributes(typeof(JsonMultiSceneNameAttribute), false) .Cast<JsonMultiSceneNameAttribute>() .FirstOrDefault(); if (multiNameAttr != null && !string.IsNullOrWhiteSpace(multiNameAttr.APIPropertyName)) { prop.PropertyName = multiNameAttr.APIPropertyName; } } return properties; } }
SmartModel存储场景解析器
直接使用原生属性名作为序列化字段名:
public class SmartModelStorageContractResolver : DefaultContractResolver { protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { var properties = base.CreateProperties(type, memberSerialization); foreach (var prop in properties) { prop.PropertyName = prop.UnderlyingName; } return properties; } }
步骤3:给模型类标记特性
和你期望的写法几乎一致,只是特性名换成自定义的即可:
class Pipe { [JsonMultiSceneName( SolverPropertyName = "z_axis_dimension", APIPropertyName = "distance")] public double Length { get; set; } [JsonMultiSceneName( SolverPropertyName = "cross_sectional_dimension", APIPropertyName = "diameter")] public double Diameter { get; set; } }
步骤4:不同场景序列化调用示例
var pipe = new Pipe { Length = 10.2, Diameter = 5.5 }; // 序列化得到SolverDTO var solverSettings = new JsonSerializerSettings { Formatting = Formatting.Indented, ContractResolver = new SolverSceneContractResolver() }; string solverJson = JsonConvert.SerializeObject(pipe, solverSettings); // 序列化得到API用DTO var apiSettings = new JsonSerializerSettings { Formatting = Formatting.Indented, ContractResolver = new APISceneContractResolver() }; string apiJson = JsonConvert.SerializeObject(pipe, apiSettings); // 序列化得到存储用SmartModelDTO var storageSettings = new JsonSerializerSettings { Formatting = Formatting.Indented, ContractResolver = new SmartModelStorageContractResolver() }; string storageJson = JsonConvert.SerializeObject(pipe, storageSettings);
补充说明
该方案比修改/扩展原生JsonProperty的灵活性更高,后续新增其他序列化场景时,仅需做两处修改:
- 在
JsonMultiSceneNameAttribute中新增对应场景的名称属性 - 新增对应场景的合约解析器即可,无需调整已有模型的标记逻辑
内容的提问来源于stack exchange,提问作者Francois Louw
相关产品推荐
相关产品推荐

