Search code examples
.netxmlasp.net-core-webapimodel-binding

XML request body is not deserialized in model binding for web API


I've read a bit about XML serialization in ASP.NET Core Web API. The default is JSON but XML might also be used sometimes. So I wanted to extend my API to support both. It was easy to make it output responses in the XML format. but it won't ever accept XML requests. I can throw in any request body as JSON but as soon as I change the content-type and body to XML, nothing is read anymore. There is no error. The controller action just receives an empty object, as if the request body was empty.

What's the problem here? Has .NET 7 silently dropped support for XML without removing the API for it? Do I need some secret sauce that isn't shown in any of the Microsoft documentation and other web sources? Can I debug into it somehow?

Here are the parts of my code that I consider relevant:

Startup.cs:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers()
            // Also tried something within the callback, setting input formats explicitly
            .AddXmlSerializerFormatters();
            // Also tried AddXmlDataContractSerializerFormatters() and both
    }
}

Controller:

[ApiController]
[Route("sample")]
public class SampleController : ControllerBase
{
    [HttpPost("time")]
    [Consumes("application/json", "application/xml")]
    // Also tried leaving only xml here, removing json completely
    // Also tried text/xml
    [Produces("application/json", "application/xml")]
    public IActionResult PostTime([FromBody] TimeConfig config)
    {
        // Should see something here in config, but it's just defaults
        // (MinTime, false, null)
    }
}

Model:

// Also tried with [Serializable]
// Also tried with some XML attribute I can't remember
public class TimeConfig
{
    public DateTimeOffset CurrentTime { get; set; }
    public bool IsNtpEnabled { get; set; }
    public string? NtpServer { get; set; }
}

Sent request: (see, certainly not false and null!)

<TimeConfig>
    <currentTime>2023-01-20T22:41:26.558Z</currentTime>
    <isNtpEnabled>true</isNtpEnabled>
    <ntpServer>string</ntpServer>
</TimeConfig>

(Also tried with <?xml version="1.0" encoding="UTF-8"?> in front)

Request headers:

Accept: */*
Content-Type: application/xml

Solution

  • The problem was from the parsed XML in which the XML elements are case-sensitive.

    <TimeConfig>
        <currentTime>2023-01-20T22:41:26.558Z</currentTime>
        <isNtpEnabled>true</isNtpEnabled>
        <ntpServer>string</ntpServer>
    </TimeConfig>
    

    Solutions

    1. Either reformat the XML elements to Pascal case to match with the properties name in TimeConfig class or

    2. Apply the XmlElement attribute to the properties below:

    public class TimeConfig
    {
        [XmlElement("currentTime")]
        public DateTimeOffset CurrentTime { get; set; }
        [XmlElement("isNtpEnabled")]
        public bool IsNtpEnabled { get; set; }
        [XmlElement("ntpServer")]
        public string? NtpServer { get; set; }
    }
    

    Demo

    enter image description here