Skip to content

Instantly share code, notes, and snippets.

@eed3si9n
Created January 17, 2013 06:24
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eed3si9n/4554127 to your computer and use it in GitHub Desktop.
Save eed3si9n/4554127 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Net;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
using System.Net.Http;
using Newtonsoft.Json.Converters;
using System.Web.Http;
// based on http://www.west-wind.com/weblog/posts/2012/Apr/02/Creating-a-JSONP-Formatter-for-ASPNET-Web-API
namespace FooService
{
public class RootFormatter: JsonMediaTypeFormatter
{
private string RootFieldName = null;
public RootFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
// SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));
}
public override bool CanWriteType(Type type)
{
return true;
}
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, System.Net.Http.HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
var formatter = new RootFormatter()
{
RootFieldName = GetRootFieldName(type)
};
// this doesn't work unfortunately
//formatter.SerializerSettings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
// You have to reapply any JSON.NET default serializer Customizations here
formatter.SerializerSettings.Converters.Add(new StringEnumConverter());
formatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
return formatter;
}
public override Task WriteToStreamAsync(Type type, object value,
Stream stream,
HttpContent content,
TransportContext transportContext)
{
if (string.IsNullOrEmpty(RootFieldName))
return base.WriteToStreamAsync(type, value, stream, content, transportContext);
StreamWriter writer = null;
// write the pre-amble
try
{
writer = new StreamWriter(stream);
writer.Write("{\"" + RootFieldName + "\":");
writer.Flush();
}
catch (Exception ex)
{
try
{
if (writer != null)
writer.Dispose();
}
catch { }
var tcs = new TaskCompletionSource<object>();
tcs.SetException(ex);
return tcs.Task;
}
return base.WriteToStreamAsync(type, value, stream, content, transportContext)
.ContinueWith(innerTask =>
{
if (innerTask.Status == TaskStatus.RanToCompletion)
{
writer.Write("}");
writer.Flush();
}
}, TaskContinuationOptions.ExecuteSynchronously)
.ContinueWith(innerTask =>
{
writer.Dispose();
return innerTask;
}, TaskContinuationOptions.ExecuteSynchronously)
.Unwrap();
}
protected string GetRootFieldName(Type type)
{
var attrs =
from x in type.CustomAttributes
where x.AttributeType == typeof(Newtonsoft.Json.JsonObjectAttribute)
select x;
if (attrs.Count() < 1)
{
return null;
} // if
var titles =
from arg in attrs.First().NamedArguments
where arg.MemberName == "Title"
select arg.TypedValue.Value.ToString();
if (titles.Count() < 1)
{
return null;
} // if
return titles.First();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace FooService
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
GlobalConfiguration.Configuration.Formatters.Insert(0, new RootFormatter());
}
}
}
@goyaweb
Copy link

goyaweb commented Oct 16, 2013

Getting this error:
Error 10 'System.Type' does not contain a definition for 'CustomAttributes' and no extension method 'CustomAttributes' accepting a first argument of type 'System.Type' could be found (are you missing a using directive or an assembly reference?)

@sephwrath
Copy link

If you update the GetRootFieldName function as follows it will handle generic lists as well.

    protected string GetRootFieldName(Type type)
    {
        if (type.IsGenericType)
        {
            Type[] args = type.GetGenericArguments();
            if (args.Length > 0) type = args[0];
        }
        var attrs =
            from x in type.CustomAttributes
            where x.AttributeType == typeof(Newtonsoft.Json.JsonObjectAttribute)
            select x;
        if (attrs.Count() < 1)
        {
            return null;
        } // if

        var titles =
            from arg in attrs.First().NamedArguments
            where arg.MemberName == "Title"
            select arg.TypedValue.Value.ToString();
        if (titles.Count() < 1)
        {
            return null;
        } // if
        return titles.First();
    }

@jbalajkpm
Copy link

Getting error:
Error 10 'System.Type' does not contain a definition for 'CustomAttributes' and no extension method 'CustomAttributes' accepting a first argument of type 'System.Type' could be found (are you missing a using directive or an assembly reference?)

Can someone please help?

@eibrahim
Copy link

eibrahim commented Apr 9, 2014

thanks for your help, i have tweaked a little more at https://gist.github.com/eibrahim/10286724 to be more dynamic and handle arrays/enumerables and generics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment