Processing an XML file removes comments - c#

This snippet <!--Please don't delete this--> is part of my xml file. After running this method, the resulting xml file does not contain this snippet anymore <!--Please don't delete this-->. Why is this?
Here's my method:
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
TextWriter writer = new StreamWriter(path);
serializer.Serialize(writer, settings);
writer.Close();

Well, this is quite obvious:
the XmlSerializer will parse the XML file and extract all instances of Settings from it - your comment won't be part of any of those objects
when you write those back out again, only the contents of the Settings objects is written out again
Your comment will fall through the cracks - but I don't see any way you could "save" that comment as long as you're using the XmlSerializer approach.
What you need to do is use the XmlReader / XmlWriter instead:
XmlReader reader = XmlReader.Create("yourfile.xml");
XmlWriter writer = XmlWriter.Create("your-new-file.xml");
while (reader.Read())
{
writer.WriteNode(reader, true);
}
writer.Close();
reader.Close();
This will copy all xml nodes - including comments - to the new file.

<!-- --> signifies a comment in XML. You are writing an object out to XML - objects do not have comments as they get compiled out during compilation.
That is, the Settings object (which is probably a de-serialized form of your .config XML) does not hold comments in memory after de-serializing, so they will not get serialized back either. There is nothing you can do about this behavior of the framework as there is no built in mechanism to de-serialize comments using XmlSerializer.

Related

Serialise C# classes to XML keeping the structure

I received 4 XSD schema files (one is the root, one for header/trailer, one for the base and one for element types)
Passing the root to the xsd.exe, successfully generates the Classes with the proper structure according to the XSD,
we can now assign values to the objects of those classes and populate them, the question is how can we serialise them to XML output keeping the original structure?
We have tried this :
// Serialize all the objects to a single XML file and save it to the output directory
XmlSerializer serializer = new XmlSerializer(objects.GetType());
using (TextWriter writer = new StreamWriter("output.xml"))
{
serializer.Serialize(writer, objects);
}
}
But it's throwing different errors saying:
InnerException = {"Token StartElement in state EndRootElement would result in an invalid XML document. Make sure that the ConformanceLevel setting is set to ConformanceLevel.Fragment or ConformanceLevel.Auto if you want to write an XML fragment. "}
we added setting for the writer:
// Create an XmlWriterSettings object and set the ConformanceLevel to Fragment
var settings = new XmlWriterSettings
{
ConformanceLevel = ConformanceLevel.Fragment
};
// Serialize the data to XML using the serializer and the settings
using (var writer = XmlWriter.Create("example.xml", settings))
{
serializer.Serialize(writer, data);
}
But now it's giving us this error:
{"WriteStartDocument cannot be called on writers created with ConformanceLevel.Fragment."}
Question is, isn't there any straightforward way of serialising the objects to XML without looping through them and have startelement/end element manually inserted to the output XML? doing that is changing the XML structure.
Full code fiddle
XSD files

How to Specify XML Encoding when Serializing an Object in C#

