C# Object to XmlElement - c#

What is the best way to convert the C# object to XmlEmenet?
Do I just use XmlSerializer and import the XmlNode or is there a better way?
This is what I found out there wondering if there is any other better way.
public XmlElement Serialize(XmlDocument document)
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlElement returnVal;
XmlSerializer serializer = new XmlSerializer(this.GetType());
MemoryStream ms = new MemoryStream();
XmlTextWriter tw = new XmlTextWriter(ms, UTF8Encoding.UTF8);
XmlDocument doc = new XmlDocument();
tw.Formatting = Formatting.Indented;
tw.IndentChar = ' ';
serializer.Serialize(tw, this, ns);
ms.Seek(0, SeekOrigin.Begin);
doc.Load(ms);
returnVal = document.ImportNode(doc.DocumentElement, true) as XmlElement;
return returnVal;
}

You can make that into an Extension Method on the Object Type so you don't have to put that method in a bunch of different classes.

Related

Serializing XmlElement too slow

XmlSerializer slows down when serializing XmlElement which include byte array about 9 MB.
How can I fix it without removing XmlElement.
static void SerializeByteArrayToXml(string filePath)
{
var array = File.ReadAllBytes(filePath);
var xmlElement = array.SerializeToXmlElement();
var serializedString = xmlElement.AsXMLString();
}
internal static XmlElement SerializeToXmlElement<T>(this T o)
{
XmlDocument doc = new XmlDocument();
using (XmlWriter writer = doc.CreateNavigator().AppendChild())
{
new XmlSerializer(o.GetType()).Serialize(writer, o);
}
return doc.DocumentElement;
}
internal static string AsXMLString<T>(this T toSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
StringWriter textWriter = new StringWriter();
//This method too slow
xmlSerializer.Serialize(textWriter, toSerialize);
return textWriter.ToString();
}

create xml file, basics

