JSON Circular References

Topics: jQuery, Web Api
May 1, 2011 at 2:01 PM

Does this project add support for circular references to JSON operations or allow DataContracts marked with IsReference?

May 5, 2011 at 6:24 PM
Edited May 5, 2011 at 6:29 PM

You can use Newtonsoft Json Serializer to support circular references with json.  You'll have to create a custom MediaTypeFormatter to use their library instead of DataContractJsonSerializer.

Something like below: (Haven't tested!!!)

    /// <summary>
    /// Defines a Json <see cref="MediaTypeFormatter"/> using Json.NET serialization/deserialization.
    /// </summary>
    public class JsonNetMediaTypeFormatter : MediaTypeFormatter
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="JsonNetMediaTypeFormatter"/> class.
        /// </summary>
        public JsonNetMediaTypeFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/json"));
        }
        
        /// <summary>
        /// Called when [read from stream].
        /// </summary>
        /// <param name="type">The type of object to deserialize.</param>
        /// <param name="stream">The stream.</param>
        /// <param name="httpContentHeaders">The HTTP content headers.</param>
        /// <returns>The de-serialized object.</returns>
        /// <remarks></remarks>
        public override Object OnReadFromStream(Type objectType, Stream stream, HttpContentHeaders httpContentHeaders)
        {
            if (stream == null)
            {
                return null;
            }

            var jsonSerializer = new JsonSerializer
                                     {
                                         NullValueHandling = NullValueHandling.Ignore,
                                         MissingMemberHandling = MissingMemberHandling.Ignore,
                                         PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                                         ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                                     };

            try
            {
                using (var streamReader = new StreamReader(stream))
                using (var jsonTextReader = new JsonTextReader(streamReader))
                {
                    return (objectType != null)
                               ? jsonSerializer.Deserialize(jsonTextReader, objectType)
                               : jsonSerializer.Deserialize(jsonTextReader);
                }
            }
            catch (JsonReaderException)
            {
                // Logging...
                throw;
            }
            catch (JsonSerializationException)
            {
                // Logging...
                throw;
            }
        }

        /// <summary>
        /// Called to write an object to the <paramref name="stream"/>.
        /// </summary>
        /// <param name="type">The type of object to write.</param>
/// <param name="value">The object instance to write.</param>
/// <param name="stream">The <see cref="T:System.IO.Stream"/> to which to write.</param>
/// <param name="contentHeaders">The content headers from the respective request or response.</param>
/// <param name="context">The <see cref="T:System.Net.TransportContext"/>.</param>
public override void OnWriteToStream(Type objectType, Object value, Stream stream, HttpContentHeaders contentHeaders, TransportContext context) { if (instance == null) { return; } var jsonSerializer = new JsonSerializer { NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore, PreserveReferencesHandling = PreserveReferencesHandling.Objects, ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; try { var streamWriter = new StreamWriter(stream); var jsonTextWriter = new JsonTextWriter(streamWriter); jsonSerializer.Serialize(jsonTextWriter, instance); jsonTextWriter.Flush(); } catch (JsonWriterException) { // Logging... stream.Dispose(); throw; } catch (JsonSerializationException) { // Logging... stream.Dispose(); throw; } } }
May 6, 2011 at 12:17 PM

Thank you! 

 

How can I register my MediaTypeFormatter? I can't seem to find anything in the documentation? Am I missing it?

May 6, 2011 at 12:27 PM

There is an AddFormatters() method on the fluent configuration builder.   

See how it is done here http://wcf.codeplex.com/SourceControl/changeset/view/44d95f912edf#WCFWebApi%2fHttp%2fsamples%2fContactManager_Advanced%2fGlobal.asax.cs

Coordinator
May 11, 2011 at 1:39 AM

Replacing our default formatter to use something like JSON.NET requires a little more work. First you need a formatter.

public class NewtonsoftJsonFormatter : MediaTypeFormatter
{
    public NewtonsoftJsonFormatter()
    {
        this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
        this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/json"));
    }

    public override object OnReadFromStream(Type type, System.IO.Stream stream, HttpContentHeaders contentHeaders)
    {
        var serializer = new JsonSerializer();
        using (var sr = new StreamReader(stream))
        using (var reader = new JsonTextReader(sr))
        {
            var result = serializer.Deserialize(reader, type);
            return result;
        }
    }

    public override void OnWriteToStream(Type type, object value, System.IO.Stream stream, HttpContentHeaders contentHeaders, System.Net.TransportContext context)
    {
        var serializer = new JsonSerializer();

        using (var sw = new StreamWriter(stream))
        using (var writer = new JsonTextWriter(sw))
        {
            serializer.Serialize(writer, value);
        }
    }
}

Then use this extension method to register it. 

public static class HttpHostConfigurationExtensions {
  public static void UseNewtonsoftJson(this HttpHostConfiguration config) {
    config.OperationHandlerFactory.Formatters.Insert(0, new NewtonsoftJsonFormatter());
  }
}