Derived type’s properties missing in JSON response from ASP.NET Core API
我的 ASP.NET Core 3.1 API 控制器的 JSON 响应缺少属性。当属性使用派生类型时会发生这种情况;在派生类型中定义但不在基/接口中定义的任何属性都不会序列化为 JSON。似乎在响应中缺乏对多态性的支持,就好像序列化是基于属性的定义类型而不是其运行时类型一样。如何更改此行为以确保 JSON 响应中包含所有公共属性?
示例:
我的 .NET Core Web API 控制器返回此对象,该对象具有接口类型的属性。
1
2 3 4 5 6 7 8 |
// controller returns this object
public class Result { public IResultProperty ResultProperty { get; set; } // property uses an interface type } public interface IResultProperty |
这是一个派生类型,它定义了一个名为 Value.
的新公共属性
1
2 3 4 |
public class StringResultProperty : IResultProperty
{ public string Value { get; set; } } |
如果我像这样从我的控制器返回派生类型:
1
2 3 |
return new MainResult {
ResultProperty = new StringResultProperty { Value =”Hi there!” } }; |
然后实际响应包含一个空对象(缺少 Value 属性):
我希望回复是:
1
2 3 |
{
“ResultProperty”: {“Value”:”Hi there!” } } |
n
- 这是最直接的解决方案。谢谢!
- 太感谢了。这对我来说非常有效。
我最终创建了一个自定义 JsonConverter(System.Text.Json.Serialization 命名空间),它强制 JsonSerializer 序列化为对象的运行时类型。请参阅下面的解决方案部分。它很长,但效果很好,并且不需要我在 API 设计中牺牲面向对象的原则。
一些背景知识:Microsoft 有一个 System.Text.Json 序列化指南,其中有一个标题为”序列化派生类的属性”的部分,其中包含与我的问题相关的良好信息。特别是它解释了为什么派生类型的属性没有序列化:
This behavior is intended to help prevent accidental exposure of data
in a derived runtime-created type.
如果这不是您关心的问题,那么可以通过显式指定派生类型或指定 object,在对 JsonSerializer.Serialize 的调用中覆盖该行为,例如:
1
2 3 4 5 |
// by specifying the derived type
jsonString = JsonSerializer.Serialize(objToSerialize, objToSerialize.GetType(), serializeOptions); // or specifying ‘object’ works too |
要使用 ASP.NET Core 完成此操作,您需要挂钩到序列化过程。我使用自定义 JsonConverter 执行此操作,该 JsonConverter 调用 JsonSerializer.Serialize 上面显示的一种方式。我还实现了对反序列化的支持,虽然在原始问题中没有明确要求,但无论如何几乎总是需要。 (奇怪的是,只支持序列化而不支持反序列化被证明是很棘手的。)
解决方案
我创建了一个基类 DerivedTypeJsonConverter,它包含所有的序列化
- 赞成,也在为此苦苦挣扎。此外,我很确定您知道这一点,但另一种选择是使用 Newtonsoft.Json,它为开箱即用的派生类型提供支持。
- 这是 System.Text.Json 的一个非常令人失望的警告。好旧的 Newtonsoft 听起来容易多了
- 我一直在为同样的问题苦苦挣扎,但出于某种原因,只有当我将应用程序部署在 Docker 容器中时。在本地它工作正常。我使用 net core 3.1.3,我的应用程序以 netcoreapp3.1 为目标。有人知道为什么吗?是否可以在运行时检查使用了哪个序列化程序?
- 他们为什么不简单地为此提供一个设置?
- docs.microsoft.com/en-us/dotnet/standard/serialization/…
n
- 当然看起来更干净,但是在序列化 MyBaseClass 的实例时在我的应用程序中导致了一个 StackOverflowException ,因为调用了 writer.Write 方法,该方法会退回到 AsRuntimeTypeConverter<T> 类型。
- 仅当我提前(即在编译时)知道要将接收到的 JSON 字符串反序列化为哪种派生类型时,这不是有效吗?
- @Frank 这是控制器响应的序列化,而不是请求数据的反序列化。如果您也想允许派生类型作为输入,您确实会遇到类似的问题,但解决方案会非常不同。
这是预期的结果。当你这样做时,你正在向上转换,所以将被序列化的是向上转换的对象,而不是实际的派生类型。如果您需要派生类型的东西,那么它必须是属性的类型。出于这个原因,您可能想要使用泛型。换句话说:
1
2 3 4 5 |
public class Result<TResultProperty>
where TResultProperty : IResultProperty { public TResultProperty ResultProperty { get; set; } // property uses an interface type } |
然后:
1
2 3 |
return new Result<StringResultProperty> {
ResultProperty = new StringResultProperty { Value =”Hi there!” } }; |
n
n
n
来源:https://www.codenong.com/59308763/