asp.net-web-api - Json.Net TypeNameHandling.Auto 和 Asp Web Api Controller 给出了意外的行为

标签 asp.net-web-api json.net

假设TypeNameHandling.Auto用于以下 Web Api Controller 中的 Json.net:

class A {}
class B : A {}

class FooController : ApiController
{
  public A Get() {
    return new A();
  }
}

class BarController : ApiController
{
  public A Get() {
    return new B();
  }
}

然后我期望生成的 Json 是:

{}

{'$type':...}

但是Bar的输出也是{} 。另一方面,如果 API Controller 返回 IEnumerable<A>我们返回了很多B然后设置 type 属性。

是否可以更改此行为,使其使用返回类型作为 Json.Net 的输入?

修复方法是返回 Json 而不是对象,但我发现这是一个不满意的解决方案。

最佳答案

感谢这个场景,因为我认为这应该默认包含在我们的 Json 格式化程序中。以下是一个自定义 Json 格式化程序,我尝试将“类型”信息传递给 JsonSerializerSerialize 方法。我根据您的场景尝试了以下自定义格式化程序,它似乎工作正常。

(下面的大部分代码都是从现有的 Web API 源代码中挑选出来的,以适合您的场景。)

public class CustomJsonFormatter : JsonMediaTypeFormatter
{
    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
    {
        try
        {
            Encoding effectiveEncoding = SelectCharacterEncoding(content == null ? null : content.Headers);

            if (!UseDataContractJsonSerializer)
            {
                using (JsonTextWriter jsonTextWriter = new JsonTextWriter(new StreamWriter(writeStream, effectiveEncoding)) { CloseOutput = false })
                {
                    if (Indent)
                    {
                        jsonTextWriter.Formatting = Newtonsoft.Json.Formatting.Indented;
                    }

                    JsonSerializer jsonSerializer = JsonSerializer.Create(this.SerializerSettings);
                    jsonSerializer.Serialize(jsonTextWriter, value, type); //NOTE: passing in 'type' here
                    jsonTextWriter.Flush();
                }
            }
            else
            {
                return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
            }

            return TaskHelpers.Completed();
        }
        catch (Exception e)
        {
            return TaskHelpers.FromError(e);
        }
    }
}

internal class TaskHelpers
{
    private static readonly Task _defaultCompleted = FromResult<AsyncVoid>(default(AsyncVoid));

    /// <summary>
    /// Used as the T in a "conversion" of a Task into a Task{T}
    /// </summary>
    private struct AsyncVoid
    {
    }

    internal static Task<TResult> FromResult<TResult>(TResult result)
    {
        TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
        tcs.SetResult(result);
        return tcs.Task;
    }

    /// <summary>
    /// Returns an error task. The task is Completed, IsCanceled = False, IsFaulted = True
    /// </summary>
    internal static Task FromError(Exception exception)
    {
        return FromError<AsyncVoid>(exception);
    }

    /// <summary>
    /// Returns an error task of the given type. The task is Completed, IsCanceled = False, IsFaulted = True
    /// </summary>
    /// <typeparam name="TResult"></typeparam>
    internal static Task<TResult> FromError<TResult>(Exception exception)
    {
        TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
        tcs.SetException(exception);
        return tcs.Task;
    }

    /// <summary>
    /// Returns a completed task that has no result. 
    /// </summary>        
    internal static Task Completed()
    {
        return _defaultCompleted;
    }
}

关于asp.net-web-api - Json.Net TypeNameHandling.Auto 和 Asp Web Api Controller 给出了意外的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20167895/

相关文章:

c# - REST 超媒体 URI 更改基于 Web API 中的上下文 (HATEOAS)

c# - 如何创建 C# 类以反序列化以方括号开头和结尾的 JSON 字符串

.net - JSON.net 不包括 EmitDefaultValue 为 false 的空属性

c# - 为什么 RestSharp 以不同的方式反序列化两个日期?

c# - Refit和Web API之间如何共享服务方法URL?

angularjs - 使用深度链接刷新 html 模式 Angular 应用程序

c# - ASP.NET Web API 应用程序在启用 TransferRequestHandler 的情况下为不存在的路由返回 HTTP 500

asp.net-web-api - 如何告诉 RavenDB 忽略属性而不是 WebAPI?

c# - 将动态类型转换为字典 C#

json - 将 log4net 属性序列化为 Json 时的附加 Stacktrace 信息