0

I have a generic method that takes T, GetResult<T>()

T represents many objects which have custom attribute [JsonProperty(Required = Required.Always)] on most of their properties. Within GetResult<T>(), I need to change T's properties to [JsonProperty(Required = Required.Default)] and I need the next method which is called from within GetResult to get the modified version of T.

Is that possible? If so, what adjustments would I need to my sample program below?

namespace StackOverflow1
{
    using System;
    using Newtonsoft.Json;

    class Program
    {
        static void Main(string[] args)
        {
            GetResult<Person>();

            Console.Read();
        }

        private static T GetResult<T>() where T : Base
        {
            // Entering this method, T is passed with Name property set to [Required = Required.Always]

            // I'm changing T's Name property to [Required = Required.Default]
            var properties = typeof(T).GetProperties();

            foreach (var property in properties)
            {
                var customAttributes = property.GetCustomAttributes(true);
                foreach (var attribute in customAttributes)
                {
                    if (attribute.GetType().Name == nameof(JsonPropertyAttribute))
                    {
                        Console.WriteLine($"Original: {((JsonPropertyAttribute)attribute).Required}");

                        // this is the change! But it looks like this does not actually change T which I need to forward into BuildSingleResult<T>
                        ((JsonPropertyAttribute)attribute).Required = Required.Default;

                        Console.WriteLine($"After modification: {((JsonPropertyAttribute)attribute).Required}");
                    }
                }
            }

            // I need T to be the **modified version** which would have [Required = Required.Default]
            return BuildSingleResult<T>();
        }

        private static T BuildSingleResult<T>() where T : Base
        {
            // **** this is just to write out JsonPropertyAttribute ***
            var props = typeof(T).GetProperties();

            foreach (var p in props)
            {
                var customAttributes = p.GetCustomAttributes(true);
                foreach (var attr in customAttributes)
                {
                    if (attr.GetType().Name == nameof(JsonPropertyAttribute))
                    {
                        // This shows that T still has [Required = Required.Always] but I need it to have [Required = Required.Default]
                        Console.WriteLine($"(expecting Default): {((JsonPropertyAttribute)attr).Required}");
                    }
                }
            }
            // *** end of debug ***

            return new Person { Name = "X" } as T;
        }
    }

    class Base
    {
        [JsonProperty(Required = Required.Always)]
        public string Name { get; set; }
    }

    // This is just to demonstrate using a generic type!
    class Person : Base { }
}
  • 5
    Maybe I'm not understanding the question, but type attributes are baked into the metadata and cannot be modified. What are you trying to do that modifying the attributes would accomplish? – D Stanley Oct 22 '19 at 21:45
  • 2
    You could define another class that is exactly the same as your existing class but with different attributes. – John Wu Oct 22 '19 at 22:16
  • "T represents many objects which have custom attribute..." - uhm. No. T represents a Type, or system of Types. As far as I am aware you can not constrain a generic type parameter by Attribute. – Sam Axe Oct 22 '19 at 22:17
  • The root problem is that the actual method that I represented as ```BuildSingleResult``` fetches data from cosmos db using ```var results = documentQuery.ExecuteNextAsync().Result;```. When this method executes, it gets a serialization error because properties that were marked ```Required.Always``` are not all fetched back. That is due to our use of the OData $select operator. – Sami M. Abbushi Oct 22 '19 at 22:20
  • Is your real problem that, in certain circumstances, you would like to tell Json.NET to ignore the `Required = Required.Always` setting in runtime? If so you can do that with a custom contract resolver that sets [`JsonProperty.Required = Required.Default`](https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_Serialization_JsonProperty_Required.htm), similar to the one shown [here](https://stackoverflow.com/a/48330214). Would that answer your question? – dbc Oct 23 '19 at 16:41
  • Thank you @dbc! Yep, tried that. I created a custom ContractResolver which overwrote CreateProperty. I then initiated my ```Azure.Documents.Client.DocumentClient```, with ```new JsonSerializerSettings{ ContractResolver = new PropertyNotRequiredOnSerializationResolver() }```. But I only want ```Required.Default``` when I read from cosmos. I still needed ```Required.Always``` on writing data to cosmos as a last layer of validation. Because this setting proved to be used for both, I abandoned that solution. If there was a way to differentiate the two, it would be a good solution. – Sami M. Abbushi Oct 23 '19 at 17:15
  • @SamiM.Abbushi - not sure I understand your problem. Can't you just pass a different contract resolver when reading vs writing? Or is there some problem with the API you are using that the required contract resolver can't be passed in and needs to be set up globally? If so, maybe you could [edit] your question and explain? – dbc Oct 23 '19 at 17:19
  • @dbc, I'd like to keep the topic as it is since D. Stanley answered what I was looking for, "type attributes are baked into the metadata and cannot be modified". So, if I understand correctly, in the sample code that I provided, while you can make the changes to T using reflection, it would not actually change the T that is passed to the next method. Thank you all for your help and comments; very much appreciated. – Sami M. Abbushi Oct 25 '19 at 16:23

0 Answers0