我正在使用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
加载内容并将加载的值设置为反序列化对象的属性。
答案 0 :(得分:1)
我找到了解决方案:
在我的示例中实现您自己的ContractResolver
(名为ContractResolverWithSwapPropertyValueSupport
)。
在解析程序覆盖CreateObjectContract
中,将原始属性的JsonProperty
设置为Ignored = true
。
使用自定义JsonProperty
添加新的IValueProvider
。此属性将交换值的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
{
}