32

Is there a way to control the JSON output of JsonResult with attributes, similar to how you can use XmlElementAttribute and its bretheren to control the output of XML serialization?

For example, given the following class:

public class Foo
{
    [SomeJsonSerializationAttribute("bar")]
    public String Bar { get; set; }

    [SomeJsonSerializationAttribute("oygevalt")]
    public String Oygevalt { get; set; }
}

I'd like to then get the following output:

{ bar: '', oygevalt: '' }

As opposed to:

{ Bar: '', Oygevalt: '' }
2
  • Check out the newly released Sierra: kohari.org/2009/08/10/siesta-painless-rest-via-asp-net-mvc Commented Aug 19, 2009 at 22:28
  • 1
    This looks promising (and interesting!), but I was hoping for something already baked in. Any way to get the existing serializer to respect the DataContract attributes? Commented Aug 19, 2009 at 22:34

6 Answers 6

37

I wanted something a bit more baked into the framework than what Jarrett suggested, so here's what I did:

JsonDataContractActionResult:

public class JsonDataContractActionResult : ActionResult
{
    public JsonDataContractActionResult(Object data)
    {
        this.Data = data;
    }

    public Object Data { get; private set; }

    public override void ExecuteResult(ControllerContext context)
    {
        var serializer = new DataContractJsonSerializer(this.Data.GetType());
        String output = String.Empty;
        using (var ms = new MemoryStream())
        {
            serializer.WriteObject(ms, this.Data);
            output = Encoding.Default.GetString(ms.ToArray());
        }
        context.HttpContext.Response.ContentType = "application/json";
        context.HttpContext.Response.Write(output);
    }
}

JsonContract() method, added to my base controller class:

    public ActionResult JsonContract(Object data)
    {
        return new JsonDataContractActionResult(data);
    }

Sample Usage:

    public ActionResult Update(String id, [Bind(Exclude="Id")] Advertiser advertiser)
    {
        Int32 advertiserId;
        if (Int32.TryParse(id, out advertiserId))
        {
            // update
        }
        else
        {
            // insert
        }

        return JsonContract(advertiser);
    }

Note: If you're looking for something more performant than JsonDataContractSerializer, you can do the same thing using JSON.NET instead. While JSON.NET doesn't appear to utilize DataMemberAttribute, it does have its own JsonPropertyAttribute which can be used to accomplish the same thing.

Sign up to request clarification or add additional context in comments.

4 Comments

The awesome part about MVC is how easy this stuff is to write. You can put together some really sophisticated solutions quite quickly!
Indeed it is! I don't plan on ever looking back to WebForms.
Very nice solution! The JsonDataContractActionResult class can be simplified even further if you inherit from JsonResult instead of the base ActionResult - then you only need to override the ExecuteResult method!
I could be missing something, but I don't think the memorystream is necessary. You can write directly to the response outputstream: serializer.WriteObject(context.HttpContext.Response.OutputStream, Data); I've tried this and it seems to work fine...
16

Here's my implementation of Daniel Schaffer's answer, with the suggested improvements by Justin Rusbatch and Daniel incorporated.

using System;
using System.Runtime.Serialization.Json;
using System.Web.Mvc;

public class JsonDataContractActionResult : JsonResult
{
    public JsonDataContractActionResult( Object data )
    {
        this.Data = data;
    }

    public override void ExecuteResult( ControllerContext context )
    {
        var serializer = new DataContractJsonSerializer( this.Data.GetType() );
        context.HttpContext.Response.ContentType = "application/json";
        serializer.WriteObject( context.HttpContext.Response.OutputStream, 
            this.Data );
    }
}

1 Comment

+1 - best as I can tell, the DataContractJsonSerializer is still not directly supported in MVC 4, so this is an elegant solution.
7

This is the solution to use NewtonSoft Json.Net (for performance) I've found part of the solution here and on SO

public class JsonNetResult : ActionResult
    {
        public Encoding ContentEncoding { get; set; }
        public string ContentType { get; set; }
        public object Data { get; set; }

        public JsonSerializerSettings SerializerSettings { get; set; }
        public Formatting Formatting { get; set; }

        public JsonNetResult(object data, Formatting formatting)
            : this(data)
        {
            Formatting = formatting;
        }

        public JsonNetResult(object data):this()
        {
            Data = data;
        }

        public JsonNetResult()
        {
            Formatting = Formatting.None;
            SerializerSettings = new JsonSerializerSettings();
        }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            var response = context.HttpContext.Response;
            response.ContentType = !string.IsNullOrEmpty(ContentType)
              ? ContentType
              : "application/json";
            if (ContentEncoding != null)
                response.ContentEncoding = ContentEncoding;

            if (Data == null) return;

            var writer = new JsonTextWriter(response.Output) { Formatting = Formatting };
            var serializer = JsonSerializer.Create(SerializerSettings);
            serializer.Serialize(writer, Data);
            writer.Flush();
        }
    }

So that in my controller, I can do that

        return new JsonNetResult(result);

In my model, I can now have:

    [JsonProperty(PropertyName = "n")]
    public string Name { get; set; }

Note that now, you have to set the JsonPropertyAttribute to every property you want to serialize.

Comments

5

I know this is an old question but for those looking for just how to avoid properties from being serialized use the ScriptIgnoreAttribute in the namespace System.Web.Script.Serialization. Sadly still can't controll the name of the serialized properties but somebody might find this helpfull.

public class MyClass {

    [ScriptIgnoreAttribute]
    public bool PropertyNotSerialized { get; set; }

    public bool AnyProperty { get; set; }
}

Will output as Json result the following:

{"AnyProperty ": false}

1 Comment

I can confirm that this works. This namespace is located in the System.Web.Extensions assembly.
1

Easy answer: the DataContractJsonSerializer should respect the [DataContract] and [DataMember] attributes in the System.Runtime.Serialization namespace of the BCL.

1 Comment

To be clear for those unaware, Nate is expressing the -desired- behavior of MVC, not the -actual- behavior of MVC today. As of MVC2, MVC uses the JavaScriptSerializer, which does not support the DataMember attributes supported by the WCF DataContract serializer. A custom ActionResult is required today to make MVC use the DataContract/DataMember attributes.
1

These answers were helpful to me, but coming to this problem a few years later than everyone else I found that this code didn't work with the current framework version. This version works with Newtonsoft.Json and ASP NET Core 3.1:

/*
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
*/
public class JsonDataContractActionResult : IActionResult
{
    public JsonDataContractActionResult(object data) 
    {
        this.Data = data;
    }

    public object Data { get; private set; }

    public async Task ExecuteResultAsync(ActionContext context)
    {
        context.HttpContext.Response.ContentType = "application/json";
        JsonSerializer serializer = new JsonSerializer();
        serializer.NullValueHandling = NullValueHandling.Ignore;
        using (MemoryStream ms = new MemoryStream()) {
            using (StreamWriter sw = new StreamWriter(ms))
            {
                using (JsonWriter writer = new JsonTextWriter(sw))
                {
                    serializer.Serialize(writer, Data);
                }
            }
            byte[] b = ms.ToArray();
            await context.HttpContext.Response.Body.WriteAsync(b);
        }
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.