Skip to content

Migrating from Newtonsoft.Json to System.Text.Json reduces quality of validation errors #38271

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
SigmundVik opened this issue Jun 23, 2020 · 3 comments

Comments

@SigmundVik
Copy link

This issue is related to this dotnet/runtime issue.

The examples below use a vanilla ASP.NET Core application with this controller:

namespace ApplicationName.Controllers
{
    using System;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;

    // This of course belongs to a different file and namespace:
    public class Model
    {
        public Guid Id { get; set; }
        public int Integer { get; set; }
        public string String { get; set; }
    }

    [Produces("application/json")]
    [Consumes("application/json")]
    [Route("test")]
    [ApiController]
    public class Controller : ControllerBase
    {
        [HttpPost]
        public async Task<IActionResult> PostAsync([FromBody] Model model)
        {
            return Ok(model);
        }
    }
}

In each example below, the body for a POST request is listed first. Then comes the response when using Newtonsoft.JSON for doing the model binding, and then the response when using System.Text.Json.

Example 1:

{
    "id": "00000000-0000-0000-0000-00000000000x",
    "integer": 2,
    "string": "2"
}

Response using Newtonsoft.JSON:

{
    "errors": {
        "id": [
            "Error converting value \"00000000-0000-0000-0000-00000000000x\" to type 'System.Guid'. Path 'id', line 2, position 48."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|ac238c1a-4b2c24d7c287f27d."
}

Response using System.Text.Json:

{
    "errors": {
        "$": [
            "The JSON value could not be converted to ApplicationName.Controllers.Model. Path: $ | LineNumber: 1 | BytePositionInLine: 48."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "detail": null,
    "instance": null,
    "traceId": "|29c80116-4896648215ca6161."
}

Example 2:

{
    "id": "00000000-0000-0000-0000-00000000000a",
    "integer": 2x,
    "string": "2"
}

Response using Newtonsoft.JSON:

{
    "errors": {
        "integer": [
            "Input string '2x' is not a valid integer. Path 'integer', line 3, position 17."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|ac238c1b-4b2c24d7c287f27d."
}

Response using System.Text.Json:

{
    "errors": {
        "$": [
            "'x' is an invalid end of a number. Expected a delimiter. Path: $ | LineNumber: 2 | BytePositionInLine: 16."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "detail": null,
    "instance": null,
    "traceId": "|29c80117-4896648215ca6161."
}

Example 3:

{
    "id": "00000000-0000-0000-0000-00000000000a",
    "integer": 2,
    "string": 2 x
}

Response using Newtonsoft.JSON:

{
    "errors": {
        "string": [
            "After parsing a value an unexpected character was encountered: x. Path 'string', line 4, position 16."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|ac238c1d-4b2c24d7c287f27d."
}

Response using System.Text.Json:

{
    "errors": {
        "$": [
            "The JSON value could not be converted to ApplicationName.Controllers.Model. Path: $ | LineNumber: 3 | BytePositionInLine: 15."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "detail": null,
    "instance": null,
    "traceId": "|a88bc7ad-44774eaf80acd428."
}

Example 4:

{
    "id": "00000000-0000-0000-0000-00000000000a",
    "integer": 2,
    "string": 2
}

Response using Newtonsoft.JSON (no error in this case due to lenient parsing):

{
    "id": "00000000-0000-0000-0000-00000000000a",
    "integer": 2,
    "string": "2"
}

Response using System.Text.Json (strict is better, but this could still be improved):

{
    "errors": {
        "$": [
            "The JSON value could not be converted to ApplicationName.Controllers.Model. Path: $ | LineNumber: 3 | BytePositionInLine: 15."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "detail": null,
    "instance": null,
    "traceId": "|29c80118-4896648215ca6161."
}

The responses when using System.Text.Json consistently makes it harder for the end user to figure out what is wrong. It would be great if the validation errors when using System.Text.Json would be of the same quality as when using Newtonsoft.Json.

@pranavkm pranavkm transferred this issue from dotnet/aspnetcore Jun 23, 2020
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-System.Text.Json untriaged New issue has not been triaged by the area owner labels Jun 23, 2020
@pranavkm
Copy link
Contributor

MVC reports deserialization errors as-is. Addressing #38269 should resolve this issue. We could close this as a dup.

@SigmundVik
Copy link
Author

What is causing Path to change from $.Id to $ (and the type from System.Guid to ApplicationName.Controllers.Model) in the first example when comparing #38269 with this issue?

@layomia layomia added this to the Future milestone Jun 30, 2020
@layomia layomia removed the untriaged New issue has not been triaged by the area owner label Jun 30, 2020
@eiriktsarpalis
Copy link
Member

Duplicate of #39055.

@ghost ghost locked as resolved and limited conversation to collaborators Nov 15, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants