Skip to content

Instantly share code, notes, and snippets.

@andreea-anastasescu
Created August 16, 2013 07:55
Show Gist options
  • Save andreea-anastasescu/6248104 to your computer and use it in GitHub Desktop.
Save andreea-anastasescu/6248104 to your computer and use it in GitHub Desktop.
namespace Me.WebApi.Mobile
{
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.Net.Http;
using Newtonsoft.Json.Converters;
/// <summary>
/// Media formatter used to produce customized json serialization so that a root element is produced in the json for classes decorated with <see cref="JsonCustomRootAttribute"/>.
/// </summary>
public class RootFormatter : JsonMediaTypeFormatter
{
private string rootFieldName = null;
/// <summary>
/// Initializes a new instance of the RootFormatter class.
/// </summary>
public RootFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
}
/// <summary>
/// Determines whether this <see cref="T:System.Net.Http.Formatting.JsonMediaTypeFormatter"/> can write objects of the specified <paramref name="type"/>.
/// </summary>
/// <returns>
/// true if objects of this <paramref name="type"/> can be written, otherwise false.
/// </returns>
/// <param name="type">The type of object that will be written.</param>
public override bool CanWriteType(Type type)
{
return true;
}
/// <summary>
/// Returns a specialized instance of the <see cref="T:System.Net.Http.Formatting.MediaTypeFormatter"/> that can format a response for the given parameters.
/// </summary>
/// <returns>
/// Returns <see cref="T:System.Net.Http.Formatting.MediaTypeFormatter"/>.
/// </returns>
/// <param name="type">The type to format.</param><param name="request">The request.</param><param name="mediaType">The media type.</param>
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, System.Net.Http.HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
var formatter = new RootFormatter()
{
rootFieldName = GetRootFieldName(type)
};
// 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;
}
/// <summary>
/// Writes an object of the specified <paramref name="type"/> to the specified <paramref name="writeStream"/>. This method is called during serialization.
/// </summary>
/// <returns>
/// A <see cref="T:System.Threading.Tasks.Task"/> that will write the value to the writeStream.
/// </returns>
/// <param name="type">The type of object to write.</param><param name="value">The object to write.</param><param name="writeStream">The <see cref="T:System.IO.Stream"/> to which to write.</param>
/// <param name="writeStream"></param>
/// <param name="content">The <see cref="T:System.Net.Http.HttpContent"/> where the content is being written.</param><param name="transportContext">The <see cref="T:System.Net.TransportContext"/>.</param>
public override Task WriteToStreamAsync(Type type, object value,
Stream writeStream,
HttpContent content,
TransportContext transportContext)
{
if (string.IsNullOrEmpty(rootFieldName))
{
Task task = base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
return task;
}
StreamWriter writer = null;
// write the pre-amble
try
{
writer = new StreamWriter(writeStream);
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, writeStream, content, transportContext)
.ContinueWith(innerTask =>
{
if (innerTask.Status == TaskStatus.RanToCompletion)
{
writer.Write("}");
writer.Flush();
}
}, TaskContinuationOptions.ExecuteSynchronously)
.ContinueWith(innerTask =>
{
writer.Dispose();
return innerTask;
}, TaskContinuationOptions.ExecuteSynchronously)
.Unwrap();
}
/// <summary>
///
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
protected string GetRootFieldName(Type type)
{
if (type == null) throw new ArgumentNullException();
JsonCustomRootAttribute customRootAttribute;
if (type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof (IEnumerable<>))
{
customRootAttribute = GetSimpleTypeRootFieldName(type.GetGenericArguments()[0]);
if (customRootAttribute == null)
return null;
return customRootAttribute.CollectionTitle;
}
customRootAttribute = GetSimpleTypeRootFieldName(type);
if (customRootAttribute == null)
return null;
return customRootAttribute.Title;
}
/// <summary>
///
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
protected JsonCustomRootAttribute GetSimpleTypeRootFieldName(Type type){
IEnumerable<JsonCustomRootAttribute> attributes =
from x in type.GetCustomAttributes(typeof(JsonCustomRootAttribute), true)
select x as JsonCustomRootAttribute;
if (attributes.Count() == 0)
{
return null;
}
return attributes.First();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment