Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
296 views
in Technique[技术] by (71.8m points)

.net - Json.net Add property to every class containing of a certain type

I want to add a property to my json for every property of type DateTime.

I have a custom converter and have an override on CreateProperties (see below). When debugging the returned list of properties holds the new values but by the time the json reaches the browser it does not contain the new properties

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);

        foreach(var prop in base.CreateProperties(type, memberSerialization))
        {
            if(prop != null
                && (prop.PropertyType == typeof(DateTime) || prop.PropertyType == typeof(DateTime?)))
            {
                properties.Add(new JsonProperty() {
                    PropertyName = String.Format("{0}$$type", prop.PropertyName),
                    PropertyType = typeof(String)
                });
            }
        }
        return properties;
    }

The first thing that comes to mind is that the new Json properties are being wiped off afterwards because they are invalid. Possibly because they have no value, however I can see no way to set the value here.

Any ideas would be most welcome

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You are trying to add a synthetic read-only property to your serialized JSON via a custom contract resolver. Your problem is that you are not fully initializing the required values of JsonProperty. At the minimum you must initialize

In addition, it is advisable to initialize:

The following does this, adding a fixed string property with the value "DateTime":

public class AddDateTypeFlagContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);
        for (int i = 0, n = properties.Count; i < n; i++)
        {
            var prop = properties[i];
            if (prop.PropertyType == typeof(DateTime) || prop.PropertyType == typeof(DateTime?))
            {
                var name = string.Format("{0}$$type", prop.PropertyName);
                var newProp = new JsonProperty
                {
                    DeclaringType = type,
                    PropertyName = name,
                    UnderlyingName = name,
                    PropertyType = typeof(string),
                    ValueProvider = new FixedValueProvider("DateTime"), // Replace with yout desired string value.
                    AttributeProvider = NoAttributeProvider.Instance,
                    Readable = true,
                    Writable = false,
                    // Ensure PreserveReferencesHandling and TypeNameHandling do not apply to the synthetic property.
                    ItemIsReference = false, 
                    TypeNameHandling = TypeNameHandling.None,
                };
                properties.Add(newProp);
            }
        }

        return properties;
    }
}

public class FixedValueProvider : IValueProvider
{
    readonly object value;

    public FixedValueProvider(object value)
    {
        this.value = value;
    }

    #region IValueProvider Members

    public object GetValue(object target) { return value; }

    public void SetValue(object target, object value)
    {
        throw new NotImplementedException("SetValue not implemented for fixed properties; set JsonProperty.Writable = false.");
    }

    #endregion
}

class NoAttributeProvider : IAttributeProvider
{
    static NoAttributeProvider() { instance = new NoAttributeProvider(); }

    static readonly NoAttributeProvider instance;

    public static NoAttributeProvider Instance { get { return instance; } }

    public IList<Attribute> GetAttributes(Type attributeType, bool inherit) { return new Attribute[0]; }

    public IList<Attribute> GetAttributes(bool inherit) { return new Attribute[0]; }
}

Then, given the following example type:

public class Example
{
    public string NotADate { get; set; }
    public DateTime ADate { get; set; }
    public DateTime? AnotherDate { get; set; }
}

The following JSON is generated:

{
  "NotADate": "a value",
  "ADate": "2017-04-23T14:25:43.511-04:00",
  "AnotherDate": "2017-04-23T14:25:43.511-04:00",
  "ADate$$type": "DateTime",
  "AnotherDate$$type": "DateTime"
}

It is also to create a synthetic property based on some other property. For instance, the following adds a Date property for every DateTime property in the model:

public class AddDateFromDateTimeContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);
        for (int i = 0, n = properties.Count; i < n; i++)
        {
            var prop = properties[i];
            if (prop.PropertyType == typeof(DateTime) || prop.PropertyType == typeof(DateTime?))
            {
                var name = string.Format("{0}$$date", prop.PropertyName);
                var newProp = new JsonProperty
                {
                    DeclaringType = type,
                    PropertyName = name,
                    UnderlyingName = name,
                    PropertyType = prop.PropertyType,
                    ValueProvider = new DateTimeToDateValueProvider(prop.ValueProvider),
                    AttributeProvider = NoAttributeProvider.Instance,
                    Readable = true,
                    Writable = false,
                    // Ensure PreserveReferencesHandling and TypeNameHandling do not apply to the synthetic property.
                    ItemIsReference = false,
                    TypeNameHandling = TypeNameHandling.None,
                };
                properties.Add(newProp);
            }
        }

        return properties;
    }
}

public class DateTimeToDateValueProvider : ValueProviderDecorator
{
    public DateTimeToDateValueProvider(IValueProvider baseProvider) : base(baseProvider) { }

    public override object GetValue(object target)
    {
        var baseValue = base.GetValue(target);
        if (baseValue is DateTime)
        {
            return ((DateTime)baseValue).Date;
        }
        return baseValue;
    }

    public override void SetValue(object target, object value)
    {
        throw new NotImplementedException();
    }
}

public abstract class ValueProviderDecorator : IValueProvider
{
    readonly IValueProvider baseProvider;

    public ValueProviderDecorator(IValueProvider baseProvider)
    {
        if (baseProvider == null)
            throw new ArgumentNullException();
        this.baseProvider = baseProvider;
    }

    public virtual object GetValue(object target) { return baseProvider.GetValue(target); }

    public virtual void SetValue(object target, object value) { baseProvider.SetValue(target, value); }
}

Notice that the value provider is created by wrapping the value provider of the original property in a decorator that transforms its value to the required result. Notice also that JsonProperty.PropertyType is set to the expected type to be returned. Using this contract resolver, the following JSON is generated from the Example type:

{
  "NotADate": "a value",
  "ADate": "2017-04-23T14:25:43.511-04:00",
  "AnotherDate": "2017-04-23T14:25:43.511-04:00",
  "ADate$$date": "2017-04-23T00:00:00-04:00",
  "AnotherDate$$date": "2017-04-23T00:00:00-04:00"
}

Sample fiddle demonstrating both contract resolvers.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...