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!

5 Answers 5

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());
Sign up to request clarification or add additional context in comments.

9 Comments

Thanks Brian, this is a real elegant solution.
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
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.
Perfect, thank you. Just had to add a .toArray() after the last select statement.
@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 for what to do in such a situation.
|
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;
                }
            }
        }
    }
}

Comments

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.

1 Comment

Thanks for this, I'm going to try to use this with my question at stackoverflow.com/questions/45826000/…
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);

Comments

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 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.