【发布时间】:2020-04-30 08:25:38
【问题描述】:
我们有一个 API,它只是将传入的 JSON 文档发布到消息总线,并为每个文档分配一个 GUID。我们正在从 .Net Core 2.2 升级到 3.1,并打算用新的 System.Text.Json 库替换 NewtonSoft。
我们反序列化传入的文档,将 GUID 分配给其中一个字段,然后在发送到消息总线之前重新序列化。不幸的是,重新序列化失败,Operation is not valid due to the current state of the object 例外。
这是一个显示问题的控制器:-
using System;
using System.Net;
using Project.Models;
using Microsoft.AspNetCore.Mvc;
using System.IO;
using System.Text;
using System.Text.Json;
namespace Project.Controllers
{
[Route("api/test")]
public class TestController : Controller
{
private const string JSONAPIMIMETYPE = "application/vnd.api+json";
public TestController()
{
}
[HttpPost("{eventType}")]
public async System.Threading.Tasks.Task<IActionResult> ProcessEventAsync([FromRoute] string eventType)
{
try
{
JsonApiMessage payload;
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8)) {
string payloadString = await reader.ReadToEndAsync();
try {
payload = JsonSerializer.Deserialize<JsonApiMessage>(payloadString);
}
catch (Exception ex) {
return StatusCode((int)HttpStatusCode.BadRequest);
}
}
if ( ! Request.ContentType.Contains(JSONAPIMIMETYPE) )
{
return StatusCode((int)HttpStatusCode.UnsupportedMediaType);
}
Guid messageID = Guid.NewGuid();
payload.Data.Id = messageID.ToString();
// we would send the message here but for this test, just reserialise it
string reserialisedPayload = JsonSerializer.Serialize(payload);
Request.HttpContext.Response.ContentType = JSONAPIMIMETYPE;
return Accepted(payload);
}
catch (Exception ex)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
}
}
}
JsonApiMessage 对象的定义如下:-
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Project.Models
{
public class JsonApiMessage
{
[JsonPropertyName("data")]
public JsonApiData Data { get; set; }
[JsonPropertyName("included")]
public JsonApiData[] Included { get; set; }
}
public class JsonApiData
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("attributes")]
public JsonElement Attributes { get; set; }
[JsonPropertyName("meta")]
public JsonElement Meta { get; set; }
[JsonPropertyName("relationships")]
public JsonElement Relationships { get; set; }
}
}
一个示例调用如下所示:-
POST http://localhost:5000/api/test/event
Content-Type: application/vnd.api+json; charset=UTF-8
{
"data": {
"type": "test",
"attributes": {
"source": "postman",
"instance": "jg",
"level": "INFO",
"message": "If this comes back with an ID, the API is probably working"
}
}
}
当我在 Visual Studio 的断点处检查 payload 的内容时,它在顶层看起来没问题,但 JsonElement 位看起来不透明,所以我不知道它们是否被正确解析。它们的结构可以变化,所以我们只关心它们是有效的 JSON。在旧的 NewtonSoft 版本中,它们是 JObjects。
添加 GUID 后,在断点检查时它会出现在 payload 对象中,但我怀疑该问题与对象中的其他元素为只读或类似内容有关。
【问题讨论】:
-
谢谢 - 我已经编辑了帖子以包含更精确的示例。
-
@JeremyGooch 您应该发布一个 minimal 示例 - 字符串、类型和对
JsonSerializer.Deserialize<JsonApiMessage>的调用。我怀疑这个错误与 JSON 无关。您应该发布完整的异常,包括堆栈跟踪,而不仅仅是消息。堆栈跟踪显示异常被抛出的位置以及导致它的调用。您可以使用Exception.ToString()或在调试器的异常弹出窗口中单击Copy Exception Details轻松获得。 -
它是可重现的。演示小提琴:dotnetfiddle.net/xzo5Ht
-
顺便说一句,您为什么首先使用此代码?为什么不将
JsonApiMessage作为操作参数传递?您以这种方式绕过流线型管道和 System.Text.Json 反序列化并失去任何好处。您可以通过使用 StreamReader 并将数据缓存到payloadString来获得 full 分配 -
@dbc 但一开始不应该这样做,因为它否定了 System.Text.Json 提供的任何好处
标签: c# json system.text.json .net-core-3.1