2

I would like to use the new IAsyncEnumerable<T> in my .net core 3.1 web api. This works alright, except I am not happy with the name of the XML root element. It appears to be ArrayOfX and I would like something like Xs. How do I achieve this?

To be more specific. My controller:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    [HttpGet]
    public async IAsyncEnumerable<WeatherForecast> Get()
    {
        await Task.Delay(0);
        var rng = new Random();

        for (var index = 1; index < 5; index++)
        {
            yield return new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            };
        }
    }
}

public class WeatherForecast
{
    public DateTime Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
}

In Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddMvcCore(options =>
        {
            options.OutputFormatters.Clear(); // Remove json for simplicity
            options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
        });
}

And the XML output:

<ArrayOfWeatherForecast xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><WeatherForecast><Date>2020-03-26T08:39:59.2303161+01:00</Date><TemperatureC>-13</TemperatureC><Summary>Warm</Summary></WeatherForecast><WeatherForecast><Date>2020-03-27T08:39:59.2389359+01:00</Date><TemperatureC>22</TemperatureC><Summary>Sweltering</Summary></WeatherForecast><WeatherForecast><Date>2020-03-28T08:39:59.2389696+01:00</Date><TemperatureC>33</TemperatureC><Summary>Scorching</Summary></WeatherForecast><WeatherForecast><Date>2020-03-29T08:39:59.2389719+02:00</Date><TemperatureC>-2</TemperatureC><Summary>Bracing</Summary></WeatherForecast></ArrayOfWeatherForecast>

How do I get WeatherForecasts instead of ArrayOfWeatherForecast?

7
  • Looks like the issue is probably in ControllerBase. With Xml serialization you current have public WeatherForecast[] WeatherForeCast { get;set;} To fix you need to add before the previous line [XmlElement("WeatherVoreCast")]. I believe that is burried in the ControllerBase code. Commented Mar 25, 2020 at 9:12
  • @jdweng My demo project is based on a Microsoft template with a few modifications, all of these listed above. There is no property of type WeatherForecast[], only the Get() method in the controller. And ControllerBase is part of standard mvc (namespace Microsoft.AspNetCore.Mvc). So I am not sure what you mean. Commented Mar 25, 2020 at 9:25
  • That is what I thought. The service is automatically when it see the array creating two xml tags (ArrayOfWeatherForecast,and WeatherForecast). The service is using Xml Serialization to read/write XML. Xml doesn't allow an array as the root element. If you have only one WeatherForeCast the remove the Enumerable from IAsyncEnumerable<WeatherForecast>. Commented Mar 25, 2020 at 10:32
  • @jdweng I would like to use the IAsyncEnumerable (see learn.microsoft.com/en-us/aspnet/core/web-api/… for further details) to return multiple items. But thanks for your suggestion. Commented Mar 25, 2020 at 10:59
  • You can't have your cake and have pie too. If you want IAsyncEnumerable as root element in xml then you are going to live with ArrayOfWeatherForecast. Since your can't have an array as root in xml with serialization. Commented Mar 25, 2020 at 11:28

1 Answer 1

3

You could write your own XmlSerializerOutputFormatter like below:

public class MyCustomXmlSerializerOutputFormatter : XmlSerializerOutputFormatter
{
    protected override void Serialize(XmlSerializer xmlSerializer, XmlWriter xmlWriter, object value)
    {

        xmlSerializer = new XmlSerializer(typeof(List<WeatherForecast>) ,new XmlRootAttribute("WeatherForecasts"));

        xmlSerializer.Serialize(xmlWriter, value);
    }
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddMvcCore(options =>
        {
            options.OutputFormatters.Clear(); // Remove json for simplicity
            options.OutputFormatters.Add(new MyCustomXmlSerializerOutputFormatter());
        });
    }

Result:

enter image description here

Sign up to request clarification or add additional context in comments.

1 Comment

Absolutely perfect. Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.