如何使用json.net来回序列化/反序列化时更改属性名称/值

时间:2016-01-06 07:27:29

标签: json.net

我正在使用Json.NET来序列化对象(命令)并通过服务总线发送它们。某些序列化对象太大而无法通过服务总线发送。我想用TooLargeForServiceBusAttribute等自定义属性标记一些属性。现在我想在序列化的json中重命名这些属性并替换值。目标是将属性的大内容交换到外部存储,并将外部存储中的内容的id添加到序列化的json字符串。

示例

class CommandWithTooLargeProperty
{
    [TooLargeForServiceBus]
    public string SomeProperty { get; set; }
}

我希望序列化的json如下:

{
    SomeProperty_EXTERNAL_STORE_ID = '10000000-2000-3000-4000-500000000000'
}

如何挂钩Json.NET的序列化过程以获得我想要的东西?我不能使用自定义转换器,因为我的一些命令类已经使用自定义转换器进行了修饰,并且我所描述的机制必须对我要序列化的每个类都是透明的。

我的第一个问题是编写一个继承自JsonTextWriter的类来重命名该属性,但我不想重命名每个属性,只重命名用TooLargeForServiceBusAttribute和{{1}修饰的属性我无权访问源对象的属性。

我必须向序列化管道注入类似JsonTextWriter的内容,以将交换属性的内容保存到外部存储中。

反序列化过程必须执行序列化的反向工作:从IExternalStore获取id,从SomeProperty_EXTERNAL_STORE_ID加载内容并将加载的值设置为反序列化对象的属性。

1 个答案:

答案 0 :(得分:1)

我找到了解决方案:

  1. 在我的示例中实现您自己的ContractResolver(名为ContractResolverWithSwapPropertyValueSupport)。

  2. 在解析程序覆盖CreateObjectContract中,将原始属性的JsonProperty设置为Ignored = true

  3. 使用自定义JsonProperty添加新的IValueProvider。此属性将交换值的id存储在外部存储中。

  4. class Program
    {
        static void Main(string[] args)
        {
            var externalStorage = new SimpleDictionaryStorage();
            var contractResolver = new ContractResolverWithSwapPropertyValueSupport(externalStorage);
    
            var serializer = JsonSerializer.Create(new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.All, // Allows deserializing to the actual runtime type
                TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple, // In a version resilient way
                NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore, // Remove null value properties from the serialized object
                Formatting = Formatting.Indented,
                ContractResolver = contractResolver
            });
    
            var command = new CommandWithLargeProperty()
            {
                SomeLargeProperty = "Hello World"
            };
    
            string serializedCommand = null;
            using (var stringWriter = new StringWriter())
            using (var jsonWriter = new JsonTextWriter(stringWriter))
            {
                serializer.Serialize(jsonWriter, command);
                serializedCommand = stringWriter.ToString();
            }
    
            Console.WriteLine(serializedCommand);
    
            CommandWithLargeProperty deserializedCommand = null;
            using (var stringReader = new StringReader(serializedCommand))
            using (var jsonReader = new JsonTextReader(stringReader))
            {
                deserializedCommand = (CommandWithLargeProperty)serializer.Deserialize(jsonReader);
            }
    
            Console.WriteLine(command.SomeLargeProperty);
            Console.WriteLine(deserializedCommand.SomeLargeProperty);
            Console.WriteLine(command.SomeLargeProperty == deserializedCommand.SomeLargeProperty);
    
            Console.ReadLine();
        }
    }
    
    public class CommandWithLargeProperty
    {
        [SwapPropertyValue]
        public string SomeLargeProperty { get; set; }
    }
    
    public class ContractResolverWithSwapPropertyValueSupport : DefaultContractResolver
    {
        private readonly IExternalStorage _externalValueStorage;
    
        public ContractResolverWithSwapPropertyValueSupport(IExternalStorage externalValueStorage)
        {
            _externalValueStorage = externalValueStorage;
        }
    
        protected override JsonObjectContract CreateObjectContract(Type objectType)
        {
            var objectContract = base.CreateObjectContract(objectType);
    
            foreach (var jsonProperty in objectContract.Properties.ToArray())
            {
                var propertyInfo = jsonProperty.DeclaringType.GetProperties().SingleOrDefault(p => p.Name == jsonProperty.UnderlyingName);
                if (propertyInfo.GetCustomAttributes(typeof(SwapPropertyValueAttribute), inherit: false).SingleOrDefault() != null)
                {
                    // Ignore the property which will be swapped
                    jsonProperty.Ignored = true;
    
                    // Add a new property with a ValueProvider which will swap the content of the real property to an external storage and vice versa
                    objectContract.Properties.Add(new JsonProperty()
                    {
                        PropertyName = string.Format("{0}_EXTERNAL_ID", jsonProperty.PropertyName),
                        PropertyType = typeof(Guid),
                        ValueProvider = new ExternalStorageValueProvider(_externalValueStorage, propertyInfo),
                        Readable = true,
                        Writable = true
                    });
                }
            }
    
            return objectContract;
        }
    }
    
    public class ExternalStorageValueProvider : IValueProvider
    {
        private readonly IExternalStorage _externalValueStorage;
        private readonly PropertyInfo _propertyInfo;
    
        public ExternalStorageValueProvider(IExternalStorage externalValueStorage, PropertyInfo propertyInfo)
        {
            _externalValueStorage = externalValueStorage;
            _propertyInfo = propertyInfo;
        }
    
        public object GetValue(object target)
        {
            object valueRaw = _propertyInfo.GetValue(target);
            string valueJson = JsonConvert.SerializeObject(valueRaw);
            Guid id = _externalValueStorage.SetValue(valueJson);
            return id;
        }
    
        public void SetValue(object target, object value)
        {
            Guid id = (Guid)value;
            string valueJson = _externalValueStorage.GetValue(id);
            object valueRaw = JsonConvert.DeserializeObject(valueJson);
            _propertyInfo.SetValue(target, valueRaw);
        }
    }
    
    public interface IExternalStorage
    {
        Guid SetValue(string objectAsJson);
        string GetValue(Guid id);
    }
    
    public class SimpleDictionaryStorage : IExternalStorage
    {
        private readonly Dictionary<Guid, string> _store = new Dictionary<Guid, string>();
    
        public Guid SetValue(string objectAsJsonString)
        {
            Guid id = Guid.NewGuid();
            _store[id] = objectAsJsonString;
            return id;
        }
    
        public string GetValue(Guid id)
        {
            return _store[id];
        }
    }
    
    public class SwapPropertyValueAttribute : Attribute
    {
    }