JsonSerializer.Deserialize失败

18
考虑以下代码...
using System;
using System.Text.Json;

public class Program
{
    public static void Main()
    {
        int id = 9;
        string str = "{\"id\": " + id + "}";
        var u = JsonSerializer.Deserialize<User>(str);
        Console.WriteLine($"User ID: {u.Id}, Correct: {id == u.Id}");  // always 0/init/default value
    }
}


public class User {
    public int Id { get; set; }
}

为什么数据无法正确反序列化为“User”对象?我还通过DotNetFiddle验证了行为,以防它是本地系统的问题。没有抛出异常。
我的实际实现是从[ApiController][HttpPost]操作中读取,在我return Created("user", newUser)之后调用。在我的MVC/Razor项目中通过_httpClient.PostAsync调用它。当Created返回到PostAsync调用时,我验证了值是正确的,但无论如何,从响应体解析的值仅包含默认值(实际ID是Guid)。
我最初认为这可能是与UTF8相关的问题,因为这是我发布到ApiControllerStringContent的编码方式。 UTF8反序列化在here中引用,但我在从HttpContent的IO.Stream到ReadOnlySpanUtf8JsonReader的过程中遇到了麻烦。

我在搜索时发现了这个项目,让我觉得它应该可以按照我的期望工作。


1
将示例序列化以查看样本始终是一个好主意。我这样做了,发现它正在寻找 Id 而不是 id,因此找不到它,所以它采用了默认值 0 - Nin
4个回答

50
您的问题在于System.Text.Json默认区分大小写,因此"id": 9(全部小写)无法映射到Id属性。根据文档

不区分大小写的属性匹配

默认情况下,反序列化会在JSON和目标对象属性之间查找区分大小写的属性名称匹配项。要更改此行为,请将JsonSerializerOptions.PropertyNameCaseInsensitive设置为true

注意: Web 默认是不区分大小写的。

var options = new JsonSerializerOptions
{
   PropertyNameCaseInsensitive = true,
};
var weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString, options);
所以你也需要做那个:
var u = JsonSerializer.Deserialize<User>(str, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

示例 fiddle #1 在这里

(如果差异完全是由于驼峰大小写而不是更普遍的大小写差异,您可以像t.j.所示的此答案一样配置序列化器以使用驼峰大小写。)

您可以如此文所示,在 ASP.NET Core 3.0 中在启动时配置该选项:

services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});

或者,您可以将[JsonPropertyName("id")]应用于您的模型:

public class User {
    [JsonPropertyName("id")]
    public int Id { get; set; }
}

这里是演示 fiddle #2 链接


1
正确,加上有关配置的额外有用信息...标记为解决方案。 - t.j.
1
如果您希望JSON中的所有属性都是驼峰式命名,那么除了使用PropertyNameCaseInsensitive之外,另一种选择是将PropertyNamingPolicy显式设置为CamelCase,并将比较保留为默认/区分大小写。 - ahsonkhan
1
使用 JsonNamingPolicy.CamelCase 通常比选择不区分大小写的比较更高效 (在大小写敏感的比较中)。否则,像 asp.net core 一样同时设置两者也可以正常工作。 - ahsonkhan
1
在启动时应该这样写,否则会出现错误:options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; - Ali Karaca
1
我花了三个小时来理解为什么它不起作用,结果发现只是大小写敏感的问题。谢谢啊。 - Андрей Голубцов
显示剩余2条评论

3

感谢mr5通过聊天建议这可能是大小写问题

将字符串更改为使用TitleCase(“Id”)解决了问题。

我正在提交工单的过程中,其中一个可能相关的问题评论引导我到另一个问题,然后到文档,其中有一个解决方案

var options = new JsonSerializerOptions();
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;

使用选项,解决问题...
string str = "{\"id\": " + id + "}";
var options = new JsonSerializerOptions();
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
var u = JsonSerializer.Deserialize<User>(str, options);

留下这一切以帮助其他人。

文档页面也可能会有所帮助。具体来说,https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#case-insensitive-deserialization关于聊天中的讨论(引用如下:“奇怪的是在其他地方没有问题([FromBody]可以很好地处理它)”,这是因为aspnet中默认选项的配置不同:https://github.com/dotnet/aspnetcore/blob/a6bc6ce23dad5a9d02596cf2e91e3c4f965c61bc/src/Mvc/Mvc.Core/src/JsonOptions.cs#L15-L29 - ahsonkhan

1

您还可以阅读此 Microsoft 文档 JSON 序列化,对于配置,您可以使用以下内容:

JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web);
var serialized = JsonSerializer.Serialize(data, _jsonOptions);
var deserialized = JsonSerializer.Deserialize<TEntity>(serialized , _jsonOptions);

JsonSerializerDefaults.Web是一个预定义的枚举设置,例如“驼峰命名值”、“不区分大小写的属性名称”等,请查看JsonSerializerDefaults.?,如果您想自定义设置,可以再次查阅文档here


-1
Startup.csConfigureServices 中。
services.AddControllers()
        .AddJsonOptions(o => {
            o.JsonSerializerOptions.PropertyNamingPolicy=JsonNamingPolicy.CamelCase;
            o.PropertyNameCasInsensitive=true
        });

PropertyNameCasInsensitive 有一个拼写错误,它并不直接在 o 中找到,而是在 JsonSerializerOptions 中找到,因此应该是 o.JsonSerializerOptions.PropertyNameCaseInsensitive。 - Marc Roussel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接