What I'm trying to do here is see whether the element exists in the xml document, if it does exist then i want to modify the inner text of it. if it doesnt exist i would like to create it and also create the appropriate inner text for it. However, when it an element does exist and i try to change it's inner text to something shorter than it was the whole xml files writing seems to shift.
My code:
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(path);
XmlNodeList felement = xmldoc.GetElementsByTagName(Element);
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
if (felement.Count == 0)
{
XmlElement elmRoot = xmldoc.DocumentElement;
XmlElement xmlele = xmldoc.CreateElement(Element);
xmlele.AppendChild(xmldoc.CreateTextNode(data));
elmRoot.AppendChild(xmlele);
xmldoc.Save(fs);
}
else
{
felement[0].InnerText = data;
xmldoc.Save(fs);
}
fs.Close();
XML File before modifying with a shorter inner text:
<?xml version="1.0" encoding="utf-8"?>
<MyXMLFile>
<Source>C:\Users\Dacto\Desktop\</Source>
<Destination>C:\Program Files\Adobe</Destination>
</MyXMLFile>
After shorter inner text:
<?xml version="1.0" encoding="utf-8"?>
<MyXMLFile>
<Source>C:\Users\Dacto\Desktop\Napster</Source>
<Destination>C:\Users</Destination>
</MyXMLFile>/MyXMLFile>
See the "extra" /MyXMLFile> what's going on??
Since the output isn't valid XML then it's unlikely that XmlDocument.Save produced the entire content of the file. Given this, I'd suspect that when creating the FileStream you should supply a different parameter rather than FileMode.Open - FileMode.Create will ensure that the file is truncated before being written to - currently it's being overwritten, leaving the old content in place if the new file isn't large enough to cover it.
The problem is with they way that you are writing out the file, not with the xml. If you try something like,
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
byte[] info = new UTF8Encoding(true).GetBytes("AAAAA");
fs.Write(info, 0, info.Length);
You should see:
AAAAA version="1.0" encoding="utf-8"?>
<MyXMLFile>
<Source>C:\Users\Dacto\Desktop\</Source>
<Destination>C:\Program Files\Adobe</Destination>
</MyXMLFile>
Instead of FileMode.Open, you want FileMode.Create, so that the file gets truncated before you write to it if it exists already.
Another option that you have to save the XmlDocument is through the use of an XmlWriter instead of a FileStream.
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(path);
XmlNodeList felement = xmldoc.GetElementsByTagName(Element);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (XmlWriter wr = XmlWriter.Create(path), settings))
{
if (felement.Count == 0)
{
XmlElement elmRoot = xmldoc.DocumentElement;
XmlElement xmlele = xmldoc.CreateElement(Element);
xmlele.AppendChild(xmldoc.CreateTextNode(data));
elmRoot.AppendChild(xmlele);
xmldoc.Save(wr);
}
else
{
felement[0].InnerText = data;
xmldoc.Save(wr);
}
}
Also, if you continue with your original FileStream method, then I would recommend wrapping it in a using statement like I have done above with the XmlWriter and if you do, then you can get rid of the fs.Close() statement.
Related
I didn't know that I can use XSD schema to serialize received XML file. I used xsd.exe to generate cs class from XSD file and now I need to use that class to get data in class properties but I miss one thing and I need help.
This is the code:
private void ParseDataFromXmlDocument_UsingSerializerClass(XmlDocument doc)
{
XmlSerializer ser = new XmlSerializer(typeof(ClassFromXsd));
string filename = Path.Combine("C:\\myxmls\\test", "xmlname.xml");
ClassFromXsdmyClass = ser.Deserialize(new FileStream(filename, FileMode.Open)) as ClassFromXsd;
if (myClass != null)
{
// to do
}
...
Here I use XML file from drive. And I want to use this XmlDocument from parameter that I passed in. So how to adapt this code to use doc instead XML from drive?
You could write the XmlDocument to a MemoryStream, and then Deserialize it like you already did.
XmlDocument doc = new XmlDocument();
ClassFromXsd obj = null;
using (var s = new MemoryStream())
{
doc.Save(s);
var ser = new XmlSerializer(typeof (ClassFromXsd));
s.Seek(0, SeekOrigin.Begin);
obj = (ClassFromXsd)ser.Deserialize(s);
}
I have a zip file that contains an xml file,
I'm Loading this xml file to an xml document without having to extract the file.
this is done via a stream.
after doing so, I'm modifying the inner text of some nodes.
The Problem is that I'm getting the previous mentioned exception after trying to save the stream, here's the code:
(I'm using DotNetZip here)
ZipFile zipFile = ZipFile.Read(zipPath); // the path is my desktop
foreach (ZipEntry entry in zipFile)
{
if (entry.FileName == "myXML.xml")
{
//creating the stream and loading the xml doc from the zip file:
Stream stream = zipFile[entry.FileName].OpenReader();
XmlReader xReader = XmlReader.Create(stream);
XmlDocument xDoc = new XmlDocument();
xDoc.Load(xReader);
//changing the inner text of the doc nodes:
xDoc.DocumentElement.SelectSingleNode("Account/Name").InnerText = "VeXe";
xDoc.DocumentElement.SelectSingleNode("Account/Money").InnerText = "Million$";
xDoc.Save(stream); // here's where I got the exception.
break;
}
}
I'm not a pro coder, but instead of xDoc.Save(stream); I noticed that it could also take a XmlWriter as a parameter, so I tried making an instance of the XmlWriter immediately after instantiating the XmlReader ..
I tried doing this: xDoc.Save(XmlWriter)
I got an exception saying something like: "Cannot Write After Reading"
how can I successfully save the xDoc ?
ADDED:
I had an idea of saving the xml file in some other place, like a temp folder or something
then adding that saved file in the zip overwriting the old one, then deleting the xml file in the temp ..
but that's not what i want, I want to deal directly with the zip file, in and out, no third parties.
You're attempting to write to the same Stream you've opened it with. You cannot do that.
Perhaps try something like this:
ZipFile zipFile = ZipFile.Read(zipPath); // the path is my desktop
foreach (ZipEntry entry in zipFile)
{
if (entry.FileName == "myXML.xml")
{
//creating the stream and loading the xml doc from the zip file:
using (Stream stream = zipFile[entry.FileName].OpenReader()) {
XmlReader xReader = XmlReader.Create(stream);
XmlDocument xDoc = new XmlDocument();
xDoc.Load(xReader);
}
//changing the inner text of the doc nodes:
xDoc.DocumentElement.SelectSingleNode("Account/Name").InnerText = "VeXe";
xDoc.DocumentElement.SelectSingleNode("Account/Money").InnerText = "Million$";
using (StreamWriter streamWriter = new StreamWriter(pathToSaveTo)) {
xDoc.Save(streamWriter);
break;
}
}
}
A quick look at the docs leads me to believe that you should do it something like this:
using(ZipFile zipFile = ZipFile.Read(zipPath))
foreach (ZipEntry entry in zipFile)
{
if (entry.FileName == "myXML.xml")
{
XmlDocument xDoc = new XmlDocument();
//creating the stream and loading the xml doc from the zip file:
using(Stream stream = zipFile[entry.FileName].OpenReader())
using(XmlReader xReader = XmlReader.Create(stream))
{
xDoc.Load(xReader);
}
//changing the inner text of the doc nodes:
xDoc.DocumentElement.SelectSingleNode("Account/Name").InnerText = "VeXe";
xDoc.DocumentElement.SelectSingleNode("Account/Money").InnerText = "Million$";
using(var ms=new MemoryStream())
using(var sw=new StreamWriter(ms))
{
xDoc.Save(sw);
sw.Flush();
ms.Position=0;
zipFile.UpdateEntry(entry.FileName,ms);
}
break;
}
}
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.
I want to append some nodes to an xml document using Linq2XML. The file in question is being used by other processes and they should be able to read the file while I update it. So I came up with this solution, which obviously isn't the correct way (The method doc.Save() fails and says that another process is using the file):
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
{
doc = XDocument.Load(new StreamReader(fs));
doc.Root.Add(entry);
doc.Save(filename);
fs.Close();
}
Any help is greatly appreceated.
Load the document, close the stream, save it again. That also means you can open it in a simpler way :)
XDocument doc;
using (StreamReader reader = File.OpenText(filename))
{
doc = XDocument.Load(reader);
doc.Root.Add(entry);
}
doc.Save(filename);
I had a Dataset with some data in it. When I tried to write this DataSet into a file, everything was OK. But When I tried to write it into a MemoryStream, the XML file declaration was lost.
The code looks like:
DataSet dSet = new DataSet();
//load schema, fill data in
dSet.WriteXML("testFile.xml");
MemoryStream stream = new MemoryStream();
dSet.WriteXML(stream);
stream.Seek(0,SeekOrigin.Begin);
When I opened file testFile.xml, I got:
<?xml version="1.0" standalone="yes"?>
//balabala
But When I open the stream with StreamReader, I only got:
//balabala
Somebody said I can insert XML file declaration in my stream manually. It works but seems so ugly. Do you know why it dropped the first line and any more simple solution?
It wasn't dropped. Simply not included. Though it is highly recommend the xml declaration is not a required element of the xml specification.
http://msdn.microsoft.com/en-us/library/ms256048(VS.85).aspx
You can use XmlWriter.WriteStartDocument to include the xml declaration in the stream like so:
MemoryStream stream = new MemoryStream();
var writer = XmlWriter.Create(stream);
writer.WriteStartDocument(true);
dSet.WriteXML(stream);
I try your solution with DataTable and don't work correctly.
using (MemoryStream stream = new MemoryStream()) {
using (XmlTextWriter writer = new XmlTextWriter(stream, Encoding.UTF8)) {
writer.WriteStartDocument(); //<?xml version="1.0" encoding="utf-8"?>
writer.WriteRaw("\r\n"); //endline
writer.Flush(); //Write immediately the stream
dTable.WriteXml(stream);
}
}
If you disassemble the 2.0 code you'll see that the WriteXml method that takes a file name explictly writes out the declaration (XmlWriter.WriteStartDocument) but the WriteXml methods that take a stream or writer do not.