I want to create xml document with following structure
<ServerFp Command="Cashed">
<Cashed Value="199.99"/>
</ServerFp>
So I tried like this :
XmlWriterSettings settings = new XmlWriterSettings() { Indent = true };
using (var writer = XmlWriter.Create(filename, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("ServerFp");
writer.WriteAttributeString("Command", "Cashed");
}
Is this good so far and how to end this file? with node <Cashed Value="199.99"/>
I would try doing it like this:
create a new XmlDocument:
XmlDocument doc = new XmlDocument();
create your nodes you want to insert
XmlNode node1 = doc.CreateElement("node1")
append your element
doc.AppendChild(node1 );
save the document
doc.Save("result.xml");
why not just LINQ to XML?
XElement ServerFp = new XElement("ServerFp",
new XAttribute("Command", "Cached"),
new XElement("CachedValue", "199.99")
);
Console.WriteLine(ServerFp.ToString());
outputting
<ServerFp Command="Cached">
<CachedValue>199.99</CachedValue>
</ServerFp>
Try this LINQ To XML
XElement result = new XElement("ServerFp", new XAttribute("Command", "Cashed"),
new XElement("Cashed", new XAttribute("Value", "199.99"))
);
Output
<ServerFp Command="Cashed">
<Cashed Value="199.99" />
</ServerFp>
this is how you can do it by using XmlWriter
writer.WriteStartDocument();
writer.WriteStartElement("ServerFp");
writer.WriteAttributeString("Command", "Cashed");
writer.WriteStartElement("Cashed");
writer.WriteAttributeString("Value", "199.99");
writer.WriteEndElement();
writer.WriteEndElement();
Or you can do the same using XDocument
XDocument doc = new XDocument(new XElement("ServerFp", new XAttribute("Command", "Cashed"),
new XElement("Cashed", new XAttribute("Value", "199.99"))));
doc.Save(filePath);
Try this (I am not sure but you can get an idea)
XmlWriterSettings settings = new XmlWriterSettings() { Indent = true };
using (var writer = XmlWriter.Create(filename, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("ServerFp");
writer.WriteAttributeString("Command", "Cashed");
writer.WriteEndElement(); // I think this will close your <Cashed Value="199.99"/>
writer.WriteEndElement(); // I think this will close your
}

How to set XmlWriter.XmlWriterSettings when created from XPathNavigator.AppendChild()?

I need to set the OmitXmlDeclaration property of the XmlWriterSettings for a XmlWriter to false to not have XML declarations created. The issue is that I have created the XmlWriter from a call of a XPathNavigator.AppendChild() method. Code below:
public String GetEntityXml<T>(List<T> entities)
{
XmlDocument xmlDoc = new XmlDocument();
XPathNavigator nav = xmlDoc.CreateNavigator();
using (XmlWriter writer = nav.AppendChild())
{
XmlSerializer ser = new XmlSerializer(typeof(List<T>), new XmlRootAttribute(typeof(T).Name + "_LIST"));
ser.Serialize(writer, entities);
}
StringWriter stringWriter = new StringWriter();
XmlTextWriter xmlTextWriter = new XmlTextWriter(stringWriter);
xmlDoc.WriteTo(xmlTextWriter);
string resultString = stringWriter.ToString();
stringWriter.Close();
xmlTextWriter.Close();
return resultString;
}
Any idea how to serialize the List and not have XML declarations?
I’m not getting the XML declaration when I execute your code. Serializing a List<int> gives me:
<Int32_LIST xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<int>5</int>
<int>7</int>
<int>2</int>
</Int32_LIST>
Note that the “XML declaration” that OmitXmlDeclaration refers to is typically something similar to:
<?xml version="1.0" encoding="UTF-8" ?>
If you’re instead referring to the xmlns parts, then those are called “XML namespace declarations”, and may be eliminated by initializing an XmlSerializerNamespaces instance with a default empty namespace, and passing it to your Serialize method:
XmlSerializer ser = new XmlSerializer(typeof(List<T>), new XmlRootAttribute(typeof(T).Name + "_LIST"));
var namespaces = new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") });
ser.Serialize(writer, entities, namespaces);
The below is a shortened implementation which achieves the same result as your code:
public String GetEntityXml<T>(List<T> entities)
{
var sb = new StringBuilder();
var settings = new XmlWriterSettings { OmitXmlDeclaration = true };
using (XmlWriter writer = XmlWriter.Create(sb, settings))
{
XmlSerializer ser = new XmlSerializer(typeof(List<T>), new XmlRootAttribute(typeof(T).Name + "_LIST"));
var namespaces = new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") });
ser.Serialize(writer, entities, namespaces);
}
return sb.ToString();
}
Try this approach (switched to var for readability):
public String GetEntityXml<T>(List<T> entities)
{
var xmlDoc = new XmlDocument();
var nav = xmlDoc.CreateNavigator();
using (var sw = new StringWriter())
{
//Create an XmlWriter that will omit xml declarations
var s = new XmlWriterSettings{ OmitXmlDeclaration = true };
using (var xmlWriter = XmlWriter.Create(sw, s))
{
//Use the following to serialize without namespaces
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
var ser = new XmlSerializer(typeof(List<T>),
new XmlRootAttribute(typeof(T).Name + "_LIST"));
ser.Serialize(xmlWriter, entities, ns);
}
//Pass xml string to nav.AppendChild()
nav.AppendChild(sw.ToString());
}
using (var stringWriter = new StringWriter())
{
using (var xmlTextWriter = XmlWriter.Create(stringWriter))
{
xmlDoc.WriteTo(xmlTextWriter);
}
return stringWriter.ToString();
}
}
Rather than using nav.AppendChild() to create the XmlWriter, you can create the XmlWriter separately and then just use nav.AppendChild(string) to write the XML into xmlDoc. When you create the XmlWriter yourself, you can omit the XML declaration. Also, when you serialize, you'll probably want to omit the xmlns:xsi and xmlns:xsd namespaces using the XmlSerializerNamespaces class.

What is the simplest way to get indented XML with line breaks from XmlDocument?

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);

C# XNode add a NameSpace

I have a xNode made by JSON.
C# code:
Class class = new Class();
class.ComboBoxChecked = Class.ComboBoxChecked;
class.RadioButtonChecked = Class.RadioButtonChecked;
string test = JsonConvert.SerializeObject(class);
XNode node = JsonConvert.DeserializeXNode(test, "Root");
XML:
<Root>
<RadioButtonChecked>1</RadioButtonChecked>
<ComboBoxChecked>5</ComboBoxChecked>
</Root>
My goal is to add a Namespace to it.
How can i achieve this?
You can add namespaces at root level this way:
XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("example", "http://www.w3.org");
using (var ms = new MemoryStream())
{
using (TextWriter writer = new StreamWriter(ms))
{
var xmlSerializer = new XmlSerializer(typeof(MyClass));
xmlSerializer.Serialize(writer, myClassInstance, ns);
XNode node = XElement.Parse(Encoding.ASCII.GetString(ms.ToArray()));
}
}
If you need namespaces in it's children, you can edit your class using the IXmlSerializable interface, here's an Example

Categories

Resources