create xml file, basics - c#

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
}

Related

XmlWriter stops writing to StringBuilder

I have a strange problem. When looping through an array and creating XML with XMLWriter and a StringBuilder the strings just stop getting added to the string builder. I don't get any errors until the XmlDoc.LoadXml method is called when I get this error:
Unexpected end of file has occurred. The following elements are not closed: ID, Port, Ports. Line 1256, position 39.
When I stepped though the code the loops continue and throw no errors but nothing is added to the string builder.
Any Ideas? Thanks
public XmlElement LclExportGetPorts()
{
DAL.DALSoapClient soapy = new DAL.DALSoapClient();
DAL.DALStringString[] ports = soapy.EnumPortsWeb(false);
XmlDocument XmlDoc = new XmlDocument();
StringBuilder SB = new StringBuilder();
XmlWriterSettings XmlSettings = new XmlWriterSettings();
XmlSettings.Indent = true;
using (XmlWriter writer = XmlWriter.Create(SB, XmlSettings))
{
writer.WriteStartDocument();
writer.WriteStartElement("Ports");
foreach (var p in ports)
{
writer.WriteStartElement("Port");
writer.WriteElementString("ID", p.Key);
writer.WriteElementString("Name", p.Value);
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndDocument();
string temp = writer.ToString();
XmlDoc.LoadXml(SB.ToString());
}
XmlElement Result = XmlDoc.DocumentElement;
return Result;
}
I think working with Linq2Xml is much more easier. (Also no need to form an xml string first and then parsing it as Wug suggested)
public XElement LclExportGetPorts()
{
......
XElement xDoc = new XElement("Ports");
foreach (var p in ports)
{
xDoc.Add(new XElement("Port", new XElement("ID", p.ID),
new XElement("Name", p.Name)));
}
return xDoc;
}
You need to Flush() the writer.

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.

C# Object to XmlElement

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.

Writing a function which returns a bit of xml?

This is probably a very newbie question but what I'm trying to do is write a function which returns something like XMLWriter and then adds its contents to another xmlwriter.
For example:
XmlWriter ToXML()
{
XmlWriterSettings oSettings = new XmlWriterSettings();
oSettings.Indent = true;
oSettings.OmitXmlDeclaration = false;
oSettings.Encoding = Encoding.Unicode;
Stream output = Stream.Null;
XmlWriter writer = XmlWriter.Create(output, oSettings);
{
writer.WriteStartDocument(true);
writer.WriteComment("This BaseSprite was created by the in-phone level editor");
writer.WriteStartElement("testelement");
writer.WriteStartAttribute("Name");
writer.WriteValue("John Howard");
writer.WriteEndAttribute();
writer.WriteEndElement();
}
return writer;
}
void SomeOtherFunction()
{
XMLWriter xmlthing;
// add xml things to it
xmlthing += ToXML(); // now the contents of ToXML has been added in to xmlthing
}
Is this possible?
*Question updated:
XmlWriter writer;
XDocument doc = new XDocument();
{
writer = doc.CreateWriter();
writer.WriteStartDocument(true);
writer.WriteComment("This BaseSprite was created by the in-phone level editor");
writer.WriteStartElement("testelement");
writer.WriteStartAttribute("Name");
writer.WriteValue("John Howard");
writer.WriteEndAttribute();
writer.WriteEndElement();
writer.Close();
}
XDocument doc2 = new XDocument();
{
writer = doc2.CreateWriter();
writer.WriteStartDocument(true);
writer.WriteStartElement("testnestedelement");
writer.WriteStartAttribute("DUUUUUDE");
writer.WriteValue("WHERES MY CAR!?");
writer.WriteEndAttribute();
writer.WriteEndElement();
writer.Close();
}
doc.Element("testelement").Add(doc2); // how can I make it so that doc2 is added as a nested element in 'testlement' from doc?
I would prefer to use XmlDocument if you need to compose the Xml among many function in your app.
http://msdn.microsoft.com/en-us/library/system.xml.xmldocument.aspx
or XDocument in Silverlight:
http://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument%28v=VS.95%29.aspx
then you create a single XDocument or XmlDocument and you pass it across all the functions needed to manipulate it.

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

Categories

Resources