【问题标题】:How to change property name/value on serializing/deserializing back and forth with json.net如何使用 json.net 来回更改序列化/反序列化的属性名称/值
【发布时间】:2016-01-06 07:27:28
【问题描述】:

我正在使用 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 加载内容并将加载的值设置为反序列化对象的属性。

【问题讨论】:

  • 如果您不更改属性名称,而是换出对象本身,这样做会容易得多(尽管仍然有些困难)。例如。创建像 {"SomeProperty" : { "$External_Store_Id" : "10000000-2000-3000-4000-500000000000" } } 这样的 JSON

标签: json.net


【解决方案1】:

我找到了解决办法:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多