Possible to write XML to memory with XmlWriter? - c#

I am creating an ASHX that returns XML however it expects a path when I do
XmlWriter writer = XmlWriter.Create(returnXML, settings)
But returnXML is just an empty string right now (guess that won't work), however I need to write the XML to something that I can then send as the response text. I tried XmlDocument but it gave me an error expecting a string. What am I missing here?

If you really want to write into memory, pass in a StringWriter or a StringBuilder like this:
using System;
using System.Text;
using System.Xml;
public class Test
{
static void Main()
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
StringBuilder builder = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("root");
writer.WriteStartElement("element");
writer.WriteString("content");
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
}
Console.WriteLine(builder);
}
}
If you want to write it directly to the response, however, you could pass in HttpResponse.Output which is a TextWriter instead:
using (XmlWriter writer = XmlWriter.Create(Response.Output, settings))
{
// Write into it here
}

Something was missing on my side: flushing the XmlWriter's buffer:
static void Main()
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
StringBuilder builder = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("root");
writer.WriteStartElement("element");
writer.WriteString("content");
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
}
Console.WriteLine(builder);
}

StringBuilder xml = new StringBuilder();
TextWriter textWriter = new StringWriter(xml);
XmlWriter xmlWriter = new XmlTextWriter(textWriter);
Then, use the xmlWriter to do all the xml writing, and that writes it directly to the StringBuilder.
Edit: Thanks to Jon Skeet's comment:
StringBuilder xml = new StringBuilder();
XmlWriter xmlWriter = XmlWriter.Create(xml);

The best way to do that is to write directly to the Response Output Stream. Its a stream that's built-in to ASP.NET to allow you to write whatever output as a stream, in this case you can write XML to it.

Related

XmlWriter outputs unexpected encoding when printing to console

