如何在使用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继承的类来重命名属性,但我不想重命名每个属性,只想重命名用TooLargeForServiceBusAttributeJsonTextWriter装饰的属性。我无权访问源对象的属性。

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

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

我找到了一个解决方案:

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

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

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


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
{
}

相关内容

  • 没有找到相关文章

最新更新