What is the best way to return XML from a controller's action in ASP.NET MVC? There is a nice way to return JSON, but not for XML. Do I really need to route the XML through a View, or should I do the not-best-practice way of Response.Write-ing it?
11 Answers
-
2Wow, this really helped me, but then I am only beginning to tinker with the MVC thingy. – Denis Valeev May 27 '11 at 15:27
-
If you're working with Linq to XML, creating a string form of the document is wasteful -- it's [better to work with streams](http://stackoverflow.com/a/12718046/24874). – Drew Noakes Oct 04 '12 at 08:35
-
2@Drew Noakes: No it's not. If you write directly to the HttpContext.Response.Output stream, you will get a YSOD on WinXP based servers. It seems to be fixed on Vista+, which is especially problematic if you develop on Windows 7 and deploy to Windows XP (Server 2003?). If you do, you need to write to a memory stream first, and then copy the memory stream to the output stream... – Stefan Steiger Apr 17 '13 at 09:39
-
6@Quandary, ok I will restate the point: creating strings is wasteful when you could avoid allocation/collection/out-of-memory-exceptions by using streams, _unless_ you're working on 11 year old computing systems that exhibit an error. – Drew Noakes Apr 17 '13 at 10:40
-
1You might want to use the `application/xml` mimetype instead. – Fred Sep 02 '14 at 12:00
-
Is it possible to set the encoding to UTF-8 using this? I have tried return this.Content(xmlString, "text/xml", System.Text.Encoding.UTF8) but the XML declaration is always UTF-16 – Andy B Mar 13 '19 at 10:42
Use MVCContrib's XmlResult Action.
For reference here is their code:
public class XmlResult : ActionResult { private object objectToSerialize; /// <summary> /// Initializes a new instance of the <see cref="XmlResult"/> class. /// </summary> /// <param name="objectToSerialize">The object to serialize to XML.</param> public XmlResult(object objectToSerialize) { this.objectToSerialize = objectToSerialize; } /// <summary> /// Gets the object to be serialized to XML. /// </summary> public object ObjectToSerialize { get { return this.objectToSerialize; } } /// <summary> /// Serialises the object that was passed into the constructor to XML and writes the corresponding XML to the result stream. /// </summary> /// <param name="context">The controller context for the current request.</param> public override void ExecuteResult(ControllerContext context) { if (this.objectToSerialize != null) { context.HttpContext.Response.Clear(); var xs = new System.Xml.Serialization.XmlSerializer(this.objectToSerialize.GetType()); context.HttpContext.Response.ContentType = "text/xml"; xs.Serialize(context.HttpContext.Response.Output, this.objectToSerialize); } } }

- 27,717
- 28
- 128
- 190

- 23,504
- 8
- 29
- 28
-
13The class here is taken straight from the MVC Contrib project. Not sure if thats what qualifies as rolling your own. – Sailing Judo Feb 12 '09 at 15:37
-
3Where would you put this class, if you're following the ASP.NET MVC convention? Controllers folder? Same place you'd put your ViewModels, perhaps? – p.campbell Sep 02 '09 at 22:01
-
7@pcampbel, I prefer creating separate folders in my project root for every kind of classes: Results, Filters, Routing, etc. – Anthony Serdyukov Apr 06 '10 at 02:32
-
Using `XmlSerialiser` and member annotations can be hard to maintain. Since Luke posted this answer (about four years ago), Linq to XML has proven itself as a more elegant and powerful replacement for most common scenarios. Check out [my answer](http://stackoverflow.com/a/12718046/24874) for an example of how to do this. – Drew Noakes Oct 04 '12 at 08:35
-
If you're building the XML using the excellent Linq-to-XML framework, then this approach will be helpful.
I create an XDocument
in the action method.
public ActionResult MyXmlAction()
{
// Create your own XDocument according to your requirements
var xml = new XDocument(
new XElement("root",
new XAttribute("version", "2.0"),
new XElement("child", "Hello World!")));
return new XmlActionResult(xml);
}
This reusable, custom ActionResult
serialises the XML for you.
public sealed class XmlActionResult : ActionResult
{
private readonly XDocument _document;
public Formatting Formatting { get; set; }
public string MimeType { get; set; }
public XmlActionResult(XDocument document)
{
if (document == null)
throw new ArgumentNullException("document");
_document = document;
// Default values
MimeType = "text/xml";
Formatting = Formatting.None;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.ContentType = MimeType;
using (var writer = new XmlTextWriter(context.HttpContext.Response.OutputStream, Encoding.UTF8) { Formatting = Formatting })
_document.WriteTo(writer);
}
}
You can specify a MIME type (such as application/rss+xml
) and whether the output should be indented if you need to. Both properties have sensible defaults.
If you need an encoding other than UTF8, then it's simple to add a property for that too.

- 2,005
- 3
- 24
- 50

- 300,895
- 165
- 679
- 742
-
Do you think it's possible to modify this for use in an API controller? – Ray Ackley Nov 01 '12 at 01:34
-
@RayAckley, I don't know as I haven't tried the new Web API stuff out yet. If you find out, let us know. – Drew Noakes Nov 03 '12 at 19:57
-
I think I was on the wrong track with the API controller question (I don't normally do MVC stuff). I just implemented it as a regular controller and it worked great. – Ray Ackley Nov 23 '12 at 19:36
-
Great work Drew. I am using a flavour of your XmlActionResult for my requirement. My dev environment: ASP.NET 4 MVC I call my controller's method (returns XmlActionResult -- containing a transformed xml for MS-Excel) from ajax. The the Ajax Success function has a data parameter that contains the transformed xml. How to use this data parameter to launch a browser window and display either a SaveAs dialog or just open Excel? – sheir Sep 18 '13 at 20:50
-
@sheir, if you want the browser to launch the file then you shouldn't load it via AJAX. Just navigate directly to your action method. The MIME type will determine how it's handled by the browser. Using something like `application/octet-stream` to force it to download. I don't know what MIME type launches Excel, but you should be able to find it online easily enough. – Drew Noakes Sep 19 '13 at 11:01
-
@DrewNoakes, just posted my "solution" in a answer below. Thanks again for the XmlActionResult class. – sheir Sep 26 '13 at 19:20
-
The current code emmits a Byte order mark (BOM) at the start of the file. Use `new UTF8Encoding(false)` to remove it. – ivarne Sep 06 '19 at 07:22
-
@ivarne you could also cache the instance to reduce per-request allocations. – Drew Noakes Sep 08 '19 at 10:49
-
Yes, but if you use Linq-to-xml, removing a single allocation, will probably be insignificant – ivarne Sep 09 '19 at 11:22
If you are only interested to return xml through a request, and you have your xml "chunk", you can just do (as an action in your controller):
public string Xml()
{
Response.ContentType = "text/xml";
return yourXmlChunk;
}

- 287
- 4
- 2
There is a XmlResult (and much more) in MVC Contrib. Take a look at http://www.codeplex.com/MVCContrib

- 11,418
- 12
- 62
- 91

- 903
- 2
- 10
- 31
I've had to do this recently for a Sitecore project which uses a method to create an XmlDocument from a Sitecore Item and its children and returns it from the controller ActionResult as a File. My solution:
public virtual ActionResult ReturnXml()
{
return File(Encoding.UTF8.GetBytes(GenerateXmlFeed().OuterXml), "text/xml");
}

- 11,995
- 10
- 76
- 85

- 59
- 1
- 1
use one of these methods
public ContentResult GetXml()
{
string xmlString = "your xml data";
return Content(xmlString, "text/xml");
}
or
public string GetXml()
{
string xmlString = "your xml data";
Response.ContentType = "text/xml";
return xmlString;
}

- 800
- 7
- 11
Finally manage to get this work and thought I would document how here in the hopes of saving others the pain.
Environment
- VS2012
- SQL Server 2008R2
- .NET 4.5
- ASP.NET MVC4 (Razor)
- Windows 7
Supported Web Browsers
- FireFox 23
- IE 10
- Chrome 29
- Opera 16
- Safari 5.1.7 (last one for Windows?)
My task was on a ui button click, call a method on my Controller (with some params) and then have it return an MS-Excel XML via an xslt transform. The returned MS-Excel XML would then cause the browser to popup the Open/Save dialog. This had to work in all the browsers (listed above).
At first I tried with Ajax and to create a dynamic Anchor with the "download" attribute for the filename, but that only worked for about 3 of the 5 browsers(FF, Chrome, Opera) and not for IE or Safari. And there were issues with trying to programmatically fire the Click event of the anchor to cause the actual "download".
What I ended up doing was using an "invisible" IFRAME and it worked for all 5 browsers!
So here is what I came up with: [please note that I am by no means an html/javascript guru and have only included the relevant code]
HTML (snippet of relevant bits)
<div id="docxOutput">
<iframe id="ifOffice" name="ifOffice" width="0" height="0"
hidden="hidden" seamless='seamless' frameBorder="0" scrolling="no"></iframe></div>
JAVASCRIPT
//url to call in the controller to get MS-Excel xml
var _lnkToControllerExcel = '@Url.Action("ExportToExcel", "Home")';
$("#btExportToExcel").on("click", function (event) {
event.preventDefault();
$("#ProgressDialog").show();//like an ajax loader gif
//grab the basket as xml
var keys = GetMyKeys();//returns delimited list of keys (for selected items from UI)
//potential problem - the querystring might be too long??
//2K in IE8
//4096 characters in ASP.Net
//parameter key names must match signature of Controller method
var qsParams = [
'keys=' + keys,
'locale=' + '@locale'
].join('&');
//The element with id="ifOffice"
var officeFrame = $("#ifOffice")[0];
//construct the url for the iframe
var srcUrl = _lnkToControllerExcel + '?' + qsParams;
try {
if (officeFrame != null) {
//Controller method can take up to 4 seconds to return
officeFrame.setAttribute("src", srcUrl);
}
else {
alert('ExportToExcel - failed to get reference to the office iframe!');
}
} catch (ex) {
var errMsg = "ExportToExcel Button Click Handler Error: ";
HandleException(ex, errMsg);
}
finally {
//Need a small 3 second ( delay for the generated MS-Excel XML to come down from server)
setTimeout(function () {
//after the timeout then hide the loader graphic
$("#ProgressDialog").hide();
}, 3000);
//clean up
officeFrame = null;
srcUrl = null;
qsParams = null;
keys = null;
}
});
C# SERVER-SIDE (code snippet) @Drew created a custom ActionResult called XmlActionResult which I modified for my purpose.
Return XML from a controller's action in as an ActionResult?
My Controller method (returns ActionResult)
- passes the keys parameter to a SQL Server stored proc that generates an XML
- that XML is then transformed via xslt into an MS-Excel xml (XmlDocument)
creates instance of the modified XmlActionResult and returns it
XmlActionResult result = new XmlActionResult(excelXML, "application/vnd.ms-excel"); string version = DateTime.Now.ToString("dd_MMM_yyyy_hhmmsstt"); string fileMask = "LabelExport_{0}.xml";
result.DownloadFilename = string.Format(fileMask, version); return result;
The main modification to the XmlActionResult class that @Drew created.
public override void ExecuteResult(ControllerContext context)
{
string lastModDate = DateTime.Now.ToString("R");
//Content-Disposition: attachment; filename="<file name.xml>"
// must set the Content-Disposition so that the web browser will pop the open/save dialog
string disposition = "attachment; " +
"filename=\"" + this.DownloadFilename + "\"; ";
context.HttpContext.Response.Clear();
context.HttpContext.Response.ClearContent();
context.HttpContext.Response.ClearHeaders();
context.HttpContext.Response.Cookies.Clear();
context.HttpContext.Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);// Stop Caching in IE
context.HttpContext.Response.Cache.SetNoStore();// Stop Caching in Firefox
context.HttpContext.Response.Cache.SetMaxAge(TimeSpan.Zero);
context.HttpContext.Response.CacheControl = "private";
context.HttpContext.Response.Cache.SetLastModified(DateTime.Now.ToUniversalTime());
context.HttpContext.Response.ContentType = this.MimeType;
context.HttpContext.Response.Charset = System.Text.UTF8Encoding.UTF8.WebName;
//context.HttpContext.Response.Headers.Add("name", "value");
context.HttpContext.Response.Headers.Add("Last-Modified", lastModDate);
context.HttpContext.Response.Headers.Add("Pragma", "no-cache"); // HTTP 1.0.
context.HttpContext.Response.Headers.Add("Expires", "0"); // Proxies.
context.HttpContext.Response.AppendHeader("Content-Disposition", disposition);
using (var writer = new XmlTextWriter(context.HttpContext.Response.OutputStream, this.Encoding)
{ Formatting = this.Formatting })
this.Document.WriteTo(writer);
}
That was basically it. Hope it helps others.
A simple option that will let you use streams and all that is return File(stream, "text/xml");
.

- 3,307
- 1
- 26
- 41
Here is a simple way of doing it:
var xml = new XDocument(
new XElement("root",
new XAttribute("version", "2.0"),
new XElement("child", "Hello World!")));
MemoryStream ms = new MemoryStream();
xml.Save(ms);
return File(new MemoryStream(ms.ToArray()), "text/xml", "HelloWorld.xml");

- 11
-
Why does this build two memory streams? Why not just pass `ms` directly, instead of copying it to a new one? Both objects will have the same lifetime. – jpaugh Oct 04 '18 at 19:58
-
Do a `ms.Position=0` and you can return the original memorystream. Then you can `return new FileStreamResult(ms,"text/xml");` – Carter Medlin Jan 28 '19 at 20:07
A small variation of the answer from Drew Noakes that use the method Save() of XDocument.
public sealed class XmlActionResult : ActionResult
{
private readonly XDocument _document;
public string MimeType { get; set; }
public XmlActionResult(XDocument document)
{
if (document == null)
throw new ArgumentNullException("document");
_document = document;
// Default values
MimeType = "text/xml";
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.ContentType = MimeType;
_document.Save(context.HttpContext.Response.OutputStream)
}
}

- 1
- 1

- 665
- 1
- 5
- 7