While learning the C# XmlWriter class I came across some strange behavior. When outputting to a file it uses UTF-8 as expected but when outputting to the Console it uses my system code-page (862) instead of UTF-8. I know consoles don't support UTF-8, so I'm wondering if XmlWriter defaults to system code-page if the stream is a console?
Code to reproduce behavior:
using System;
using System.Xml;
namespace xmlwriterTest
{
class Program
{
static void Main(string[] args)
{
var settings = new XmlWriterSettings
{
Indent = true
};
using (XmlWriter writer = XmlWriter.Create(Console.Out, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("users");
writer.WriteStartElement("user");
writer.WriteAttributeString("age", "42");
writer.WriteString("John Doe");
writer.WriteEndElement();
writer.WriteStartElement("user");
writer.WriteAttributeString("age", "39");
writer.WriteString("Jane Doe");
writer.WriteEndDocument();
writer.Close();
Console.WriteLine("\n\n");
}
}
}
}
Expected output:
<?xml version="1.0" encoding="utf-8"?>
Actual output:
<?xml version="1.0" encoding="Codepage - 862"?>
edit
My question is not how to achieve the expected output in the console. It is why this happens in the first place
Update as per comment
Why this happens in the first place : because the default Encoding for Console.Out is not UTF-8, I'm doing a test like this:
1.1 - Default Encoding :
TextWriter textWriter = Console.Out;
Console.WriteLine(textWriter.Encoding.BodyName);
1.2 - Result :
ibm850
2.1 - Custom Encoding of Console.Out :
Console.OutputEncoding = Encoding.UTF8;
TextWriter textWriter = Console.Out;
Console.WriteLine(textWriter.Encoding.BodyName);
2.2 - Result :
utf-8
The whole code :
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true
};
Console.OutputEncoding = Encoding.UTF8;
using (XmlWriter writer = XmlWriter.Create(Console.Out, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("users");
writer.WriteStartElement("user");
writer.WriteAttributeString("age", "42");
writer.WriteString("John Doe");
writer.WriteEndElement();
writer.WriteStartElement("user");
writer.WriteAttributeString("age", "39");
writer.WriteString("Jane Doe");
writer.WriteEndDocument();
writer.Close();
Console.WriteLine();
}
Result:
<?xml version="1.0" encoding="utf-8"?>
<users>
<user age="42">John Doe</user>
<user age="39">Jane Doe</user>
</users>
Old code
You can create a custom StringWriter by using StringBuilder and override Encoding property like :
1 - Custom class:
public class EncodedStringWriter : StringWriter
{
public EncodedStringWriter(StringBuilder sb, Encoding encoding)
: base(sb)
{
_Encoding = encoding;
}
private readonly Encoding _Encoding;
public override Encoding Encoding => _Encoding;
}
2 - Construct the XML, by using the custom stringWriter :
StringBuilder sb = new StringBuilder();
EncodedStringWriter stringWriter = new EncodedStringWriter(sb, Encoding.UTF8);
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true
};
using (XmlWriter writer = XmlWriter.Create(stringWriter, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("users");
writer.WriteStartElement("user");
writer.WriteAttributeString("age", "42");
writer.WriteString("John Doe");
writer.WriteEndElement();
writer.WriteStartElement("user");
writer.WriteAttributeString("age", "39");
writer.WriteString("Jane Doe");
writer.WriteEndDocument();
writer.Close();
Console.WriteLine(sb.ToString());
}
Result
<?xml version="1.0" encoding="utf-8"?>
<users>
<user age="42">John Doe</user>
<user age="39">Jane Doe</user>
</users>
I hope you find this helpful.

Why xmlreader can't read something that xmlwriter wrote?

I have the simplest code in the world,
using (XmlWriter writer = XmlWriter.Create(stringWriter))
{
writer.WriteStartDocument();
writer.WriteStartElement("Board");
writer.WriteAttributeString("Rows", mRows.ToString());
writer.WriteAttributeString("Columns", mColumns.ToString());
writer.WriteEndElement();
writer.WriteEndDocument();
}
TextWriter writer1 = new StreamWriter(path);
writer1.Write(stringWriter.toString());
writer1.Close();
Then i write it to a txt file that looks like this:
<?xml version="1.0" encoding="utf-16"?>
<Board Rows="30" Columns="50">
</Board>
Then i do the following :
FileStream str = new FileStream(s.FileName, FileMode.Open);
using(XmlReader reader = XmlReader.Create(stream))
{
reader.Read();
}
And it throws an exception :
"There is no Unicode byte order mark. Cannot switch to Unicode."
I googled the exception and found several workarounds, but i don't understand why i need a work around, i just want to read the xml i wrote.
Can some one please explain what exactly the problem is ?
Should i write something differently in the xml ?
What is the simplest solution to this ?
You're probably not writing to a unicode file which File.WriteAllText or a vanilla FileStream does not do.
Instead use File.OpenWrite or FileStream combined with the StreamWriter(Stream steam, Encoding encoding) constructor to specify unicode.
Sample:
var path = #"C:\Dev\sample.xml";
string xml;
var mRows = 30;
var mColumns = 50;
var options = new XmlWriterSettings { Indent = true };
using (var stringWriter = new StringWriter())
{
using (var writer = XmlWriter.Create(stringWriter, options))
{
writer.WriteStartDocument();
writer.WriteStartElement("Board");
writer.WriteAttributeString("Rows", mRows.ToString());
writer.WriteAttributeString("Columns", mColumns.ToString());
writer.WriteEndElement();
writer.WriteEndDocument();
}
xml = stringWriter.ToString();
}
if(File.Exists(path))
File.Delete(path);
using(var stream = File.OpenWrite(path))
using(var writer = new StreamWriter(stream, Encoding.Unicode))
{
writer.Write(xml);
}
Console.Write(xml);
using(var stream = File.OpenRead(path))
using(var reader = XmlReader.Create(stream))
{
reader.Read();
}
File.Delete(path);

Force XDocument to write to String with UTF-8 encoding

I want to be able to write XML to a String with the declaration and with UTF-8 encoding. This seems mighty tricky to accomplish.
I have read around a bit and tried some of the popular answers for this but the they all have issues. My current code correctly outputs as UTF-8 but does not maintain the original formatting of the XDocument (i.e. indents / whitespace)!
Can anyone offer some advice please?
XDocument xml = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), xelementXML);
MemoryStream ms = new MemoryStream();
using (XmlWriter xw = new XmlTextWriter(ms, Encoding.UTF8))
{
xml.Save(xw);
xw.Flush();
StreamReader sr = new StreamReader(ms);
ms.Seek(0, SeekOrigin.Begin);
String xmlString = sr.ReadToEnd();
}
The XML requires the formatting to be identical to the way .ToString() would format it i.e.
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<root>
<node>blah</node>
</root>
What I'm currently seeing is
<?xml version="1.0" encoding="utf-8" standalone="yes"?><root><node>blah</node></root>
Update
I have managed to get this to work by adding XmlTextWriter settings... It seems VERY clunky though!
MemoryStream ms = new MemoryStream();
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
settings.ConformanceLevel = ConformanceLevel.Document;
settings.Indent = true;
using (XmlWriter xw = XmlTextWriter.Create(ms, settings))
{
xml.Save(xw);
xw.Flush();
StreamReader sr = new StreamReader(ms);
ms.Seek(0, SeekOrigin.Begin);
String blah = sr.ReadToEnd();
}
Try this:
using System;
using System.IO;
using System.Text;
using System.Xml.Linq;
class Test
{
static void Main()
{
XDocument doc = XDocument.Load("test.xml",
LoadOptions.PreserveWhitespace);
doc.Declaration = new XDeclaration("1.0", "utf-8", null);
StringWriter writer = new Utf8StringWriter();
doc.Save(writer, SaveOptions.None);
Console.WriteLine(writer);
}
private class Utf8StringWriter : StringWriter
{
public override Encoding Encoding { get { return Encoding.UTF8; } }
}
}
Of course, you haven't shown us how you're building the document, which makes it hard to test... I've just tried with a hand-constructed XDocument and that contains the relevant whitespace too.
Try XmlWriterSettings:
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = false;
xws.Indent = true;
And pass it on like
using (XmlWriter xw = XmlWriter.Create(sb, xws))
See also https://stackoverflow.com/a/3288376/1430535
return xdoc.Declaration.ToString() + Environment.NewLine + xdoc.ToString();

