.NET 将复杂的 Dto 序列化为 JSON 字符串

问题描述 投票:0回答:1

我正在使用如下所示的 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"
  }
]
c# json .net json.net
1个回答
0
投票

您遇到以下问题:

  1. 您正在混合来自 System.Text.Json 和 Newtonsoft.Json 的属性。例如。

    JsonDerivedTypeAttribute
    来自 System.Text.Json,而
    JsonPropertyAttribute
    来自 Newtonsoft.Json。

    这些序列化器不兼容,您不能混合和匹配两者的属性。我建议您选择一个并坚持下去。

  2. 您也混合了来自两个序列化器的文档对象模型对象。您声明您的

    "oldValue"
    "newValue"
    dynamic
    。 ASP.NET Core 使用 SystemText.Json 进行模型绑定,对于
    dynamic
    object
    类型,此序列化程序将 JSON 反序列化为
    JsonElement
    。但 Json.NET 不理解这种类型,这就是为什么您看到它的单个属性被序列化:
    { "ValueKind": 1 }

  3. 一般来说,我建议不要使用

    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。

备注:

演示小提琴这里

© www.soinside.com 2019 - 2024. All rights reserved.