我想在我的 json 中添加一个元数据属性,以便客户端可以知道哪些属性是日期。
例如,如果我有一个这样的对象:
{
"notADate": "a value",
"aDate": "2017-04-23T18:25:43.511Z",
"anotherDate": "2017-04-23T18:25:43.511Z"
}
我想添加一个元数据属性来告诉消费者哪些属性被视为日期,如下所示:
{
"_date_properties_": ["aDate", "anotherDate"],
"notADate": "a value",
"aDate": "2017-04-23T18:25:43.511Z",
"anotherDate": "2017-04-23T18:25:43.511Z"
}
任何帮助都会很棒,谢谢!
可以创建自定义ContractResolver
,将合成"_date_properties_"
属性插入到序列化的每个对象的协定中。
为此,第一个子类DefaultContractResolver
允许在应用程序添加的事件处理程序创建合约后对其进行流畅的自定义:
public class ConfigurableContractResolver : DefaultContractResolver
{
readonly object contractCreatedPadlock = new object();
event EventHandler<ContractCreatedEventArgs> contractCreated;
int contractCount = 0;
void OnContractCreated(JsonContract contract, Type objectType)
{
EventHandler<ContractCreatedEventArgs> created;
lock (contractCreatedPadlock)
{
contractCount++;
created = contractCreated;
}
if (created != null)
{
created(this, new ContractCreatedEventArgs(contract, objectType));
}
}
public event EventHandler<ContractCreatedEventArgs> ContractCreated
{
add
{
lock (contractCreatedPadlock)
{
if (contractCount > 0)
{
throw new InvalidOperationException("ContractCreated events cannot be added after the first contract is generated.");
}
contractCreated += value;
}
}
remove
{
lock (contractCreatedPadlock)
{
if (contractCount > 0)
{
throw new InvalidOperationException("ContractCreated events cannot be removed after the first contract is generated.");
}
contractCreated -= value;
}
}
}
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
OnContractCreated(contract, objectType);
return contract;
}
}
public class ContractCreatedEventArgs : EventArgs
{
public JsonContract Contract { get; private set; }
public Type ObjectType { get; private set; }
public ContractCreatedEventArgs(JsonContract contract, Type objectType)
{
this.Contract = contract;
this.ObjectType = objectType;
}
}
public static class ConfigurableContractResolverExtensions
{
public static ConfigurableContractResolver Configure(this ConfigurableContractResolver resolver, EventHandler<ContractCreatedEventArgs> handler)
{
if (resolver == null || handler == null)
throw new ArgumentNullException();
resolver.ContractCreated += handler;
return resolver;
}
}
接下来,创建一个扩展方法以将所需的属性添加到JsonObjectContract
:
public static class JsonContractExtensions
{
const string DatePropertiesName = "_date_properties_";
public static void AddDateProperties(this JsonContract contract)
{
var objectContract = contract as JsonObjectContract;
if (objectContract == null)
return;
var properties = objectContract.Properties.Where(p => p.PropertyType == typeof(DateTime) || p.PropertyType == typeof(DateTime?)).ToList();
if (properties.Count > 0)
{
var property = new JsonProperty
{
DeclaringType = contract.UnderlyingType,
PropertyName = DatePropertiesName,
UnderlyingName = DatePropertiesName,
PropertyType = typeof(string[]),
ValueProvider = new FixedValueProvider(properties.Select(p => p.PropertyName).ToArray()),
AttributeProvider = new NoAttributeProvider(),
Readable = true,
Writable = false,
// Ensure // Ensure PreserveReferencesHandling and TypeNameHandling do not apply to the synthetic property.
ItemIsReference = false,
TypeNameHandling = TypeNameHandling.None,
};
objectContract.Properties.Insert(0, property);
}
}
class FixedValueProvider : IValueProvider
{
readonly object properties;
public FixedValueProvider(object value)
{
this.properties = value;
}
#region IValueProvider Members
public object GetValue(object target)
{
return properties;
}
public void SetValue(object target, object value)
{
throw new NotImplementedException("SetValue not implemented for fixed properties; set JsonProperty.Writable = false.");
}
#endregion
}
class NoAttributeProvider : IAttributeProvider
{
#region IAttributeProvider Members
public IList<Attribute> GetAttributes(Type attributeType, bool inherit) { return new Attribute[0]; }
public IList<Attribute> GetAttributes(bool inherit) { return new Attribute[0]; }
#endregion
}
}
最后,按如下所示序列化示例类型:
var settings = new JsonSerializerSettings
{
ContractResolver = new ConfigurableContractResolver
{
// Here I am using CamelCaseNamingStrategy as is shown in your JSON.
// If you don't want camel case, leave NamingStrategy null.
NamingStrategy = new CamelCaseNamingStrategy(),
}.Configure((s, e) => { e.Contract.AddDateProperties(); }),
};
var json = JsonConvert.SerializeObject(example, Formatting.Indented, settings);
此解决方案仅处理静态类型化DateTime
和DateTime?
属性。 如果您有有时具有DateTime
值的object
值属性、Dictionary<string, DateTime>
或包含DateTime
值的扩展数据,则需要更复杂的解决方案。
(作为替代实现,您可以使用JsonContractExtensions.AddDateProperties()
对DefaultContractResolver.CreateObjectContract
进行子类化并在那里硬编码所需的属性,但是我认为创建一个通用的、流畅可配置的合约解析器会更有趣,以防以后需要插入不同的自定义项。
您可能希望缓存协定解析程序以获得最佳性能。
示例 .Net 小提琴。