Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A JSON formatter for ASP.NET Web API to support Ember Data
public class MyEmberJsonMediaTypeFormatter : JsonMediaTypeFormatter
{
private int _maxDepth = 256;
private JsonSerializerSettings _jsonSerializerSettings;
public MyEmberJsonMediaTypeFormatter()
{
_jsonSerializerSettings = CreateDefaultSerializerSettings();
}
public override System.Threading.Tasks.Task WriteToStreamAsync(System.Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
{
var root = GetRootFieldName(type, value);
var obj = new ExpandoObject() as IDictionary<string, Object>;
obj[root] = value;
return base.WriteToStreamAsync(type, obj as object, writeStream, content, transportContext);
}
public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
{
return Task.FromResult(ReadFromStream(type, readStream, content, formatterLogger)); ;
}
private object ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
{
var root = GetRootFieldName(type);
var contentHeaders = content == null ? null : content.Headers;
// If content length is 0 then return default value for this type
if (contentHeaders != null && contentHeaders.ContentLength == 0)
return GetDefaultValueForType(type);
// Get the character encoding for the content
var effectiveEncoding = SelectCharacterEncoding(contentHeaders);
try
{
using (var reader = (new StreamReader(readStream, effectiveEncoding)))
{
var json = reader.ReadToEnd();
var jo = JObject.Parse(json);
return jo.SelectToken(root, false).ToObject(type);
}
}
catch (Exception e)
{
if (formatterLogger == null)
{
throw;
}
formatterLogger.LogError(String.Empty, e);
return GetDefaultValueForType(type);
}
}
private string GetRootFieldName(Type type, dynamic value=null)
{
//get element type if array
if (value != null && (value is IEnumerable || type.IsArray))
type = value[0].GetType();
var attrs = type.CustomAttributes.Where(x => x.AttributeType == typeof (Newtonsoft.Json.JsonObjectAttribute)).ToList();
if (attrs.Any())
{
var titles = attrs.First().NamedArguments.Where(arg => arg.MemberName == "Title")
.Select(arg => arg.TypedValue.Value.ToString()).ToList();
if (titles.Any()) return titles.First();
}
return type.Name;
}
}
[JsonObject(Title = "users")]
public class UserViewModel
{
public string username { get; set; }
public string email { get; set; }
public string password { get; set; }
}
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
GlobalConfiguration.Configuration.Formatters.Insert(0, new MyEmberJsonMediaTypeFormatter());
}
}
@PhilippRoessner

This comment has been minimized.

Copy link

commented Apr 29, 2014

it doesn't work here with one-to-many relationship. Ember sais: "Cannot set property 'store' of undefined" Am I missing something here?

@PhilippRoessner

This comment has been minimized.

Copy link

commented Apr 30, 2014

Line 58 of MyEmberJsonMediaTypeFormatter.cs thorws exception when dynamic "value" is of Type IEnumerable and not Array. Instead I tried "type = Enumerable.FirstOrDefault(value).GetType();" and it works.

@rowandh

This comment has been minimized.

Copy link

commented Aug 4, 2014

An IEnumerable does not guarantee indexation, but it is used as such in GetRootName. A workaround is to cast the IEnumerable to a list - just be aware of any performance concerns as this may execute a DB query.

        var materializedValue = value as IEnumerable;

        //get element type if array
        if (materializedValue != null)
        {
            var indexable = materializedValue as IList<object> ?? materializedValue.Cast<object>().ToList();
            type = indexable[0].GetType();
        }
@haldiggs

This comment has been minimized.

Copy link

commented Feb 9, 2016

genius... even though its old. I made mine all work my creating custom binding DataContract objects and using a DataMember(Name = "") decorator but this makes that seem painful. I wish I had thought to write this.

@wayne-o

This comment has been minimized.

Copy link

commented Dec 9, 2016

good catch @rowandh 💯 :)

@wayne-o

This comment has been minimized.

Copy link

commented Dec 10, 2016

This doesn't handle empty lists - what's the best way of handling that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.