Skip to content

[API Proposal]: Add support for custom JSON deserialization error messages to System.Text.Json #76149

@magyarandras

Description

@magyarandras

Background and motivation

Currently, when the JsonSerializer can't convert a value to a specific type or encounters invalid/malformed JSON it throws a JsonException with a default message. For example, The JSON value could not be converted to System.DateTime. Path: $.Date | LineNumber: 1 | BytePositionInLine: 36.
The default message is useful, but there should be a feature that allows specifying custom error messages.

An attribute could be used for the purpose.

(There might be workarounds in some cases, for example creating custom JsonConverters and extending the JsonConverterAttribute to pass a message parameter works for properties, but it would be more convenient if the library could support this feature out of the box.)

This feature can be useful in many cases, for example, REST API clients can get more user-friendly or even localized JSON parsing error messages from the server.

For more details see my implementation: magyarandras@a0538d2

API Proposal

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Text.Json.Serialization
{

    /// <summary>
    /// Specifies a custom JSON serialization error message
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
    public sealed class JsonSerializationErrorAttribute : JsonAttribute
    {
        /// <summary>
        /// The serialization error message.
        /// </summary>
        public string Message { get; }

        /// <summary>
        /// Initializes a new instance of <see cref="JsonSerializationErrorAttribute"/>.
        /// </summary>
        public JsonSerializationErrorAttribute(string message)
        {
            Message = message;
        }
    }

}
public abstract partial class JsonPropertyInfo
{
    public string ErrorMessage { get { throw null; } set { } }
}

API Usage

[JsonSerializationError("Invalid JSON! Path: {0} Line: {1} Byte position: {2}")]
public class Event
{
    [JsonSerializationError("Invalid event date! Path: {0} Line: {1} Byte position: {2}")]
    public DateTime EventDate { get; set; }
}
string jsonString = @"{
    ""EventDate"": ""Not a date""
}";
        
try
{
   JsonSerializer.Deserialize<Event>(jsonString);
}
catch(JsonException ex)
{
   //Should be "Invalid event date! Path: $.EventDate Line: 1 Byte position: 41"
   Console.WriteLine(ex.Message);
}
//Missing curly bracket
string invalidJsonString = @"{
    ""EventDate"": ""2022-10-05""
";

try
{
    JsonSerializer.Deserialize<Event>(invalidJsonString);
}
catch(JsonException ex)
{
   //Should be "Invalid JSON! Path: $ Line: 2 Byte position: 8"
   Console.WriteLine(ex.Message);
}

Alternative Designs

No response

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions