提问人:Zenith 提问时间:9/6/2023 最后编辑:Uwe KeimZenith 更新时间:9/18/2023 访问量:195
如何序列化泛型对象
How to serialize a generic object
问:
我很抱歉这个糟糕的标题,但我真的不知道如何以其他方式表达。具体问题如下:
我有一个带有控制器的 .NET API。我有一个控制器,它引用了泛型 Entity 类型的服务,并返回一个泛型 的 ,如下所示:IEnumerable
Dto<>
public class SomeController {
private readonly Service<SomeEntity> _someService;
//constructor
[HttpGet]
public async Task<IEnumerable<Dto<SomeEntity>>> AsyncGetSomeData() {
return await _someService.AsyncGet();
}
}
服务接口如下所示;它的目的是调用实体的存储库,并将实体转换为 DTO,其方式可以用于给定的:Dto
Entity
public interface Service<T> where T : Entity
{
Task<IEnumerable<Dto<T>>?> AsyncGet();
}
实体界面如下所示(目前):
public interface Entity {}
Dto 界面如下所示:
public interface Dto<T> where T : Entity.Entity {}
Entity 接口的实现如下所示:
public record SomeEntity (Guid id) : Entity
Dto 的实现如下所示:
public record SomeDto(string Id) : Dto<SomeEntity>;
当我以现在设置控制器的方式返回对象时(如上所述),将返回空的 json 对象。这是因为 json 序列化程序无法确定可以序列化为 .更改要返回的签名并将项目强制转换为实际上可以正确序列化对象,这一事实证明了这一点。Dto<SomeEntity>
SomeDto
IEnumerable<SomeDto>
IEnumerable<Dto<SomeEntity>>
SomeDto
我的问题是:是否可以更改设置或映射或其他内容以避免更改签名和投射?Dto<SomeEntity>
SomeDto
我尝试过找到这样的例子(Google 和 StackOverflow),但正如你所看到的,这个问题用一句话来概括是相当复杂的(我正在接受标题建议来改进这个问题)。我希望有人能给我指出一个重复的问题,或者给我一个答案。
编辑:
我正在尝试使用 .我有两个实体类型和相应的 dto:JsonDerivedTypeAttribute
public sealed record CourseDto() : Dto<CourseEntity>
public sealed record CourseDefinitionDto() : Dto<CourseDefinitionEntity>
然后,我在接口上定义了:JsonDerivedTypeAttribute
Dto
[JsonDerivedType(derivedType: typeof(CourseDto), "a")]
[JsonDerivedType(derivedType: typeof(CourseDefinitionDto), "b")]
public interface Dto<T> where T : Entity.Entity;
无论我在 typeDiscriminator 参数中使用什么,我都会收到以下错误:
“System.InvalidOperationException:指定类型'Application.Data.Dto.CourseDto'不是多态类型'Application.Data.Dto.Dto'1[Application.Data.Entity.CourseDefinitionEntity]'支持的派生类型。派生类型必须可分配给基类型,不能是泛型的,并且不能是 abstact 类或接口,除非指定了“JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor”。
答:
如果您使用的是 System.Text.Json,则应查看 JsonDerivedTypeAttribute 和 JsonConverterAttribute
以下是您可以执行的操作的快速示例JsonDerivedTypeAttribute
public interface Entity { }
[JsonDerivedType(typeof(SomeDto), "#some")]
[JsonDerivedType(typeof(AnotherDto), "#another")]
public interface Dto { }
public interface Dto<T> : Dto where T : Entity { }
public class SomeEntity : Entity { }
public class AnotherEntity : Entity { }
public class SomeDto : Dto<SomeEntity> { }
public class AnotherDto : Dto<AnotherEntity> { }
要使用的示例
Dto some = new SomeDto();
Dto another = new AnotherDto();
var options = new JsonSerializerOptions();
var someJson = JsonSerializer.Serialize(some, options);
var anotherJson = JsonSerializer.Serialize(another, options);
Console.WriteLine(someJson);
Console.WriteLine(anotherJson);
var someDto = JsonSerializer.Deserialize<Dto>(someJson);
var anotherDto = JsonSerializer.Deserialize<Dto>(anotherJson);
Console.WriteLine(someDto?.GetType().Name);
Console.WriteLine(anotherDto?.GetType().Name);
输出
{"$type":"#some"}
{"$type":"#another"}
SomeDto
AnotherDto
编辑
下面是一个提示,让你的控制器能够返回接口,而不是泛型接口。
你可以这样做,因为接口是协变的。Dto
Dto<T>
IEnumerable<T>
public interface Service<T> where T : Entity
{
Task<IEnumerable<Dto<T>>?> GetAsync();
}
您的操作签名变为
[HttpGet]
public async Task<IEnumerable<Dto>?> GetSomeDataAsync()
{
return await _someService.GetAsync();
}
我切换了方法名称中的单词,以更符合 C# 命名约定。Async
评论
JsonDerivedTypeAttribute
Dto<T>
Dto
JsonDerivedTypeAttribute
Dto<T>
不能装饰,因为它是通用的。我做了一个编辑来简化代码。如您所见,只有操作签名发生了变化。使用非泛型接口欺骗序列化程序。JsonDervivedTypeAttribute
Dto
我的建议是你给它两个类:
public class MyClass<Tin,Tout>
评论
Service
Task<IEnumerable<Tout>?> AsyncGet();
_someService
AsyncGetSomeData
上一个:基于运行时值获取泛型类型的字典
下一个:如何从一组状态中计算总进度状态?
评论
Dto<SomeEntity>
SomeDto
dto<SomeEntity>