假设TypeNameHandling.Auto
用于以下Web Api控制器中的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控制器返回IEnumerable<A>
并且我们返回了很多B
,那么就会设置type属性。
是否可以更改此行为,以便它使用返回类型作为Json.Net的输入?
修复是返回Json而不是对象,但我发现这是一个不满意的解决方案。
答案 0 :(得分:3)
感谢这种情况,因为我认为这是我们的Json格式化程序中默认包含的内容。以下是一个自定义Json格式化程序,我尝试将“类型”信息传递给Serialize
JsonSerializer
方法。我在你的场景中尝试了下面的自定义格式化程序,它似乎工作正常。
(下面的大部分代码都是从现有的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;
}
}
答案 1 :(得分:0)
我必须使用以下内容更改Kiran的解决方案,以便将数组等写为[...]
而不是{$type: IEnumerab...., values = ...}
。
var contract = jsonSerializer.ContractResolver.ResolveContract(type);
// Only use the declared type if it is an object contract such that
// arrays, dictionaries, etc. aren't affected.
if (contract is JsonObjectContract)
{
jsonSerializer.Serialize(jsonTextWriter, value, type); // NOTE: passing in 'type' here
}
else
{
jsonSerializer.Serialize(jsonTextWriter, value);
}