If you want to support multiple de-serializers for the same controller endpoint, then you should not try to accept a generic content like JObject or xElement as the argument for your controller. The other Faux Pas is that your method can only have a single argument decorated with the FromBodyAttribute.
Configuring services.AddControllers().AddXmlSerializerFormatters() is all you need to support automatic deserialization from XML and Serialization to XML via a mechanims called content negotiation. For this to work as an input type, you must first use a typed argument in your method, then the FromBodyAttribute will use the configured SerializerFormatters to deserialize the body to the requested type based on the Content-Type of the request.
One caveat that I have found is that you should use the application/xml as the Content-Type for posting and XML document.
We use the Consumes attribute to declare the supported input content types, the Produces attribute describes the response types that are supported.
[HttpPost]
[Route("Patient/$gpc.registerstudent")]
[SwaggerOperation(Summary = "Register student")]
[Consumes("application/xml", "application/json")]
[Produces("application/xml", "application/json")]
public async Task<IActionResult> Registerstudent([FromBody] RegisterStudentRequestDTO request)
{
await Task.CompletedTask;
return Ok(request); // just echo out for now to show the output serialization
}
In this case my register DTO is very simple:
public class RegisterStudentRequestDTO
{
public string Name { get; set; }
public DateTime Date { get; set; }
public string Comments { get; set;}
}
The way that a caller can define the expected response type if by providing an Accept header. For instance, we can now provide a XML input to this method and tell the controller to serialize the response into XML:
In the request, Content-Type header describes the input that we are providing, the Accept header describes the content-type of the response.
curl -X 'POST' \
'https://localhost:7208/WeatherForecast/Patient/$gpc.registerstudent' \
-H 'accept: application/xml' \
-H 'Content-Type: application/xml' \
-d '<?xml version="1.0" encoding="UTF-8"?>
<RegisterStudentRequestDTO>
<Name>string</Name>
<Date>2023-07-04T12:34:43.128Z</Date>
<Comments>string</Comments>
</RegisterStudentRequestDTO>'
You will notice that the name of the elements MUST match the casing of the name of the class and properties, by default the XML serializer is case-sensitive.
This will return:
<RegisterStudentRequestDTO xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>string</Name>
<Date>2023-07-04T12:34:43.128Z</Date>
<Comments>string</Comments>
</RegisterStudentRequestDTO>
But if you change the Accept header to application/json then without altering any code, the output changes to this:
{
"name": "string",
"date": "2023-07-04T12:29:14.595Z",
"comments": "string"
}
To complete the demo, to provide JSON input, the request would look like this:
curl -X 'POST' \
'https://localhost:7208/WeatherForecast/Patient/$gpc.registerstudent' \
-H 'accept: application/xml' \
-H 'Content-Type: application/json' \
-d '{ "name":"string", "date":"2023-07-04T12:29:14.595Z" , "comments": "string" }'
The response will still be in application/xml due to the Accept header.
Further Reading:
You can also read about a workaround here: Issue receiveing POST request with Content-Type text/xml
If you receive a response similar to this:
<problem xmlns="urn:ietf:rfc:7807">
<status>400</status>
<title>One or more validation errors occurred.</title>
<type>https://tools.ietf.org/html/rfc7231#section-6.5.1</type>
<traceId>00-94549daf9ab55604a516f0b2b012d563-23030c613b93e726-00</traceId>
<MVC-Errors>
<MVC-Empty>An error occurred while deserializing input data.</MVC-Empty>
<request>The request field is required.</request>
</MVC-Errors>
</problem>
Then please check the casing of your class or property names, the swagger document will use (lower) camel case by default in the Try it Now section, this is unfortunate:

Providing the correctly typed input will work, you can also implement DataContractAttribute and DataMemberAttribute to explicitly rename the properties to their lower case variant. If you do this, you will need to also add the .AddXmlDataContractSerializerFormatters() to your fluent AddControllers() implementation in startup.cs/program.cs
AddXmlSerializerFormatters[FromBody] JObject parameterswhat do you expect to happen here? You would have to pass a JObject inside XML? And you can't have twoFromBodyparameters anyway. Perhaps you should just have a single[FromBody] dynamic parametersalthough to be honest it's not clear why you don't have explicit paramaters rather than some big dynamic blob.Acceptheader.