public
Created

  • Download Gist
RootFormatter.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
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();
}
}
}
WebApiConfig.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
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());
}
}
}

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?)

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();
    }

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?

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.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.