如何添加元数据以描述JSON.Net中哪些属性是日期 [英] How to add metadata to describe which properties are dates in JSON.Net

查看:96
本文介绍了如何添加元数据以描述JSON.Net中哪些属性是日期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在我的json中添加一个元数据属性,以便客户端可以知道什么是日期.

I would like to add a metadata property to my json so that the client side can know what properties are dates.

例如,如果我有一个像这样的对象:

For example if I had an object like this:

{
  "notADate": "a value",
  "aDate": "2017-04-23T18:25:43.511Z",
  "anotherDate": "2017-04-23T18:25:43.511Z"
}

我想添加一个元数据属性,以告知消费者将哪些属性视为日期,如下所示:

I would like to add a metadata property to tell the consumer which properties to treat as dates something like this:

{
  "_date_properties_": ["aDate", "anotherDate"],
  "notADate": "a value",
  "aDate": "2017-04-23T18:25:43.511Z",
  "anotherDate": "2017-04-23T18:25:43.511Z"
}

任何帮助都会很棒,谢谢!

Any help would be great, thanks!

推荐答案

您可以创建属性插入序列化的每个对象的协定中.

You could create a custom ContractResolver that inserts a synthetic "_date_properties_" property into the contract of every object that is serialized.

为此,请首先子类 DefaultContractResolver 由添加了应用程序的事件处理程序创建的合同可以流利地定制:

To do this, first subclass DefaultContractResolver to allow contracts to be fluently customized after they have been created by application-added event handlers:

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

接下来,创建一个扩展方法以将所需的属性添加到 :

Next, create an extension method to add the desired property to a 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
    }
}

最后,按如下所示序列化示例类型:

Finally, serialize your example type as follows:

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);

此解决方案仅处理静态类型的DateTimeDateTime?属性.如果您具有object值的属性,有时具有DateTime值,或者是Dictionary<string, DateTime>包含DateTime值的扩展数据,您将需要一个更复杂的解决方案.

This solution only handles statically typed DateTime and DateTime? properties. If you have object-valued properties that sometimes have DateTime values, or a Dictionary<string, DateTime>, or extension data containing DateTime values, you will need a more complex solution.

(作为一种替代实现,您可以代替 DefaultContractResolver.CreateObjectContract a>并使用JsonContractExtensions.AddDateProperties()在那里硬编码所需的属性,但是我认为创建通用的,流利的可配置合同解析器会更加有趣,以防日后有必要插入不同的自定义.)

(As an alternative implementation, you could instead subclass DefaultContractResolver.CreateObjectContract and hardcode the required properties there using JsonContractExtensions.AddDateProperties(), however I thought it would be more interesting to create a general-purpose, fluently configurable contract resolver in case it becomes necessary to plug in different customizations later.)

您可能想要缓存合同解析器以获得最佳性能.

You may want to cache the contract resolver for best performance.

示例 .Net小提琴.

这篇关于如何添加元数据以描述JSON.Net中哪些属性是日期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