我正在使用如下所示的 Dto 结构。我将省略有关其他派生类型的信息,因为它们不是当前问题的一部分。请注意,
OldValue
和 NewValue
属性实际上可以是许多其他 Dto(多类型)之一。
[JsonDerivedType(typeof(ChangeBucketDto), "changeBucketDto")][JsonDerivedType(typeof(TrackedChangeDto), "trackedChangeDto")]
public class Dto
{
/// <summary>
/// The unique identifier of the entity
/// </summary>
[JsonProperty("key")]
public required string Key { get; set; }
}
public class ChangeBucketDto : Dto
{
[Required]
[JsonProperty("text")]
public required string Text { get; set; }
[Required]
[JsonProperty("changes")]
public required List<TrackedChangeDto> Changes { get; set; }
}
public class TrackedChangeDto : Dto
{
[Required]
[JsonProperty("origin")]
public required ChangeOrigin Origin { get; set; }
[JsonProperty("oldValue")]
public dynamic? OldValue { get; set; }
[JsonProperty("newValue")]
public dynamic? NewValue { get; set; }
[Required]
[JsonProperty("changeType")]
public required string ChangeType { get; set; }
[Required]
[JsonProperty("displayOrder")]
public required List<string> DisplayOrder { get; set; }
[Required]
[JsonProperty("lastModified")]
public required DateTime LastModified { get; set; }
[Required]
[JsonProperty("live")]
public required bool Live { get; set; }
}
使用从以下函数获得的 JSON。
public async Task<ActionResult<dynamic>> WriteChangesAsJson(StorageEnvironment environment, [FromBody] List<ChangeBucketDto> json)
{
return await WriteAllAsJson(environment, json);
}
我会尝试将其序列化为可以用来处理的Dto。
TDto
类型源自启动时配置的引用服务的控制器。此类型也使用 AutoMapper
映射到相应的 Model
。
async Task IStorageService<TDto>.WriteAllAsync(ConnectionPropertiesModel connectionProperties, dynamic json)
{
try
{
string stringified = JsonConvert.SerializeObject(json, Formatting.Indented);
// Write a new file content to the blob
await _repository.WriteAsync(connectionProperties, jsonString:stringified);
}
catch (ConnectionPropertiesException connectionEx)
{
IError error = _errorFactory.CreateError(ErrorCode.ServiceNoAccess, _exceptionResourceManager.GetString("ServiceAccessExc")!);
throw new ServiceAccessException(error, connectionEx);
}
}
生成的
ChangeBucket
数组在正确的存储桶中正确包含 TrackedChange
对象,但是 OldValue
和 NewValue
将不包含相应 Dto 的值。相反,它会显示 ValueKind
。
{
"text": "Highlights",
"changes": [
{
"origin": {
"ContainerName": "highlights",
"FileName": "highlights.json"
},
"oldValue": {
"ValueKind": 1
},
"newValue": {
"ValueKind": 1
},
"changeType": "Update",
"displayOrder": [
"key",
"languages",
"enabled",
"repeat",
"timeSpan",
"timeStampFrom",
"timeStampTo"
],
"lastModified": "2024-05-13T14:13:11.629Z",
"live": true,
"key": "change-highlights-Maintenance"
}
],
"key": "highlights"
},
编辑:我现在意识到
System.Text.Json
和NewtonSoft.Json
之间存在一些混合,导致了这里的问题。我的最终目标是能够仅使用其中之一来读取这个对象作为一个整体,但我不太确定我需要什么才能到达那里。
EDIT2: 每个请求我都包含了完整的 JSON,用于了解更改跟踪控制器的发布方式,包括它可以获得的 Dto 的两个示例。
[
{
"text": "Vragen",
"changes": [],
"key": "questions"
},
{
"text": "Berichten",
"changes": [
{
"origin": {
"ContainerName": "answers",
"FileName": "answers.json"
},
"oldValue": {
"key": "Foo",
"languages": [
{ "NL": "Bor" },
{ "EN": "Bar" }
],
"type": "message"
},
"newValue": {
"key": "Foo",
"languages": [
{ "NL": "Bar" },
{ "EN": "Bar" }
],
"type": "message"
},
"changeType": "Update",
"displayOrder": [
"key",
"languages",
"type"
],
"lastModified": "2024-05-13T17:23:30.509Z",
"live": true,
"key": "change-highlights-Maintenance"
}
],
"key": "answers"
},
{
"text": "FAQs",
"changes": [],
"key": "faqs"
},
{
"text": "Dialogen",
"changes": [],
"key": "dialogs"
},
{
"text": "Highlights",
"changes": [
{
"origin": {
"ContainerName": "highlights",
"FileName": "highlights.json"
},
"oldValue": {
"key": "Foo",
"languages": [
{ "NL": "Bor" },
{ "EN": "Bar" }
],
"enabled": "true",
"repeat": "true",
"timeSpan": 7,
"timeStampFrom": "2023-12-13T11:50",
"timeStampTo": "2023-12-13T17:00"
},
"newValue": {
"key": "Foo",
"languages": [
{ "NL": "Bar" },
{ "EN": "Bar" }
],
"enabled": "true",
"repeat": "true",
"timeSpan": 7,
"timeStampFrom": "2023-12-13T11:50",
"timeStampTo": "2023-12-13T18:00"
},
"changeType": "Update",
"displayOrder": [
"key",
"languages",
"enabled",
"repeat",
"timeSpan",
"timeStampFrom",
"timeStampTo"
],
"lastModified": "2024-05-13T17:23:30.509Z",
"live": true,
"key": "change-highlights-Maintenance"
}
],
"key": "highlights"
},
{
"text": "Groepsincidenten",
"changes": [],
"key": "groupincidents"
},
{
"text": "Kennisbanken",
"changes": [],
"key": "knowledgesources"
}
]
您遇到以下问题:
您正在混合来自 System.Text.Json 和 Newtonsoft.Json 的属性。例如。
JsonDerivedTypeAttribute
来自 System.Text.Json,而 JsonPropertyAttribute
来自 Newtonsoft.Json。
这些序列化器不兼容,您不能混合和匹配两者的属性。我建议您选择一个并坚持下去。
您也混合了来自两个序列化器的文档对象模型对象。您声明您的
"oldValue"
和 "newValue"
为 dynamic
。 ASP.NET Core 使用 SystemText.Json 进行模型绑定,对于 dynamic
和 object
类型,此序列化程序将 JSON 反序列化为 JsonElement
。但 Json.NET 不理解这种类型,这就是为什么您看到它的单个属性被序列化:{ "ValueKind": 1 }
。
一般来说,我建议不要使用
dynamic
。它会让您认为您正在编写普遍适用的代码,但实际上您最终编写了“伪通用”代码,该代码可以成功编译,但包含仅在运行时出现的各种隐藏假设和依赖项。
由于 ASP.NET Core 使用 System.Text.Json 进行模型绑定,因此我建议您也使用它进行序列化。按如下方式修改您的模型:
[JsonDerivedType(typeof(ChangeBucketDto), "changeBucketDto"), JsonDerivedType(typeof(TrackedChangeDto), "trackedChangeDto")]
public class Dto
{
/// <summary>
/// The unique identifier of the entity
/// </summary>
[JsonPropertyName("key")]
public required string Key { get; set; }
}
public class ChangeBucketDto : Dto
{
[JsonRequired]
[JsonPropertyName("text")]
public required string Text { get; set; }
[JsonRequired]
[JsonPropertyName("changes")]
public required List<TrackedChangeDto> Changes { get; set; }
}
public class TrackedChangeDto : Dto
{
[JsonRequired]
[JsonPropertyName("origin")]
public required ChangeOrigin Origin { get; set; }
[JsonPropertyName("oldValue")]
public JsonNode? OldValue { get; set; }
[JsonPropertyName("newValue")]
public JsonNode? NewValue { get; set; }
[JsonRequired]
[JsonPropertyName("changeType")]
public required string ChangeType { get; set; }
[JsonRequired]
[JsonPropertyName("displayOrder")]
public required List<string> DisplayOrder { get; set; }
[JsonRequired]
[JsonPropertyName("lastModified")]
public required DateTime LastModified { get; set; }
[JsonRequired]
[JsonPropertyName("live")]
public required bool Live { get; set; }
}
public class ChangeOrigin
{
[JsonPropertyName("ContainerName")]
public string? ContainerName { get; set; }
[JsonPropertyName("FileName")]
public string? FileName { get; set; }
}
您应该能够成功地往返 JSON。
备注:
using Newtonsoft.Json
命名空间引用。[JsonProperty("name")]
替换为 [JsonPropertyName("name")]
。[JsonRequired]
或仅使用 required
关键字使 System.Text.Json 在缺少属性时抛出错误。JsonNode
而不是 dynamic
来捕获自由格式的 JSON。如果您有一些 POCO 想要序列化到 JsonNode
,您可以使用 JsonSerializer.SerializeToNode()
来实现。演示小提琴这里。