Answered by:
BaseJsonMediaTypeFormatter WriteToStream does not pass type to Serialize method

Question
-
User1809142031 posted
Due to this bug, when a web api action returns an interface or abstract type the root type information is not serialized when using a TypeNameHandling of Auto.
Example:
public abstract class Base
{
}
public class Derived : Base
{
}
public Base Get()
{
return new Derived(); // The json should contain a $type field
}Tuesday, February 16, 2016 6:32 PM
Answers
-
User36583972 posted
Hi Tydude4Christ,
From your description, I did a program on my side, it can be executed correctly. I put my code in the follow for your reference.
public class CustomJsonMediaTypeFormatter : JsonMediaTypeFormatter { public JsonSerializerSettings _jsonSerializerSettings; public CustomJsonMediaTypeFormatter() { _jsonSerializerSettings = CustomJsonSettings.Instance; // Fill out the mediatype and encoding we support SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json")); SupportedEncodings.Add(new UTF8Encoding(false, true)); } public override bool CanReadType(Type type) { return true; } public override bool CanWriteType(Type type) { return true; } public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger) { // Create a serializer JsonSerializer serializer = JsonSerializer.Create(_jsonSerializerSettings); // Create task reading the content return Task.Factory.StartNew(() => { using (StreamReader streamReader = new StreamReader(stream, SupportedEncodings.First())) { using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader)) { return serializer.Deserialize(jsonTextReader, type); } } }); } public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext) { // Create a serializer JsonSerializer serializer = JsonSerializer.Create(_jsonSerializerSettings); // Create task writing the serialized content return Task.Factory.StartNew(() => { using (StreamWriter streamWriter = new StreamWriter(stream, SupportedEncodings.First())) { using (JsonTextWriter jsonTextWriter = new JsonTextWriter(streamWriter)) { serializer.Serialize(jsonTextWriter, value); } } }); } } public static class CustomJsonSettings { private static JsonSerializerSettings _settings; public static JsonSerializerSettings Instance { get { if (_settings == null) { var settings = new JsonSerializerSettings(); settings.Converters.Add(new IsoDateTimeConverter { DateTimeStyles = System.Globalization.DateTimeStyles.AssumeUniversal }); settings.DateTimeZoneHandling = DateTimeZoneHandling.Local; settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; _settings = settings; } return _settings; } } }
You need to add the following code to my Application_Start method.
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.JsonFormatter); GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter); GlobalConfiguration.Configuration.Formatters.Add(new CustomJsonMediaTypeFormatter());
Information about JsonMediaTypeFormatter:
Best Regards,
Yohann Lu
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Friday, February 19, 2016 12:45 PM
All replies
-
User36583972 posted
Hi Tydude4Christ,
From to your description, I have made a sample on my side. It is works. You can try the following code to implement your needs.
Base class:[JsonConverter(typeof(MyCustomConverter))] public abstract class Base { public string Hello; public string Mobile; private class MyCustomConverter : JsonCreationConverter<Base> { protected override Base Create(Type objectType, Newtonsoft.Json.Linq.JObject jObject) { //if ("ID".Equals(jObject.Value<string>("1")) return new Derived(); } } } public class Derived : Base { public string ID { get; set; } public string name { get; set; } public string address { get; set; } } public abstract class JsonCreationConverter<T> : JsonConverter { /// <summary> /// this is very important, otherwise serialization breaks! /// </summary> public override bool CanWrite { get { return false; } } /// <summary> /// Create an instance of objectType, based properties in the JSON object /// </summary> /// <param name="objectType">type of object expected</param> /// <param name="jObject">contents of JSON object that will be /// deserialized</param> /// <returns></returns> protected abstract T Create(Type objectType, JObject jObject); public override bool CanConvert(Type objectType) { return typeof(T).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; // Load JObject from stream JObject jObject = JObject.Load(reader); // Create target object based on JObject T target = Create(objectType, jObject); // Populate the object properties serializer.Populate(jObject.CreateReader(), target); return target; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } }
API method:
[AllowAnonymous] public IHttpActionResult Get() { //return new string[] { "value1", "value2" }; return Ok(new Derived() { ID = "1", name = "name", address = "1222" , Hello="hello", Mobile="123456789"}); }
Icon:
Best Regards,
Yohann LuWednesday, February 17, 2016 5:44 AM -
User1809142031 posted
Thanks Yohann, that seems like it might work but is very dependent on knowing all derived types. What I did was simply to create a custom Media Type Formatter and register it. Here it is.
public class CustomJsonMediaTypeFormatter : JsonMediaTypeFormatter { public override void WriteToStream(Type type, object value, Stream writeStream, Encoding effectiveEncoding) { using (var jsonWriter = CreateJsonWriter(type, writeStream, effectiveEncoding)) { CreateJsonSerializer().Serialize(jsonWriter, value, type); } } }
Wednesday, February 17, 2016 1:21 PM -
User36583972 posted
Hi Tydude4Christ,
From your description, I did a program on my side, it can be executed correctly. I put my code in the follow for your reference.
public class CustomJsonMediaTypeFormatter : JsonMediaTypeFormatter { public JsonSerializerSettings _jsonSerializerSettings; public CustomJsonMediaTypeFormatter() { _jsonSerializerSettings = CustomJsonSettings.Instance; // Fill out the mediatype and encoding we support SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json")); SupportedEncodings.Add(new UTF8Encoding(false, true)); } public override bool CanReadType(Type type) { return true; } public override bool CanWriteType(Type type) { return true; } public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger) { // Create a serializer JsonSerializer serializer = JsonSerializer.Create(_jsonSerializerSettings); // Create task reading the content return Task.Factory.StartNew(() => { using (StreamReader streamReader = new StreamReader(stream, SupportedEncodings.First())) { using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader)) { return serializer.Deserialize(jsonTextReader, type); } } }); } public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext) { // Create a serializer JsonSerializer serializer = JsonSerializer.Create(_jsonSerializerSettings); // Create task writing the serialized content return Task.Factory.StartNew(() => { using (StreamWriter streamWriter = new StreamWriter(stream, SupportedEncodings.First())) { using (JsonTextWriter jsonTextWriter = new JsonTextWriter(streamWriter)) { serializer.Serialize(jsonTextWriter, value); } } }); } } public static class CustomJsonSettings { private static JsonSerializerSettings _settings; public static JsonSerializerSettings Instance { get { if (_settings == null) { var settings = new JsonSerializerSettings(); settings.Converters.Add(new IsoDateTimeConverter { DateTimeStyles = System.Globalization.DateTimeStyles.AssumeUniversal }); settings.DateTimeZoneHandling = DateTimeZoneHandling.Local; settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; _settings = settings; } return _settings; } } }
You need to add the following code to my Application_Start method.
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.JsonFormatter); GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter); GlobalConfiguration.Configuration.Formatters.Add(new CustomJsonMediaTypeFormatter());
Information about JsonMediaTypeFormatter:
Best Regards,
Yohann Lu
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Friday, February 19, 2016 12:45 PM