如何在.NET中用Newtonsoft JSON.NET序列化类属性及自定义属性?
解决JSON.NET序列化时保留自定义[Encrypted]特性的方案
核心结论
Newtonsoft JSON.NET没有直接内置的实现来自动将自定义特性序列化到JSON中,但可以通过自定义ContractResolver实现动态识别任意类上的[Encrypted]特性,并在序列化时添加标记信息,满足接收方识别加密属性的需求。
实现方案
方式一:将加密属性值包装为带标记的对象
这种方式会把加密属性的原值包装成包含加密标识的对象,接收方可以直接通过对象结构识别加密字段。
1. 定义包装类
public class EncryptedValue { public string Value { get; set; } public string EncryptionKey { get; set; } }
2. 实现自定义ContractResolver与转换器
public class EncryptedContractResolver : DefaultContractResolver { protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); // 检查当前属性是否带有Encrypted特性 var encryptedAttr = member.GetCustomAttribute<EncryptedAttribute>(); if (encryptedAttr != null) { // 为该属性指定自定义转换器,实现值的包装 property.Converter = new EncryptedValueConverter(encryptedAttr.Key); } return property; } } public class EncryptedValueConverter : JsonConverter { private readonly string _encryptionKey; public EncryptedValueConverter(string encryptionKey) { _encryptionKey = encryptionKey; } public override bool CanConvert(Type objectType) { // 只处理字符串类型的属性(可根据实际需求扩展) return objectType == typeof(string); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var encryptedValue = new EncryptedValue { Value = (string)value, EncryptionKey = _encryptionKey }; serializer.Serialize(writer, encryptedValue); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // 反序列化时直接返回原始值(可根据需求添加解密逻辑) var encryptedValue = serializer.Deserialize<EncryptedValue>(reader); return encryptedValue?.Value; } }
方式二:添加独立的加密标记字段
如果希望保留属性原值的结构,可通过添加额外字段标记该属性是否需要加密,比如为Address属性添加IsAddressEncrypted字段。
实现自定义ContractResolver
public class EncryptedMetadataContractResolver : DefaultContractResolver { protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { var properties = base.CreateProperties(type, memberSerialization); var extendedProperties = new List<JsonProperty>(properties); foreach (var property in properties) { var encryptedAttr = property.Member.GetCustomAttribute<EncryptedAttribute>(); if (encryptedAttr != null) { // 创建标记字段,格式为"Is{PropertyName}Encrypted" var metadataProperty = new JsonProperty { PropertyName = $"Is{property.PropertyName}Encrypted", Readable = true, Writable = false, ValueProvider = new ConstantValueProvider(true), DefaultValueHandling = DefaultValueHandling.Ignore }; extendedProperties.Add(metadataProperty); } } return extendedProperties; } } public class ConstantValueProvider : IValueProvider { private readonly object _fixedValue; public ConstantValueProvider(object value) { _fixedValue = value; } public object GetValue(object target) => _fixedValue; public void SetValue(object target, object value) { // 标记字段为只读,无需实现赋值逻辑 } }
使用示例
序列化时指定自定义的ContractResolver即可:
var person = new Person { Id = 1, FirstName = "John", LastName = "Doe", Address = "123 Main St", Phone = "555-1234", IdentityDetails = "XYZ123" }; // 使用方式一的序列化配置 var settings1 = new JsonSerializerSettings { ContractResolver = new EncryptedContractResolver() }; string wrappedJson = JsonConvert.SerializeObject(person, settings1); // 使用方式二的序列化配置 var settings2 = new JsonSerializerSettings { ContractResolver = new EncryptedMetadataContractResolver() }; string markedJson = JsonConvert.SerializeObject(person, settings2);
说明
- 两种方案都支持动态处理任意类,无需修改原有类的代码结构
- 方式一的JSON结构更直观,适合接收方直接解析加密信息;方式二保留了原始属性值结构,适合需要兼容非加密场景的需求
- 反序列化时可根据业务需求,在转换器或ContractResolver中添加解密、验证等逻辑
内容的提问来源于stack exchange,提问作者Neeraj Kumar Gupta
相关产品推荐
相关产品推荐

