Xml.Serialization, Correct way to append - c#

For my windows 7 phone application i serialize my object and save it to items.xml.
Now when i want to add more items to my items.xml i have a problem co's the writer.WriteEndElement();
has already been written.
now what i can do is read all the items that are in items.xml save it in a list and then overwrite the current items.xml tough this puts heavy usage on the phone so i doubt its the correct way but is there any decent work around for this and thus decently append the file ? thanks !
using (var isfs = new IsolatedStorageFileStream(#"items.xml", FileMode.Append, store))
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (var writer = XmlWriter.Create(isfs, settings))
{
writer.WriteStartElement("ToDoItem");
item.WriteXml(writer);
writer.WriteEndElement();
writer.Close();
}
}
Misc.
[XmlRoot("ToDoItem")]
public class ToDoItem : IXmlSerializable{
...
}

Here is one way to write more than one object to an XMLSerializer:
List<Object> myobjects = new List<Object>( {myObject1, myObject2 } ); // IEnumerable Constructor
using (XMLSerializer xml = new XMLSerializer(typeof(List<Object>)))
{
// write them to the file
xml.Serialize(File.OpenWrite(filename), myobjects);
}
And to deserialize:
using (XMLSerializer xml = new XMLSerializer(typeof(List<object>)))
{
List<Object> myobjects = xml.Deserialize(File.OpenRead(filename));
}

Related

How to use XDocument.Save to save a file using custom indentation for attributes

My goal is to output a modified XML file and preserve a special indentation that was present in the original file. The objective is so that the resulting file still looks like the original, making them easier to compare and merge through source control.
My program will read a XML file and add or change one specific attribute.
Here is the formatting I'm trying to achieve / preserve:
<Base Import="..\commom\style.xml">
<Item Width="480"
Height="500"
VAlign="Center"
Style="level1header">
(...)
In this case, I simply wish to align all attributes past the first one with the first one.
XmlWriterSettings provides formatting options, but they won't achieve the result I'm looking for.
settings.Indent = true;
settings.NewLineOnAttributes = true;
These settings will put the first attribute on a newline, instead of keeping it on the same line as the node, and will line up attributes with the node.
Here is the Load call, which asks to preserve whitespace:
MyXml = XDocument.Load(filepath, LoadOptions.PreserveWhitespace);
But it seems like it won't do what I expected.
I tried to provide a custom class, which derives from XmlWriter to the XDocument.Save call, but I haven't managed to insert whitespace correctly without running into InvalidOperationException. Plus that solution seems overkill for the small addition I'm looking for.
For reference, this is my save call, not using my custom xml writer (which doesn't work anyway)
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.NewLineOnAttributes = true;
settings.OmitXmlDeclaration = true;
using (XmlWriter writer = XmlWriter.Create(filepath + "_auto", settings))
{
MyXml.Save(writer);
}
I ended up not using XDocument.Save altogether, and instead created a class that takes the XDocument, an XmlWriter, as well as a TextWriter.
The class parses all nodes in XDocument, TextWriter is bound to the file on disk, which XmlWriter uses as its output pipe.
My class then uses the XmlWriter to output xml. To achieve the extra spacing, I used the solution described here, https://stackoverflow.com/a/24010544/5920497 , which is why I also use the underlying TextWriter.
Here's an example of the solution.
Calling the class to save the document:
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.NewLineOnAttributes = false; // Behavior changed in PrettyXmlWriter
settings.OmitXmlDeclaration = true;
using(TextWriter rawwriter = File.CreateText(filepath))
using (XmlWriter writer = XmlWriter.Create(rawwriter, settings))
{
// rawwriter is used both by XmlWriter and PrettyXmlWriter
PrettyXmlWriter outputter = new PrettyXmlWriter(writer, rawwriter);
outputter.Write(MyXml);
writer.Flush();
writer.Close();
}
Inside PrettyXmlWriter:
private XmlWriter Writer { get; set; }
private TextWriter InnerTextWriter { get; set; }
public void Write(XDocument doc)
{
XElement root = doc.Root;
WriteNode(root, 0);
}
private void WriteNode(XNode node, int currentNodeDepth)
{
if(node.NodeType == XmlNodeType.Element)
{
WriteElement((XElement)node, currentNodeDepth);
}
else if(node.NodeType == XmlNodeType.Text)
{
WriteTextNode((XText)node, currentNodeDepth, doIndentAttributes);
}
}
private void WriteElement(XElement node, int currentNodeDepth)
{
Writer.WriteStartElement(node.Name.LocalName);
// Write attributes with indentation
XAttribute[] attributes = node.Attributes().ToArray();
if(attributes.Length > 0)
{
// First attribute, unindented.
Writer.WriteAttributeString(attributes[0].Name.LocalName, attributes[0].Value);
for(int i=1; i<attributes.Length; ++i)
{
// Write indentation
Writer.Flush();
string indentation = Writer.Settings.NewLineChars + string.Concat(Enumerable.Repeat(Writer.Settings.IndentChars, currentNodeDepth));
indentation += string.Concat(Enumerable.Repeat(" ", node.Name.LocalName.Length + 1));
// Using Underlying TextWriter trick to output whitespace
InnerTextWriter.Write(indentation);
Writer.WriteAttributeString(attributes[i].Name.LocalName, attributes[i].Value);
}
}
// output children
foreach(XNode child in node.Nodes())
{
WriteNode(child, currentNodeDepth + 1);
}
Writer.WriteEndElement();
}

Well Formed XML using Service Stack

I'm building an MVC5 application which pulls records from a database and allows a user to perform some basic data cleansing edits.
Once the data has been cleansed it needs to be exported as XML, run through a validator and then uploaded to a third party portal.
I'm using Service Stack, and I've found it fairly quick and straightforward in the past, particularly when outputting to CSV.
The one issue I'm having is with the XML serialzer. I'm not sure how to make it generate well formed XML.
The file that i'm getting simply dumps it on one line, which won't validate because it isn't well formed.
below is an extract from my controller action:
Response.Clear();
Response.ContentType = "text/xml";
Response.AddHeader("Content-Disposition", "attachment; filename="myFile.xml"");
XmlSerializer.SerializeToStream(viewModel, Response.OutputStream);
Response.End();
UPDATE: Thanks for the useful comments, as explained I'm not talking about pretty printing, the issue is I need to run the file through a validator before uploading it to a third party. The error message the validator is throwing is Error:0000, XML not well-formed. Cannot have more than one tag on one line.
Firstly, be aware that most white space (including new lines) in XML is insignificant -- it has no meaning, and is only for beautification. The lack of new lines doesn't make the XML ill-formed. See White Space in XML Documents or https://www.w3.org/TR/REC-xml/#sec-white-space. Thus in theory it shouldn't matter whether ServiceStack's XmlSerializer is putting all of your XML on a single line.
That being said, if for whatever reason you must cosmetically break your XML up into multiple lines, you'll need to do a little work. From the source code we can see that XmlSerializer uses DataContractSerializer with a hardcoded static XmlWriterSettings that does not allow for setting XmlWriterSettings.Indent = true. However, since this class is just a very thin wrapper on Microsoft's data contract serializer, you can substitute your own code:
public static class DataContractSerializerHelper
{
private static readonly XmlWriterSettings xmlWriterSettings = new XmlWriterSettings { Indent = true, IndentChars = " " };
public static string SerializeToString<T>(T from)
{
try
{
using (var ms = new MemoryStream())
using (var xw = XmlWriter.Create(ms, xmlWriterSettings))
{
var serializer = new DataContractSerializer(from.GetType());
serializer.WriteObject(xw, from);
xw.Flush();
ms.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(ms);
return reader.ReadToEnd();
}
}
catch (Exception ex)
{
throw new SerializationException(string.Format("Error serializing \"{0}\"", from), ex);
}
}
public static void SerializeToWriter<T>(T value, TextWriter writer)
{
try
{
using (var xw = XmlWriter.Create(writer, xmlWriterSettings))
{
var serializer = new DataContractSerializer(value.GetType());
serializer.WriteObject(xw, value);
}
}
catch (Exception ex)
{
throw new SerializationException(string.Format("Error serializing \"{0}\"", value), ex);
}
}
public static void SerializeToStream(object obj, Stream stream)
{
if (obj == null)
return;
using (var xw = XmlWriter.Create(stream, xmlWriterSettings))
{
var serializer = new DataContractSerializer(obj.GetType());
serializer.WriteObject(xw, obj);
}
}
}
And then do:
DataContractSerializerHelper.SerializeToStream(viewModel, Response.OutputStream);

Avoid XML Escape Double Quote

I'm currently trying to serialize a class into XML to be posted to php web service.
Whenever I did the normal serialization using XMLSerializer, XML declaration is always appear in the first line of the XML document (similar as to <?xml ....?>). I tested the XML and unable to get it working because the endpoint does not accept XML declaration and I can't do anything about it.
I'm unfamiliar with XML Serialization in C# to be honest.
Therefore, I used XMLWriter to do this as below :-
private string SerializeClassToString(GetRiskReport value)
{
var emptyNS = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
var ser = new XmlSerializer(value.GetType());
var settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
using (var stream = new StringWriter())
{
using (var writer = XmlWriter.Create(stream, settings))
{
ser.Serialize(writer, value, emptyNS);
return stream.ToString();
}
}
}
Result for the Namespace is
<GetRiskReport FCRA=\"false\" ReturnResultsOnly=\"false\" Monitoring=\"false\">
... and I'm able to omit the XML Declaration, however I'm being introduced with 2 new problem.
I got \r\n for new line and I have escaped double quote such as ReturnResultsOnly=\"false\" Monitoring=\"false\" which is also unable processed by the endpoint.
I would like to ask is that does anyone can give me an idea on how to change the XmlWriterSetting to omit XML Declaration, avoid \r\n and also avoid escaped double quotes \"
Thanks for your advice in advance.
Simon
Try with following settings
settings.NewLineHandling = NewLineHandling.None;
settings.CheckCharacters = false;
private void SerializeClassToString(GetRiskReport value)
{
var emptyNS = new XmlSerializerNamespaces(new[]{XmlQualifiedName.Empty});
var ser = new XmlSerializer(value.GetType());
var settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
string path = 'your_file_path_here'
if (File.Exists(path)) File.Delete(path);
FileStream stream = File.Create(path);
using (var writer = XmlWriter.Create(stream, settings))
{
ser.Serialize(writer, value, emptyNS);
return;
}
}
There was no way to avoid ms bug or thier intensional specification about xmlserializing.It's easier and faster to use filestream object.

Save and restore values from ArrayList

I store some string values in an array list at runtime. When the application closes I want to store the data from the array list in another location so that I can open and retrieve the data the next time the application starts. What are some possible ways to do this?
You can use Reading and Writing from/to files or storing the values in windows registry.
For Reading/Writing from/to files use:
StreamReader sr = new StreamReader(#"C:/store.dll"); //for reading
StreamWriter sw = new StreamWriter(#"C:/store.dll"); //for writing
This is the basic. Here are two great articles for this:
Reding from files
Writing to files
I used this for storing the High Scores for a simple game. :)
Here is a nice tutorial for using Windows registry.
You could Serialize the ArrayList and write it to disk, then later on load the file and Deserialize it to an object:
public static XDocument Serialize<T>(T objectIn, Type[] extraTypes)
{
try
{
var target = new XDocument();
XmlSerializer s = extraTypes != null ? new XmlSerializer(objectIn.GetType(), extraTypes) : new XmlSerializer(objectIn.GetType());
s = extraTypes != null
? new XmlSerializer(objectIn.GetType(), extraTypes)
: new XmlSerializer(objectIn.GetType());
var writer = target.CreateWriter();
s.Serialize(writer, objectIn);
writer.Close();
return target;
}
catch (Exception ex)
{
throw new Exception(string.Format("Could not serialize object: {0}", ex.Message));
}
}
public static T Deserialize<T>(XDocument xDocument, string defaultNamespace)
{
XmlSerializer s = new XmlSerializer(typeof(T), defaultNamespace);
T result = (T)s.Deserialize(xDocument.CreateReader());
return result;
}

streaming XML serialization in .net

I'm trying to serialize a very large IEnumerable<MyObject> using an XmlSerializer without keeping all the objects in memory.
The IEnumerable<MyObject> is actually lazy..
I'm looking for a streaming solution that will:
Take an object from the IEnumerable<MyObject>
Serialize it to the underlying stream using the standard serialization (I don't want to handcraft the XML here!)
Discard the in memory data and move to the next
I'm trying with this code:
using (var writer = new StreamWriter(filePath))
{
var xmlSerializer = new XmlSerializer(typeof(MyObject));
foreach (var myObject in myObjectsIEnumerable)
{
xmlSerializer.Serialize(writer, myObject);
}
}
but I'm getting multiple XML headers and I cannot specify a root tag <MyObjects> so my XML is invalid.
Any idea?
Thanks
The XmlWriter class is a fast streaming API for XML generation. It is rather low-level, MSDN has an article on instantiating a validating XmlWriter using XmlWriter.Create().
Edit: link fixed. Here is sample code from the article:
async Task TestWriter(Stream stream)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Async = true;
using (XmlWriter writer = XmlWriter.Create(stream, settings)) {
await writer.WriteStartElementAsync("pf", "root", "http://ns");
await writer.WriteStartElementAsync(null, "sub", null);
await writer.WriteAttributeStringAsync(null, "att", null, "val");
await writer.WriteStringAsync("text");
await writer.WriteEndElementAsync();
await writer.WriteCommentAsync("cValue");
await writer.WriteCDataAsync("cdata value");
await writer.WriteEndElementAsync();
await writer.FlushAsync();
}
}
Here's what I use:
using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
using System.Text;
using System.IO;
namespace Utils
{
public class XMLSerializer
{
public static Byte[] StringToUTF8ByteArray(String xmlString)
{
return new UTF8Encoding().GetBytes(xmlString);
}
public static String SerializeToXML<T>(T objectToSerialize)
{
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings =
new XmlWriterSettings {Encoding = Encoding.UTF8, Indent = true};
using (XmlWriter xmlWriter = XmlWriter.Create(sb, settings))
{
if (xmlWriter != null)
{
new XmlSerializer(typeof(T)).Serialize(xmlWriter, objectToSerialize);
}
}
return sb.ToString();
}
public static void DeserializeFromXML<T>(string xmlString, out T deserializedObject) where T : class
{
XmlSerializer xs = new XmlSerializer(typeof (T));
using (MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(xmlString)))
{
deserializedObject = xs.Deserialize(memoryStream) as T;
}
}
}
}
Then just call:
string xml = Utils.SerializeToXML(myObjectsIEnumerable);
I haven't tried it with, for example, an IEnumerable that fetches objects one at a time remotely, or any other weird use cases, but it works perfectly for List<T> and other collections that are in memory.
EDIT: Based on your comments in response to this, you could use XmlDocument.LoadXml to load the resulting XML string into an XmlDocument, save the first one to a file, and use that as your master XML file. For each item in the IEnumerable, use LoadXml again to create a new in-memory XmlDocument, grab the nodes you want, append them to the master document, and save it again, getting rid of the new one.
After you're finished, there may be a way to wrap all of the nodes in your root tag. You could also use XSL and XslCompiledTransform to write another XML file with the objects properly wrapped in the root tag.
You can do this by implementing the IXmlSerializable interface on the large class. The implementation of the WriteXml method can write the start tag, then simply loop over the IEnumerable<MyObject> and serialize each MyObject to the same XmlWriter, one at a time.
In this implementation, there won't be any in-memory data to get rid of (past what the garbage collector will collect).

Categories