14

I have added a custom property to some of my objects like this:

[JsonCustomRoot("status")]
public class StatusDTO 
{
    public int StatusId { get; set; }
    public string Name { get; set; }
    public DateTime Created { get; set; }
}

The attribute is very simple:

public class JsonCustomRoot :Attribute
{
    public string rootName { get; set; }

    public JsonCustomRoot(string rootName)
    {
        this.rootName = rootName;
    }
}

The default output from JSON.NET when serializing an instance of an object is this:

{"StatusId":70,"Name":"Closed","Created":"2012-12-12T11:50:56.6207193Z"}

Now the question is: How can I add a root-node to the JSON with the value of the custom attribute like so:

{status:{"StatusId":70,"Name":"Closed","Created":"2012-12-12T11:50:56.6207193Z"}}

I have found several articles mentioning the IContractResolver interface, but I cannot grasp how to do it. My attempts include this unfinished piece of code:

protected override JsonObjectContract CreateObjectContract(Type objectType)
{
    JsonObjectContract contract = base.CreateObjectContract(objectType);

    var info = objectType.GetCustomAttributes()
                   .SingleOrDefault(t => (Type)t.TypeId==typeof(JsonCustomRoot));
    if (info != null)
    {
        var myAttribute = (JsonCustomRoot)info;
        // How can i add myAttribute.rootName to the root from here?
        // Maybe some other method should be overrided instead?
    }

    return contract;
}
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Espo
  • 41,399
  • 21
  • 132
  • 159
  • I'm also looking for something like this, I don't want to wrap my responses on the controllers for each `GET`, I was looking for a way to do this as either a setting or a custom attribute on each model. – MilkyWayJoe Jan 02 '13 at 17:00
  • Ideally, I'd like to do almost the same as yours but I'd be returning as an array even if only one object is returned e.g: `{status:[{"StatusId":70,"Name":"Closed","Created":"2012-12-12T11:50:56.6207193Z"}]}`. Also trying with `IContractResolver` and JSON.NET – MilkyWayJoe Jan 02 '13 at 17:02
  • I blogged about a solution specific to web api at http://www.emadibrahim.com/2014/04/09/emberjs-and-asp-net-web-api-and-json-serialization/ – Emad Apr 09 '14 at 17:19

4 Answers4

13

What if you use an anonymous object?

JSON.Serialize(new { status = targetObject});
BuddhiP
  • 6,231
  • 5
  • 36
  • 56
  • 5
    I am using JSON.Net as a serializer on a .NET 4.0 WebAPI project, so I am trying to avoid writing custom code when returning data. I want it to be done for me when the attribute is applied. – Espo Dec 12 '12 at 12:23
6

Here's a solution specifically for Web API, which I am also using: RootFormatter.cs

I wrote it based on Creating a JSONP Formatter for ASP.NET Web API.

Instead of using a custom attribute I am reusing Title field of JsonObjectAttribute. Here's a usage code:

using Newtonsoft.Json

[JsonObject(Title = "user")]
public class User
{
    public string mail { get; set; }
}

Then, add RootFormatter to your App_Start and register it as follows in WebApiConfig:

GlobalConfiguration.Configuration.Formatters.Insert(0, new RootFormatter());

I was able to get a wrapped response similar to WCF's WebMessageBodyStyle.Wrapped:

{"user":{
  "mail": "foo@example.com"
}}
Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
5

A very easy way of approaching this is to place the object inside another object. it maybe a over simplistic was of looking at it, but this works when working with collections, and singular objects.

  public class StatusDTO
    {
        public int StatusId { get; set; }
        public string Name { get; set; }
        public DateTime Created { get; set; }
    }

    public class statusJasonModel
    {
        public StatusDTO status { get; set; }
    }

Now you if you put the StatusDTO inside the statusJsonModel object and serialize it as Json. It should give you your desired answer.

Benjishk
  • 51
  • 1
  • 3
-2

I had similar challenge in one of my project. Below is the step that I took in resolving the problem.

1. My Entity class

public class Product
    {
        [Key]
        public string Id { get; set; }
        public string Title { get; set; }
        public string Album { get; set; }
        public string Artist { get; set; }
        public string Genre { get; set; }

    }

2. Created another Class, which is defined in this form.

    public class KindOfMedia
        {
            public KindOfMedia()
            {
                Product = new List<Product>();
            }
            public List<Product> Product { get; set; }
        }

3. The Web API controller, that will return json

            public HttpResponseMessage Products()
            {
                var kind = new KindOfMedia();
                kind.Products = new List<Product>();
                kind.Products.Add(new Product
                {
                    Id = Guid.NewGuid().ToString(),
                    Title = "I am A Winner",
                    Album = "",
                    Artist = "Project Fame",
                    Genre = "Contemporal"                
                });
                kind.Products.Add(new Product
                {
                    Id = Guid.NewGuid().ToString(),
                    Title = "Great Nation",
                    Album = "Oceans",
                    Artist = "Timi Dakolo",
                    Genre = "Gospel"
                });

                return Request.CreateResponse(HttpStatusCode.OK, kind);
            }

4. Add this line of code to my WebApi Config file in App_Start folder

     var json = config.Formatters.JsonFormatter;
                json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None;

please note that Newtonsoft.Json.PreserveReferencesHandling.None, will not preserve reference of the serialize type

The resulting Json is

{
  "Musics": [
    {
      "Id": "bf9faeee-7c39-4c33-a0ea-f60333604061",
      "Title": "I am A Winner",
      "Album": "",
      "Artist": "Project Fame",
      "Genre": "Contemporal"
    },
    {
      "Id": "243edd32-7ba2-4ac4-8ab9-bba6399cb0a6",
      "Title": "Great Nation",
      "Album": "Oceans",
      "Artist": "Timi Dakolo",
      "Genre": "Gospel"
    }
  ]
}
Christoph
  • 1,631
  • 8
  • 19