I'm building an Parts app in order to learn C# and WPF. I trying having trouble adding new parts using XmlWriter. I can create the xml file, but can not figure how to add additional parts. Should I be using something else like XmlDocument? Here is my code behind:
private void btnSave_Click(object sender, RoutedEventArgs e)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
settings.Indent = true;
using (XmlWriter writer = XmlWriter.Create("f:\\MyParts.xml", settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("MyParts");
writer.WriteStartElement("parts");
writer.WriteStartElement("item");
writer.WriteString(txtbxitem.Text);
writer.WriteEndElement();
writer.WriteStartElement("color");
writer.WriteString(txtbxcolor.Text);
writer.WriteEndElement();
writer.WriteStartElement("size");
writer.WriteString(txtbxsize.Text);
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
writer.Close();
}
}
This code creates the xml file and a node correctly, but how do I add additional parts? Here is what I am trying to create:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<MyParts>
<parts>
<item>Part1</item>
<color>Red</color>
<size>SM</size>
</parts>
<parts>
<item>Part2</item>
<color>Blue</color>
<size>XXL</size>
</parts>
</MyParts>
Personally I'd suggest using LINQ to XML. It's a much easier API to use than XmlDocument.
But yes, if you want to modify an existing document then typically using an in-memory representation is simpler than using a streaming API. It's possible to do the latter of course, but it's not easy.
Here's an example to create the same XML as you've already got (other than the declaration: any reason you'd want to use Latin-1 instead of something like UTF-8 which can represent the whole of Unicode, btw?)
var doc = new XDocument(
new XElement("MyParts",
new XElement("parts",
new XElement("item", "Part1"),
new XElement("color", "Red"),
new XElement("size", "SM")),
new XElement("parts",
new XElement("item", "Part2"),
new XElement("color", "Blue"),
new XElement("size", "XXL"))));
Then if you wanted to add another part:
doc.Root.Add(
new XElement("parts",
new XElement("item", "Part3"),
new XElement("color", "Green"),
new XElement("size", "L")));
Admittedly I'd expect you'd want to encapsulate the "create a parts element" bit into a method to avoid repeating it all the time... but hopefully you get the picture.
Use a loop, and you'll end up with something like:
var parts = new List<Part>() { ...... parts here ...... };
using (XmlWriter writer = XmlWriter.Create("f:\\MyParts.xml", settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("MyParts");
foreach(var part in parts)
{
writer.WriteStartElement("parts");
writer.WriteStartElement("item");
writer.WriteString(part.Item);
writer.WriteEndElement(); // </item>
writer.WriteStartElement("color");
writer.WriteString(part.Color);
writer.WriteEndElement();
writer.WriteStartElement("size");
writer.WriteString(part.Size);
writer.WriteEndElement(); // </size>
writer.WriteEndElement(); // </parts>
}
writer.WriteEndElement(); // </MyParts>
writer.WriteEndDocument();
writer.Flush();
writer.Close();
}
The general idea is that, for each part in your list of parts, you write the "parts" (should be "part"?) tag and all of its contents, filling item, color and size with data from a Part class, which in its simplest form might be:
class Part
{
public string Item { get; set; }
public Color Color { get; set; }
public string Size { get; set; }
}
The code above does exactly what it looks like: writes an element "MyParts" and then writes a child element "parts" and then a child element "item" with a value of whatever is in your text box.
This smells suspiciously like homework, and is readily Google-able, so I'm only going to give a quick pseudo-answer.
You (may) want to:
Create appropriate class(es) for parts that have the members you want
Create a collection of those items
Update that in-memory collection from your UI
Save the collection using the XML formatting and functionality of your choice (including but not limited to what you are doing above, or LINQ to XML, or XML Serialization, or...)
Have you considered using the out of the box XML Serialization that comes with .NET? You just populate your objects within some collection and then use the XML Serializer to persist to a file. You can then use the DeSerializer to hydrate your objects.
This would allow your to spend more time on your application's UI (WPF), and logic. All you need to do is all the Serializable attribute to your class.
Here's a good example: http://www.jonasjohn.de/snippets/csharp/xmlserializer-example.htm
The biggest benefit is that as you build your data object over time, the serialization/de-serialization will grow with it.
I'm trying to find out why my namespace(xmlns:ren="http://www.example.com/rss2/ext") appears on every custom syndicationItem element (<ren:rssTranslationType typeId="1" xmlns:ren="http://www.example.com/rss2/ext">Original Content</ren:rssTranslationType>) when it's already declared at the top of the file?
Bizarrely this only happens after the .rss file has been read into the syndicationFeed > contents updated > then spat back out. Creating a new rss file works fine (no additional name space usages).
This seemed to cure it:
XmlWriterSettings settings = new XmlWriterSettings{Indent = true, NamespaceHandling = NamespaceHandling.OmitDuplicates};
using (XmlWriter xmlWriter = XmlWriter.Create(sourcePath, settings))
{
rssFeedData.SaveAsRss20(xmlWriter);
}
I am seeking a help regarding file saving of XML file using XDocument (NOT XMLDocument).
So I have a xml file that does not have indentation (in fact it is 1 line). When I read this to XDocument using XDocument.Parse (after reading and storing string using StreamReader), the resulting XDocument is indented.
Alright, I thought it will be fine as long as if I can save it back to the file without indentation. However, even though I have
XmlWriterSettings writerSettings = new XmlWriterSettings();
writerSettings.NewLineOnAttributes = false;
writerSettings.NewLineHandling = NewLineHandling.None;
writerSettings.Indent = false;
and pass that in when I create XmlWriter
using (var writer = XmlWriter.Create(u.ToFileSystemPath(), settings))
{
xd.Save(writer);
}
The resulting XML file still has indentation. When I am debugging on Visual studio, I noticed that the writer is a class XmlWellFormedWriter. Does this have something to do with my result? Any help would be appreciated.
Thank you.
SaveOptions are available on Save() as well as ToString().
string xmlstring =
#"<Top>
<First>1</First>
<Second>Dude</Second>
<Third>Now</Third>
</Top>";
XDocument doc = XDocument.Parse(xmlstring);
doc.Save(#"C:\temp\noIndet.xml", SaveOptions.DisableFormatting);
// string noIndent = doc.ToString(SaveOptions.DisableFormatting);
Output:
xmlWriter.WriteStartElement("Video");
xmlWriter.WriteElementString("FileName", temp.nameFile);
xmlWriter.WriteElementString("VideoName", temp.nameVideo);
xmlWriter.WriteElementString("Path", temp.path);
xmlWriter.WriteEndElement();
I wrote this part of code with c#, but when I'm going to check the result I see just one row without a new line anywhere!
How is it possible?
Thanks so much!
If you want to have a new line with your xml writer then you should use xml settings like so:
XmlWriterSettings settings = new XmlWriterSettings();
//this will auto indent as well as put everything on a new line
settings.Indent = true;
XmlWriter writer = XmlWriter.Create("file_Name.xml", settings);
writer.WriteStartDocument();
xmlWriter.WriteStartElement("Video");
xmlWriter.WriteElementString("FileName", temp.nameFile);
xmlWriter.WriteElementString("VideoName", temp.nameVideo);
xmlWriter.WriteElementString("Path", temp.path);
xmlWriter.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
writer.Close();
Hope this helps
You may want to pass in settings when constructing your xml writer so it behaves the way you want it to. Check this out for more information.
The code below is causing the " Data at the root level is invalid. Line 1, position 1. "
I like to keep the code indent(linebreak) but keep having the problem as mentioned above. I could use the TextReader to load the xml but it will remove the indent which i don't like it. If you know how to fix problem please let me know. Thanks
public XmlDocument MYXML()
{
XmlWriterSettings wSettings = new XmlWriterSettings();
wSettings.Indent = false;
wSettings.OmitXmlDeclaration = false;
MemoryStream ms = new MemoryStream();
XmlWriter xw = XmlWriter.Create(ms, wSettings);// Write Declaration
xw.WriteStartDocument();
// Write the root node
xw.WriteStartElement("Library");
// Write the books and the book elements
xw.WriteStartElement("Book");
xw.WriteStartAttribute("BookType");
xw.WriteString("Hardback");
xw.WriteEndAttribute();
xw.WriteStartElement("Title");
xw.WriteString("Door Number Three");
xw.WriteEndElement();
xw.WriteStartElement("Author");
xw.WriteString("O'Leary, Patrick");
xw.WriteEndElement();
xw.WriteEndElement();
xw.WriteEndElement();
// Close the document
xw.WriteEndDocument();
// Flush the write
xw.Flush();
Byte[] buffer = new Byte[ms.Length];
buffer = ms.ToArray();
string xmlOutput = System.Text.Encoding.UTF8.GetString(buffer);
//The next 3 line works fine but it will remove the Indent from the XmlWriterSettings
//TextReader tr = new StreamReader(ms);
//ms.Seek(0, SeekOrigin.Begin);
//xmlOutput = tr.ReadToEnd() + "";
//Can't nload the xmlOutput from buffer
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(xmlOutput);
return xmldoc;
}
The XmlWriter is writing a utf-8 byte-order mark to the stream. Encoding.UTF8.GetString doesn't account for this (since it's only supposed to occur in files) so the first character of the string becomes an invalid, unprintable character, which is what XmlDocument.LoadXml chokes on.
EDIT: Since you said you want to create an XmlDocument so you can reuse it, I recommend one of the following:
If using .Net 3.5 or newer, use XDocument which is much easier to use (I recommend this).
Create the XmlDocument directly by constructing nodes and adding them to the tree.
Create the XmlDocument directly from the writer by using XPathNavigator (XmlWriter writer = doc.CreateNavigator.AppendChild())
Note that you can't easily add insignificant whitespace to an XmlDocument. Using XDocument and writing it to the output using doc.Save(Response.Output) is by far the easiest option if you want to have nicely formatted output.