I am serializing a C# object into an XML document and sending the XML document to a third party vendor. The vendor is telling me that the encoding specification in the document is UTF-16, but the XML document contains UTF-8 content and they can't use it. Here is the code I am using to create the XML file, which runs without error and creates an XML document.
// Instantiate xmlSerializer with my object type.
XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyObject));
// Instantiate a new stream and pass file location and mode.
Stream stream = new FileStream(#"C:\doc.xml", FileMode.Create);
// Instantiate xmlWriter and pass stream and encoding.
XmlWriter xmlWriter = new XmlTextWriter(stream, Encoding.Unicode);
// Call serialize method and pass xmlWriter and my object.
xmlSerializer.Serialize(xmlWriter, myObject);
// Close writer and stream.
xmlWriter.Close();
stream.Close();
When I run this, the XML Doc shows this on the first line:
<?xml version="1.0" encoding="UTF-16"?>
I've tried changing the Encoding from Encoding.Unicode to Encoding.UTF8 in the XmlTextWriter, but that doesn't change the first line of the XML Doc and it still shows UTF-16.
I also tried using the Serialize method signature that takes 4 parameters (writer, object, namespaces, encoding) and specified UTF8 as the encoding and that didn't change the XML Doc specification either.
I believe all I need to do is change the encoding that shows in the XML Doc to UTF-8 and the third party vendor will be happy. I can't figure out what I am doing wrong.
If I change from Encoding.Unicode to Encoding.UTF8, the file is generated properly. Perhaps you're looking at an old version of your file?
In an unrelated bit, you should use using for deterministic disposal of objects which implement IDisposable:
XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyObject));
using (Stream stream = new FileStream(#".\doc.xml", FileMode.Create))
using (XmlWriter xmlWriter = new XmlTextWriter(stream, Encoding.UTF8))
{
xmlSerializer.Serialize(xmlWriter, myObject);
}

Serialize to an XML document without overwriting previous data

I need to serialize to an XML document without overwriting the data that is currently in there. I have a method that does this and it will save to the xml file, but will delete whatever is currently in that file upon serializing. Below is the code.
public void SaveSubpart()
{
SOSDocument doc = new SOSDocument();
doc.ID = 1;
doc.Subpart = txtSubpart.Text;
doc.Title = txtTitle.Text;
doc.Applicability = txtApplicability.Text;
doc.Training = txtTraining.Text;
doc.URL = txtUrl.Text;
StreamWriter writer = new StreamWriter(Server.MapPath("~/App_Data/Contents.xml"));
System.Xml.Serialization.XmlSerializer serializer;
try
{
serializer = new System.Xml.Serialization.XmlSerializer(doc.GetType());
serializer.Serialize(writer, doc);
}
catch (Exception ex)
{
//e-mail admin - serialization failed
}
finally
{ writer.Close(); }
}
The contract for the StreamWriter constructor taking only a filename says that if the named file exists, it is overwritten. So this has nothing to do with serializing to XML, per se. You would get the same result if you wrote to the stream through some other means.
The way to do what you are looking for is to read the old XML file into memory, make whatever changes are necessary, and then serialize and write the result to disk.
And even if it was possible to transparently modify an on-disk XML file, that's almost certainly what would happen under the hood because it's the only way to really do it. Yes, you probably could fiddle around with seeking and writing directly on disk, but what if something caused the file to change on disk while you were doing that? If you do the read/modify/write sequence, then you lose out on the changes that were made after you read the file into memory; but if you modify the file directly on disk by seeking and writing, you would be almost guaranteed to end up with the file in an inconsistent state.
And of course, you could only do it if you could fit whatever changes you wanted to make into the bytes that were already on disk...
If concurrency is a problem, either use file locking or use a proper database with transactional support.
try this:
StreamWriter writer = new StreamWriter(Server.MapPath("~/App_Data/Contents.xml"),true);
this determines to append the data to the file.
true=append,
false = overwrite
more info http://msdn.microsoft.com/en-us/library/36b035cb.aspx
So what you want to implement is to serialize an object without overwriting it to an existing file.
Thus
XmlSerializer s = new XmlSerializer(doc.GetType());
TextWriter w = new StringWriter();
s.Serialize(w, doc);
var yourXMLstring = w.ToString();
Then you can process this xml string and append it to existing xml file if you want to.
XmlDocument xml = new XmlDocument();
xml.LoadXml(yourXMLstring );

XmlSerializer Deserialize fails in release mode

This is pretty odd. I have a configuration file which is well formed XML. I create a stream from the file and serialize it using what seems to be pretty typical code:
TextWriter tw = new StreamWriter(tempFile);
I use a serializer created as follows:
XmlSerializer ConfigSettingSerializer = new XmlSerializer(typeof(ConfigSettings));
Where ConfigSettings is just a container class containing string variables and values.
I then take the serialized stream and stash it away as a configuration using the ConfigurationManager class and AppSettings. I then retrieve the serialized data from appSettings and attempt to convert the stream back to the original class:
string configXml = ConfigurationManager.AppSettings[Id];
using (StringReader reader = new StringReader(configXml))
{
retVal = (ConfigSettings)MVHelper.ConfigSettingSerializer.Deserialize(reader);
}
This all works perfectly well until I switch from Debug to Release, when I get an error on the Deserialize call about invalid XML, complaining about the very last character in the document: There is an error in XML document (92, 18). The inner exception is: "Data at the root level is invalid. Line 92, position 18". The document is identical to the one generated in debug mode, and it renders fine in any browser. My guess is that there maybe something else going on and that the real error is somehow being masked, but so far I don't see it. Any advice would be greatly appreciated.
Thanks,
Gary
Load the XML file in a hex editor or other binary editor and check for unprintable characters like an encoding preamble.

Wrap Serialized Data to a higher level XML tag

All,
I have a list of objects which I have serialized to an XML document using XmlSerializer.
However I would like to wrap the whole result into two tags:
<message>
<!-My Serialized content goes here-->
</message>
Do I need to open it as an XML Document and Add a new root element or is there another way of doing it ?
Rgds,
MK
XmlSerializer writes to an XmlWriter. Write the start tag to the writer first, then serialize, and close your message tag at the end.
Example:
XmlWriter writer = // Your writer
XmlSerializer ser = new XmlSerializer(typeof(DateTime));
writer.WriteStartElement("message");
ser.Serialize(writer,DateTime.Now);
writer.WriteEndElement();

Categories

Resources