Using the below code, I can serialize / deserialize PrinterSettings to a file.
I would like to ask if there's a way to serialize it to a string or byte array or similar instead in order to save it directly into a database.
Thank you!
PrinterSettings prtSettings = new PrinterSettings();
prtSettings.PrintFileName = "does not matter, unused if PrintToFile == false";
//serialise
System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(prtSettings.GetType());
using (System.IO.TextWriter txtWriter = new StreamWriter(#"c:\temp\printerSettings.xml"))
{
xmlSerializer.Serialize(txtWriter,prtSettings);
}
//deserialise
using (FileStream fileStream = new FileStream(#"c:\temp\printerSettings.xml", FileMode.Open))
{
object obj = xmlSerializer.Deserialize(fileStream);
prtSettings = (PrinterSettings)obj;
}
Instead of a StreamWriter use a StringWriter
string printerSettingText = "";
XmlSerializer xser = new XmlSerializer(typeof(PrinterSettings));
using (StringWriter sw = new StringWriter())
{
xser.Serialize(sw, prtSettings);
printerSettingText = sw.ToString();
}
Deserialization of the object is simple like this
string dataToDeserialize = GetYourDataFromDb();
xser = new XmlSerializer(typeof(PrinterSettings));
using (StringReader sr = new StringReader(dataToDeserialize))
{
PrinterSettings prn = (PrinterSettings)xser.Deserialize(sr);
Console.WriteLine(prn.PrintFileName);
}
Microsoft recommend to use XmlWriter instead of XmlTextWriter
https://msdn.microsoft.com/en-us/library/system.xml.xmltextwriter(v=vs.110).aspx
public string Serialize(BackgroundJobInfo info)
{
var stringBuilder = new StringBuilder();
using (var stringWriter = new StringWriter(stringBuilder, CultureInfo.InvariantCulture))
{
var writer = new XmlTextWriter(stringWriter);
new DataContractSerializer(typeof(BackgroundJobInfo)).WriteObject(writer, info);
}
return stringBuilder.ToString();
}
How to correctly use XmlWriter in my method instead of XmlTextWriter?
I'd use the factory method Create on XmlWriter class like:
var stringBuilder = new StringBuilder();
using(var writer = XmlWriter.Create(stringBuilder))
{
new DataContractSerializer(typeof(BackgroundJobInfo)).WriteObject(writer, info)
}
Or you can do it without XmlWriter
public static string Serialize(BackgroundJobInfo info)
{
string result = String.Empty;
using (var ms = new MemoryStream())
{
var sw = new StreamWriter(ms);
DataContractSerializer dcs = new DataContractSerializer(typeof(BackgroundJobInfo));
dcs.WriteObject(ms, info);
sw.Flush();
ms.Position = 0;
var sr = new StreamReader(ms);
result = sr.ReadToEnd();
}
return result;
}
I seem to be getting some junk at the head of my serialized XML string. I have a simple extension method
public static string ToXML(this object This)
{
DataContractSerializer ser = new DataContractSerializer(This.GetType());
var settings = new XmlWriterSettings { Indent = true };
using (MemoryStream ms = new MemoryStream())
using (var w = XmlWriter.Create(ms, settings))
{
ser.WriteObject(w, This);
w.Flush();
return UTF8Encoding.Default.GetString(ms.ToArray());
}
}
and when I apply it to my object I get the string
<?xml version="1.0" encoding="utf-8"?>
<RootModelType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/WeinCad.Data">
<MoineauPump xmlns:d2p1="http://schemas.datacontract.org/2004/07/Weingartner.Numerics">
<d2p1:Rotor>
<d2p1:Equidistance>0.0025</d2p1:Equidistance>
<d2p1:Lobes>2</d2p1:Lobes>
<d2p1:MajorRadius>0.04</d2p1:MajorRadius>
<d2p1:MinorRadius>0.03</d2p1:MinorRadius>
</d2p1:Rotor>
</MoineauPump>
</RootModelType>
Note the junk at the beginning. When I try to deserialize this
I get an error. If I copy paste the XML into my source minus
the junk prefix I can deserialize it. What is the junk text
and how can I remove it or handle it?
Note my deserialization code is
public static RootModelType Load(Stream data)
{
DataContractSerializer ser = new DataContractSerializer(typeof(RootModelType));
return (RootModelType)ser.ReadObject(data);
}
public static RootModelType Load(string data)
{
using(var stream = new MemoryStream(Encoding.UTF8.GetBytes(data))){
return Load(stream);
}
}
This fix seems to work
public static string ToXML(this object obj)
{
var settings = new XmlWriterSettings { Indent = true };
using (MemoryStream memoryStream = new MemoryStream())
using (StreamReader reader = new StreamReader(memoryStream))
using(XmlWriter writer = XmlWriter.Create(memoryStream, settings))
{
DataContractSerializer serializer =
new DataContractSerializer(obj.GetType());
serializer.WriteObject(writer, obj);
writer.Flush();
memoryStream.Position = 0;
return reader.ReadToEnd();
}
}
I had a quick question regarding the datacontractserializer. Maybe it's more of a stream question. I found a piece of code that writes the xml to a filestream. I basically don't want the file and just need the string output.
public static string DataContractSerializeObject<T>(T objectToSerialize)
{
var fs = new FileStream("test.xml", FileMode.OpenOrCreate);
var serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(fs, objectToSerialize);
fs.Close();
return fs.ToString();
}
fs.ToString() is obviously not what I'm looking for. What stream or writer etc, can I use just to return the proper string and not create a file? I did look at the XML the filestream created and it's exactly what I'm looking for. The XmlSerializer wrote the XML a bit strange and I prefer the output of the DataContractSerializer in this case. Can anyone point me in the right direction?
Something like this - put your output into a MemoryStream and then read that back in:
public static string DataContractSerializeObject<T>(T objectToSerialize)
{
using(MemoryStream memStm = new MemoryStream())
{
var serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(memStm, objectToSerialize);
memStm.Seek(0, SeekOrigin.Begin);
using(var streamReader = new StreamReader(memStm))
{
string result = streamReader.ReadToEnd();
return result;
}
}
}
Thanks to #xr280xr for pointing out my forgotten StringWriter disposal in the first draft.
/// <summary>
/// Converts this instance to XML.
/// </summary>
/// <returns>XML representing this instance.</returns>
public string ToXml()
{
var serializer = new DataContractSerializer(this.GetType());
using (var output = new StringWriter())
using (var writer = new XmlTextWriter(output) { Formatting = Formatting.Indented })
{
serializer.WriteObject(writer, this);
return output.GetStringBuilder().ToString();
}
}
And even easier:
var serializer = new DataContractSerializer(typeof(T));
var sb = new StringBuilder();
using (var writer = XmlWriter.Create(sb))
{
serializer.WriteObject(writer, objectToSerialize);
writer.Flush();
return sb.ToString();
}
I suggest combining the methods given by Pat and marc_s:
public static string DataContractSerializeObject<T>(T objectToSerialize)
{
using (var output = new StringWriter())
using (var writer = new XmlTextWriter(output) {Formatting = Formatting.Indented})
{
new DataContractSerializer(typeof (T)).WriteObject(writer, objectToSerialize);
return output.GetStringBuilder().ToString();
}
}
A variant of #root's answer:
var serializer = new DataContractSerializer(typeof(T));
var sb = new StringBuilder();
using (var writer = XmlWriter.Create(sb))
{
serializer.WriteObject(writer, objectToSerialize);
}
return sb.ToString();
When I build XML up from scratch with XmlDocument, the OuterXml property already has everything nicely indented with line breaks. However, if I call LoadXml on some very "compressed" XML (no line breaks or indention) then the output of OuterXml stays that way. So ...
What is the simplest way to get beautified XML output from an instance of XmlDocument?
Based on the other answers, I looked into XmlTextWriter and came up with the following helper method:
static public string Beautify(this XmlDocument doc)
{
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
IndentChars = " ",
NewLineChars = "\r\n",
NewLineHandling = NewLineHandling.Replace
};
using (XmlWriter writer = XmlWriter.Create(sb, settings)) {
doc.Save(writer);
}
return sb.ToString();
}
It's a bit more code than I hoped for, but it works just peachy.
As adapted from Erika Ehrli's blog, this should do it:
XmlDocument doc = new XmlDocument();
doc.LoadXml("<item><name>wrench</name></item>");
// Save the document to a file and auto-indent the output.
using (XmlTextWriter writer = new XmlTextWriter("data.xml", null)) {
writer.Formatting = Formatting.Indented;
doc.Save(writer);
}
Or even easier if you have access to Linq
try
{
RequestPane.Text = System.Xml.Linq.XElement.Parse(RequestPane.Text).ToString();
}
catch (System.Xml.XmlException xex)
{
displayException("Problem with formating text in Request Pane: ", xex);
}
A shorter extension method version
public static string ToIndentedString( this XmlDocument doc )
{
var stringWriter = new StringWriter(new StringBuilder());
var xmlTextWriter = new XmlTextWriter(stringWriter) {Formatting = Formatting.Indented};
doc.Save( xmlTextWriter );
return stringWriter.ToString();
}
If the above Beautify method is being called for an XmlDocument that already contains an XmlProcessingInstruction child node the following exception is thrown:
Cannot write XML declaration.
WriteStartDocument method has already
written it.
This is my modified version of the original one to get rid of the exception:
private static string beautify(
XmlDocument doc)
{
var sb = new StringBuilder();
var settings =
new XmlWriterSettings
{
Indent = true,
IndentChars = #" ",
NewLineChars = Environment.NewLine,
NewLineHandling = NewLineHandling.Replace,
};
using (var writer = XmlWriter.Create(sb, settings))
{
if (doc.ChildNodes[0] is XmlProcessingInstruction)
{
doc.RemoveChild(doc.ChildNodes[0]);
}
doc.Save(writer);
return sb.ToString();
}
}
It works for me now, probably you would need to scan all child nodes for the XmlProcessingInstruction node, not just the first one?
Update April 2015:
Since I had another case where the encoding was wrong, I searched for how to enforce UTF-8 without BOM. I found this blog post and created a function based on it:
private static string beautify(string xml)
{
var doc = new XmlDocument();
doc.LoadXml(xml);
var settings = new XmlWriterSettings
{
Indent = true,
IndentChars = "\t",
NewLineChars = Environment.NewLine,
NewLineHandling = NewLineHandling.Replace,
Encoding = new UTF8Encoding(false)
};
using (var ms = new MemoryStream())
using (var writer = XmlWriter.Create(ms, settings))
{
doc.Save(writer);
var xmlString = Encoding.UTF8.GetString(ms.ToArray());
return xmlString;
}
}
XmlTextWriter xw = new XmlTextWriter(writer);
xw.Formatting = Formatting.Indented;
public static string FormatXml(string xml)
{
try
{
var doc = XDocument.Parse(xml);
return doc.ToString();
}
catch (Exception)
{
return xml;
}
}
A simple way is to use:
writer.WriteRaw(space_char);
Like this sample code, this code is what I used to create a tree view like structure using XMLWriter :
private void generateXML(string filename)
{
using (XmlWriter writer = XmlWriter.Create(filename))
{
writer.WriteStartDocument();
//new line
writer.WriteRaw("\n");
writer.WriteStartElement("treeitems");
//new line
writer.WriteRaw("\n");
foreach (RootItem root in roots)
{
//indent
writer.WriteRaw("\t");
writer.WriteStartElement("treeitem");
writer.WriteAttributeString("name", root.name);
writer.WriteAttributeString("uri", root.uri);
writer.WriteAttributeString("fontsize", root.fontsize);
writer.WriteAttributeString("icon", root.icon);
if (root.children.Count != 0)
{
foreach (ChildItem child in children)
{
//indent
writer.WriteRaw("\t");
writer.WriteStartElement("treeitem");
writer.WriteAttributeString("name", child.name);
writer.WriteAttributeString("uri", child.uri);
writer.WriteAttributeString("fontsize", child.fontsize);
writer.WriteAttributeString("icon", child.icon);
writer.WriteEndElement();
//new line
writer.WriteRaw("\n");
}
}
writer.WriteEndElement();
//new line
writer.WriteRaw("\n");
}
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
This way you can add tab or line breaks in the way you are normally used to, i.e. \t or \n
When implementing the suggestions posted here, I had trouble with the text encoding. It seems the encoding of the XmlWriterSettings is ignored, and always overridden by the encoding of the stream. When using a StringBuilder, this is always the text encoding used internally in C#, namely UTF-16.
So here's a version which supports other encodings as well.
IMPORTANT NOTE: The formatting is completely ignored if your XMLDocument object has its preserveWhitespace property enabled when loading the document. This had me stumped for a while, so make sure not to enable that.
My final code:
public static void SaveFormattedXml(XmlDocument doc, String outputPath, Encoding encoding)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = "\t";
settings.NewLineChars = "\r\n";
settings.NewLineHandling = NewLineHandling.Replace;
using (MemoryStream memstream = new MemoryStream())
using (StreamWriter sr = new StreamWriter(memstream, encoding))
using (XmlWriter writer = XmlWriter.Create(sr, settings))
using (FileStream fileWriter = new FileStream(outputPath, FileMode.Create))
{
if (doc.ChildNodes.Count > 0 && doc.ChildNodes[0] is XmlProcessingInstruction)
doc.RemoveChild(doc.ChildNodes[0]);
// save xml to XmlWriter made on encoding-specified text writer
doc.Save(writer);
// Flush the streams (not sure if this is really needed for pure mem operations)
writer.Flush();
// Write the underlying stream of the XmlWriter to file.
fileWriter.Write(memstream.GetBuffer(), 0, (Int32)memstream.Length);
}
}
This will save the formatted xml to disk, with the given text encoding.
If you have a string of XML, rather than a doc ready for use, you can do it this way:
var xmlString = "<xml>...</xml>"; // Your original XML string that needs indenting.
xmlString = this.PrettifyXml(xmlString);
private string PrettifyXml(string xmlString)
{
var prettyXmlString = new StringBuilder();
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlString);
var xmlSettings = new XmlWriterSettings()
{
Indent = true,
IndentChars = " ",
NewLineChars = "\r\n",
NewLineHandling = NewLineHandling.Replace
};
using (XmlWriter writer = XmlWriter.Create(prettyXmlString, xmlSettings))
{
xmlDoc.Save(writer);
}
return prettyXmlString.ToString();
}
A more simplified approach based on the accepted answer:
static public string Beautify(this XmlDocument doc) {
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true
};
using (XmlWriter writer = XmlWriter.Create(sb, settings)) {
doc.Save(writer);
}
return sb.ToString();
}
Setting the new line is not necessary. Indent characters also has the default two spaces so I preferred not to set it as well.
Set PreserveWhitespace to true before Load.
var document = new XmlDocument();
document.PreserveWhitespace = true;
document.Load(filename);