Writing XMLDocument to file with specific newline character (c#)

I have an XMLDocument that I have read in from file. The file is Unicode, and has the newline character '\n'. When I write the XMLDocument back out, it has the newline characters '\r\n'.
Here is the code, pretty simple:
XmlTextWriter writer = new XmlTextWriter(indexFile + ".tmp", System.Text.UnicodeEncoding.Unicode);
writer.Formatting = Formatting.Indented;
doc.WriteTo(writer);
writer.Close();
XmlWriterSettings has a property, NewLineChars, but I am unable to specify the settings parameter on 'writer', it is read-only.
I can create a XmlWriter with a specified XmlWriterSettings property, but XmlWriter does not have a formatting property, resulting in a file with no linebreaks at all.
So, in short, I need to write a Unicode Xml file with newline character '\n' and Formatting.Indented. Thoughts?
I think you're close. You need to create the writer from the settings object:
(Lifted from the XmlWriterSettings MSDN page)
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
settings.NewLineOnAttributes = true;
writer = XmlWriter.Create(Console.Out, settings);
writer.WriteStartElement("order");
writer.WriteAttributeString("orderID", "367A54");
writer.WriteAttributeString("date", "2001-05-03");
writer.WriteElementString("price", "19.95");
writer.WriteEndElement();
writer.Flush();
Use XmlWriter.Create() to create the writer and specify the format. This worked well:
using System;
using System.Xml;
class Program {
static void Main(string[] args) {
XmlWriterSettings settings = new XmlWriterSettings();
settings.NewLineChars = "\n";
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(#"c:\temp\test.xml", settings);
XmlDocument doc = new XmlDocument();
doc.InnerXml = "<root><element>value</element></root>";
doc.WriteTo(writer);
writer.Close();
}
}

How to append new elements to Xml from a stream

I have a method which returns some xml in a memory stream
private MemoryStream GetXml()
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (MemoryStream memoryStream = new MemoryStream())
{
using (XmlWriter writer = XmlWriter.Create(memoryStream, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("root");
writer.WriteStartElement("element");
writer.WriteString("content");
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
}
return memoryStream;
}
}
In this example the format of the xml will be:
<?xml version="1.0" encoding="utf-8"?>
<root>
<element>content</element>
</root>
How can I insert a new element under the root e.g.:
<?xml version="1.0" encoding="utf-8"?>
<root>
<element>content</element>
----->New element here <------
</root>
EDIT:
Also please suggest the most efficient method as returning a MemoryStream may not be the best solution.
The final xml will be passed to a custom HttpHandler so what are the best options for writing the output?
context.Response.Write vs context.Response.OutputStream
Your first issue is that you don't have XML - you have a stream of bytes which can be loaded into an XDocument or XmlDocument. Do you really need to output to a stream instead of directly into an XML Document?
BTW, I think you need to lose the using block around the MemoryStream. You won't be able to use the stream for much after it's been disposed.
public void AppendElement()
{
XDocument doc;
using(MemoryStream stream = GetXml())
{
using(var sr = new StreamReader(stream))
{
doc = XDocument.Load(sr);
}
}
if(doc.Root != null)
{
doc.Root.Add(new XElement("Whatever"));
}
}
private static MemoryStream GetXml()
{
var settings = new XmlWriterSettings {Indent = true};
var memoryStream = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(memoryStream, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("root");
writer.WriteStartElement("element");
writer.WriteString("content");
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
}
return memoryStream;
}
For efficiency, try to work directly with XDocument. You seem to want to use XmlWriter, so here's how to do it (not tested):
public void AppendElement()
{
XDocument doc = GetXml();
if(doc.Root != null)
{
doc.Root.Add(new XElement("Whatever"));
}
}
private static XDocument GetXml()
{
var doc = new XDocument();
using (XmlWriter writer = doc.CreateWriter())
{
writer.WriteStartDocument();
writer.WriteStartElement("root");
writer.WriteStartElement("element");
writer.WriteString("content");
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
}
return doc;
}
Update:
You can write the XML with
doc.Save(HttpContext.Response.Output);
Again, I can't test it now, but try this:
private static XDocument GetXml()
{
return new XDocument(
new XElement(
"root",
new XElement(
"element",
"content")));
}
Consider something like this
XElement element = XElement.Parse(#" <root>
<element>content</element>
</root>");
element.Element("element").AddAfterSelf(new XElement("foo", "blah"));

Categories