我正在使用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
和JsonTextWriter
装饰的属性。我无权访问源对象的属性。
我必须将类似IExternalStore
的东西注入序列化管道,以将交换的属性的内容保存到外部存储中。
反序列化过程必须执行与序列化相反的工作:从SomeProperty_EXTERNAL_STORE_ID
获取id,从IExternalStore
加载内容,并将加载的值设置为反序列化对象的属性。
我找到了一个解决方案:
-
实现您自己的
ContractResolver
(在下面的示例中命名为ContractResolverWithSwapPropertyValueSupport
)。 -
在冲突解决程序中,覆盖
CreateObjectContract
并将原始属性的JsonProperty
设置为Ignored = true
。 -
添加具有自定义
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
{
}