26

If I have a C# model class that is used by JSON.net to bind data from a serialized JSON string, is there a way that I can create a query string from that class in order to make the initial request?

Model class example:

public class model
{
   [JsonProperty(PropertyName = "id")]
   public long ID { get; set; }
   [JsonProperty(PropertyName = "some_string")]
   public string SomeString {get; set;} 
}

Querystring example:

baseUrl + uri + "&fields=id,some_string" + token

So the essence of what I am trying to do is gather both "id" and "some_string" from the model object so i can dynamically create a the "&fields" arguments. Thanks!

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
CostelloNicho
  • 704
  • 1
  • 9
  • 20

5 Answers5

40

@Leigh Shepperson has the right idea; however, you can do it with less code using LINQ. I would create a helper method like this:

using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
...

public static string GetFields(Type modelType)
{
    return string.Join(",",
        modelType.GetProperties()
                 .Select(p => p.GetCustomAttribute<JsonPropertyAttribute>())
                 .Select(jp => jp.PropertyName));
}

You can use it like this:

var fields = "&fields=" + GetFields(typeof(model));

EDIT

If you're running under the 3.5 version of the .Net Framework such that you don't have the generic GetCustomAttribute<T> method available to you, you can do the same thing with the non-generic GetCustomAttributes() method instead, using it with SelectMany and Cast<T>:

    return string.Join(",",
        modelType.GetProperties()
                 .SelectMany(p => p.GetCustomAttributes(typeof(JsonPropertyAttribute))
                                   .Cast<JsonPropertyAttribute>())
                 .Select(jp => jp.PropertyName)
                 .ToArray());
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • Thanks Brian, this is a real elegant solution. – CostelloNicho Nov 09 '15 at 19:34
  • I should have specified better in the question, I'll update that after this meeting. I'm using Unity3D i.e. modified .net 3.5 and it seems like generic type was given to getcustomattributes in 4.5 – CostelloNicho Nov 09 '15 at 21:15
  • Your question was originally tagged with `Unity3d`, but since the question did not seem to have anything to do with that, I removed it and added `Json.Net` in its place. I've updated my answer to show how you can use the non-generic `GetCustomAttributes()` method to do the same thing in .NET 3.5. – Brian Rogers Nov 09 '15 at 22:05
  • Perfect, thank you. Just had to add a .toArray() after the last select statement. – CostelloNicho Nov 09 '15 at 22:17
  • @CostelloNicho Good catch. I missed that one. I updated my answer with your fix. Thanks for the accept! – Brian Rogers Nov 09 '15 at 22:24
  • I know this is old, but I just needed this and is great! However, Not all my properties have a `JsonPropertyAttribute` and I'm getting `null` exceptions. How can this `Linq` be modified to help that? – RoLYroLLs Aug 22 '17 at 19:35
  • Ooo! I tested something and it worked. I hope it's ok that I modify the answer. – RoLYroLLs Aug 22 '17 at 19:38
  • 1
    @RoLYroLLs - Your edit was not correct; it skipped the properties without `[JsonProperty(PropertyName = "XXX")]` attributes rather than falling back on the c# property name. You really should ask another question about this situation, but I added [another answer](https://stackoverflow.com/a/45829514/3744182) for what to do in such a situation. – dbc Aug 23 '17 at 02:05
  • @dbc, ah you're right. I guess I wasn't thinking about it, since it's what I wanted in the first place, but didn't think of other case-scenarios. – RoLYroLLs Aug 23 '17 at 19:37
8

You can do this using reflection. This is the general idea:

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Reflection;

namespace ConsoleApplication8
{
    public class model
    {
        [JsonProperty(PropertyName = "id")]
        public long ID { get; set; }

        [JsonProperty(PropertyName = "some_string")]
        public string SomeString { get; set; }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            var model = new model();

            var result = string.Empty;

            PropertyInfo[] props = typeof(model).GetProperties();
            foreach (PropertyInfo prop in props)
            {
                foreach (object attr in prop.GetCustomAttributes(true))
                {
                    result += (attr as JsonPropertyAttribute).PropertyName;
                }
            }
        }
    }
}
Leigh Shepperson
  • 1,043
  • 5
  • 13
8

In cases where the model is only partially annotated with [JsonProperty(PropertyName = "XXX")] attributes, or is annotated with data contract attributes, or has ignored properties, you can use Json.NET's own contract resolver to obtain the list of serialized property names. First, introduce the following extension method:

public static class JsonExtensions
{
    public static string [] PropertyNames(this IContractResolver resolver, Type type)
    {
        if (resolver == null || type == null)
            throw new ArgumentNullException();
        var contract = resolver.ResolveContract(type) as JsonObjectContract;
        if (contract == null)
            return new string[0];
        return contract.Properties.Where(p => !p.Ignored).Select(p => p.PropertyName).ToArray();
    }
}

Then, do:

// Allocate the relevant contract resolver. 
// Options are CamelCasePropertyNamesContractResolver() or DefaultContractResolver().
IContractResolver resolver = new DefaultContractResolver(); 

// Get properties
var propertyNames = resolver.PropertyNames(typeof(model));
var fields = "&fields=" + String.Join(",", propertyNames);

For resolver use CamelCasePropertyNamesContractResolver if you are camel casing your property names (which ASP.NET Core Web API does by default); otherwise use DefaultContractResolver.

Sample fiddle.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Thanks for this, I'm going to try to use this with my question at https://stackoverflow.com/questions/45826000/getting-the-jsonpropertyattribute-of-a-property/ – RoLYroLLs Aug 23 '17 at 19:39
0

A small variation of @Brian Rogers solution that solve null exception problem:

IEnumerable<string> props = typeof(T).GetProperties()
                                     .Select(p => p.GetCustomAttribute<JsonPropertyAttribute>())
                                     .Where(jp => jp != null)
                                     .Select(jp => jp.PropertyName);

string propsList = string.Join(',', props);
0
public class CreateContactProperties
{
   [JsonProperty("email")] public string email { get; set; }
   [JsonProperty("firstname")] public string firstname { get; set; }
}

public string GetJsonPropertyFieldName(PropertyInfo t)
{
   var attr = t.GetCustomAttributes(typeof(JsonPropertyAttribute), true).FirstOrDefault() as JsonPropertyAttribute;

   return attr.PropertyName;
}


IList<PropertyInfo> entityprops = new List<PropertyInfo>(typeof(CreateContactProperties).GetProperties());
foreach (var item in entityprops)
{
  properties += $"{GetJsonPropertyFieldName(item)}, ";
}
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 30 '22 at 07:33