Writing compact xml with XmlDictionaryWriter.CreateBinaryWriter and a XmlDictionary - c#

I want to write an xml document to disk in a compact format. To this end, I use the net framework method XmlDictionaryWriter.CreateBinaryWriter(Stream stream,IXmlDictionary dictionary)
This method writes a custom compact binary xml representation, that can later be read by XmlDictionaryWriter.CreateBinaryReader. The method accepts an XmlDictionary that can contain common strings, so that those strings do not have to be printed in the output each time. Instead of the string, the dictionary index will be printed in the file. CreateBinaryReader can later use the same dictionary to reverse the process.
However the dictionary I pass is apparently not used. Consider this code:
using System.IO;
using System.Xml;
using System.Xml.Linq;
class Program
{
public static void Main()
{
XmlDictionary dict = new XmlDictionary();
dict.Add("myLongRoot");
dict.Add("myLongAttribute");
dict.Add("myLongValue");
dict.Add("myLongChild");
dict.Add("myLongText");
XDocument xdoc = new XDocument();
xdoc.Add(new XElement("myLongRoot",
new XAttribute("myLongAttribute", "myLongValue"),
new XElement("myLongChild", "myLongText"),
new XElement("myLongChild", "myLongText"),
new XElement("myLongChild", "myLongText")
));
using (Stream stream = File.Create("binaryXml.txt"))
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, dict))
{
xdoc.WriteTo(writer);
}
}
}
The produced output is this (binary control characters not shown)
#
myLongRootmyLongAttribute˜myLongValue#myLongChild™
myLongText#myLongChild™
myLongText#myLongChild™
myLongText
So apparently the XmlDictionary has not been used. All strings appear in their entirety in the output, even multiple times.
This is not a problem limited to XDocument. In the above minimal example I used a XDocument to demonstrate the problem, but originally I stumbled upon this while using XmlDictionaryWriter in conjunction with a DataContractSerializer, as it is commonly used. The results were the same:
[Serializable]
public class myLongChild
{
public double myLongText = 0;
}
...
using (Stream stream = File.Create("binaryXml.txt"))
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, dict))
{
var dcs = new DataContractSerializer(typeof(myLongChild));
dcs.WriteObject(writer, new myLongChild());
}
The resulting output did not use my XmlDictionary.
How can I get XmlDictionaryWriter to use the suplied XmlDictionary?
Or have I misunderstood how this works?
with the DataContractSerializer approach, I tried debugging the net framework code (visual studio/options/debugging/enable net. framework source stepping). Apparently the Writer does attempt to lookup each of the above strings in the dictionary, as expected. However the lookup fails in line 356 of XmlbinaryWriter.cs, for reasons that are not clear to me.
Alternatives I have considered:
There is an overload for XmlDictionaryWriter.CreatebinaryWriter, that also accepts a XmlBinaryWriterSession. The writer then adds any new strings it encounters into the session dictionary. However, I want to only use a static dictionary for reading and writing, which is known beforehand.
I could wrap the whole thing into a GzipStream and let the compression take care of the multiple instances of strings. However, this would not compress the first instance of each string, and seems like a clumsy workaround overall.

Yes there is a misunderstanding. XmlDictionaryWriter is primarily used for serialization of objects and it is child class of XmlWriter. XDocument.WriteTo(XmlWriter something) takes XmlWriter as argument. The call XmlDictionaryWriter.CreateBinaryWriter will create an instance of System.Xml.XmlBinaryNodeWriter internally. This class has both methods for "regular" writing:
// override of XmlWriter
public override void WriteStartElement(string prefix, string localName)
{
// plain old "xml" for me please
}
and for dictionary based approach:
// override of XmlDictionaryWriter
public override void WriteStartElement(string prefix, XmlDictionaryString localName)
{
// I will use dictionary to hash element names to get shorter output
}
The later is mostly used if you serialize object via DataContractSerializer (notice its method WriteObject takes argument of both XmlDictionaryWriter and XmlWriter type), while XDocument takes just XmlWriter.
As for your problem - if I were you I'd make my own XmlWriter:
class CustomXmlWriter : XmlWriter
{
private readonly XmlDictionaryWriter _writer;
public CustomXmlWriter(XmlDictionaryWriter writer)
{
_writer = writer;
}
// override XmlWriter methods to use the dictionary-based approach instead
}
UPDATE (based on your comment)
If you indeed use DataContractSerializer you have few mistakes in your code.
1) POC classes have to be decorated with [DataContract] and [DataMember] attribute, the serialized value should be property and not field; also set namespace to empty value or you'll have to deal with namespaces in your dictionary as well. Like:
namespace XmlStuff {
[DataContract(Namespace = "")]
public class myLongChild
{
[DataMember]
public double myLongText { get; set; }
}
[DataContract(Namespace = "")]
public class myLongRoot
{
[DataMember]
public IList<myLongChild> Items { get; set; }
}
}
2) Provide instance of session as well; for null session the dictionary writer uses default (XmlWriter-like) implementation:
// order matters - add new items only at the bottom
static readonly string[] s_Terms = new string[]
{
"myLongRoot", "myLongChild", "myLongText",
"http://www.w3.org/2001/XMLSchema-instance", "Items"
};
public class CustomXmlBinaryWriterSession : XmlBinaryWriterSession
{
private bool m_Lock;
public void Lock() { m_Lock = true; }
public override bool TryAdd(XmlDictionaryString value, out int key)
{
if (m_Lock)
{
key = -1;
return false;
}
return base.TryAdd(value, out key);
}
}
static void InitializeWriter(out XmlDictionary dict, out XmlBinaryWriterSession session)
{
dict = new XmlDictionary();
var result = new CustomXmlBinaryWriterSession();
var key = 0;
foreach(var term in s_Terms)
{
result.TryAdd(dict.Add(term), out key);
}
result.Lock();
session = result;
}
static void InitializeReader(out XmlDictionary dict, out XmlBinaryReaderSession session)
{
dict = new XmlDictionary();
var result = new XmlBinaryReaderSession();
for (var i = 0; i < s_Terms.Length; i++)
{
result.Add(i, s_Terms[i]);
}
session = result;
}
static void Main(string[] args)
{
XmlDictionary dict;
XmlBinaryWriterSession session;
InitializeWriter(out dict, out session);
var root = new myLongRoot { Items = new List<myLongChild>() };
root.Items.Add(new myLongChild { myLongText = 24 });
root.Items.Add(new myLongChild { myLongText = 25 });
root.Items.Add(new myLongChild { myLongText = 27 });
byte[] buffer;
using (var stream = new MemoryStream())
{
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, dict, session))
{
var dcs = new DataContractSerializer(typeof(myLongRoot));
dcs.WriteObject(writer, root);
}
buffer = stream.ToArray();
}
XmlBinaryReaderSession readerSession;
InitializeReader(out dict, out readerSession);
using (var stream = new MemoryStream(buffer, false))
{
using (var reader = XmlDictionaryReader.CreateBinaryReader(stream, dict, new XmlDictionaryReaderQuotas(), readerSession))
{
var dcs = new DataContractSerializer(typeof(myLongRoot));
var rootCopy = dcs.ReadObject(reader);
}
}
}

Related

How to customize dictionary serialization with DataContractSerializer?

Dictionary serialization with DataContractSerializer generate below data. How to replace d2p1:KeyValueOfintint, d2p1:Key and d2p1:Value with our own attributes / tags / identifier.
Serializing Dictionary in [CashCounter],
Output Generate after Serialization given below
<CashCounter xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/DictionarySerlization">
<BankNote xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:KeyValueOfintint>
<d2p1:Key>10</d2p1:Key>
<d2p1:Value>6</d2p1:Value>
</d2p1:KeyValueOfintint>
<d2p1:KeyValueOfintint>
<d2p1:Key>5</d2p1:Key>
<d2p1:Value>10</d2p1:Value>
</d2p1:KeyValueOfintint>
</BankNote>
<TotalCount>16</TotalCount>
<TotalSum>110</TotalSum>
</CashCounter>
static void Main(string[] args) {
CashCounter cashCounter = addCashCounter();
string serilizedData = GetXml(cashCounter);
}
private static CashCounter addCashCounter() {
CashCounter cashCounter = CreateCounter();
for(var i = 0; i < 6; i++) { cashCounter = incrementCountAmount(cashCounter, 10); }
for(var i = 0; i < 10; i++) { cashCounter = incrementCountAmount(cashCounter, 5); }
return cashCounter;
}
private static CashCounter CreateCounter()
{
var cashCounter = new CashCounter
{
BanknotesCount = new Dictionary<int, int>(),
TotalSum = 0,
TotalCount = 0
};
return cashCounter;
}
private static CashCounter incrementCountAmount(CashCounter cashCounter, int amount){
const int count = 1;
cashCounter.TotalCount += count;
cashCounter.TotalSum += amount * count;
if (cashCounter.BanknotesCount.ContainsKey(amount))
{
cashCounter.BanknotesCount[amount] += count;
}
else
{
cashCounter.BanknotesCount.Add(amount, count);
}
return cashCounter;
}
public static string GetXml<T>(T obj, DataContractSerializer serializer)
{
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings {OmitXmlDeclaration = true,Indent = true, IndentChars = " " };
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
{
serializer.WriteObject(xmlWriter, obj);
}
return textWriter.ToString();
}
}
public static string GetXml<T>(T obj)
{
var serializer = new DataContractSerializer(typeof(T));
return GetXml(obj, serializer);
}
You can control the item, key and value element names of a dictionary when serialized to XML by subclassing Dictionary<TKey, TValue>, applying CollectionDataContractAttribute, and setting the following attribute values:
ItemName: gets or sets a custom name for a dictionary key/value pair element.
KeyName: gets or sets a custom name for a dictionary key name element.
ValueName: gets or sets a custom name for a dictionary value element.
Namespace: if needed, gets or sets a namespace for the data contract.
Name: if needed, gets or sets the data contract name for the dictionary type. This becomes the XML root element name when the dictionary is serialized as the root object.
(Since the dictionary is not the root object in your data model, setting this particular property is not needed in this case.)
Thus, if you define your CashCounter data model as follows (simplified to remove irrelevant members):
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/DictionarySerlization")]
public class CashCounter
{
[DataMember]
public BankNoteDictionary BankNote { get; set; }
}
[CollectionDataContract(ItemName = "MyItemName", KeyName = "MyKeyName", ValueName = "MyValueName",
Namespace = "http://schemas.datacontract.org/2004/07/DictionarySerlization")]
public class BankNoteDictionary : Dictionary<int, int>
{
}
The resulting XML will look like:
<CashCounter xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/DictionarySerlization">
<BankNote>
<MyItemName>
<MyKeyName>10</MyKeyName>
<MyValueName>6</MyValueName>
</MyItemName>
<MyItemName>
<MyKeyName>5</MyKeyName>
<MyValueName>10</MyValueName>
</MyItemName>
</BankNote>
</CashCounter>
Notes:
You should consider replacing the namespace with something permanent.
The namespace is part of the data contract name. Currently it has some default value related to your c# namespace, so if you refactor your code and put classes into different c# namespaces, the data contract namespace might change. Since the data contract namespace is actually published in your WSDL that could be a nuisance for your customers. You may also want to use the namespace for branding purposes by including your organization's URL at the beginning.
For further reading see What does adding Name and Namespace to DataContract do? and What are XML namespaces for?.
For documentation see Collection Types in Data Contracts: Customizing Dictionary Collections.

Change XML opener(reader) to JSON

I have followed a good tutorial that shows how to make an Automation Framework in C# Selenium.
The config file is in XML at the moment, but I wanted some more practice and change it to a .json file.
At the moment we are using the namespace System.Xml.XPath; and my question is, are there a similar for JSON? Lets say "System.Json;" that works the same as my XML reader. So I don't need to refactor to much code, or is it unavoidably?
This is how it works at the moment.
//Initialize
ConfigReader.SetFrameworkSettings();
public class ConfigReader
{
public static void SetFrameworkSettings()
{
XPathItem aut;
string strFilename = Environment.CurrentDirectory.ToString() + "\\Config\\GlobalConfig.xml";
FileStream stream = new FileStream(strFilename, FileMode.Open);
XPathDocument document = new XPathDocument(stream);
XPathNavigator navigator = document.CreateNavigator();
//Get XML Details and pass it in XPathItem type variables
aut = navigator.SelectSingleNode("AutoFramework/RunSettings/AUT");
Settings.AUT = aut.Value.ToString();
}
}
public class Settings
{
public static string AUT { get; set; }
}
Would be awesome if you could just change this two lines
XPathDocument document = new XPathDocument(stream);
XPathNavigator navigator = document.CreateNavigator()
And the XpathItem
Cheers
I would recommend using Newtonsoft.Json which is available from Nuget.
To "reuse" your current code you would have to make some basic SettingsConverter first:
public static class SettingsConverter
{
public static string FromXmlToJson(string xml)
{
Settings settings = null;
// read your xml file and assign values to the settings object
// now you can "serialize" settings object into Json
return JsonSerialization.Serialize(settings);
}
public static string FromJsonToXml(string json)
{
Settings settings = JsonSerialization.Deserialize<MeSettings>(json);
// save settings using your "xml" serialization
return xmlString; // return xml string
}
}
To use these methods I'm using this JsonSerialization helper class :
public static class JsonSerialiation
{
public static string Serialize<T>(T obj)
{
using (MemoryStream stream = new MemoryStream())
{
using (JsonTextWriter writer = new JsonTextWriter(new StreamWriter(stream))
{
JsonSerializer serializer = new JsonSerializer();
serializer.Serializer(writer, obj);
writer.Flush();
stream.Position = 0;
using (StreamReader reader = new StreamREader(stream))
{
return reader.ReadToEnd();
}
}
}
}
public static T Deserialize<T>(string jsonString)
{
using (JsonTextReader reader = new JsonTextReader(new StringReader(str)))
{
JsonSerializer serializer = new JsonSerializer();
return serializer.Deserialize<T>(reader)
}
}
}
Above example requires from you to change your Settings class from static :
public class Settings
{
public static string AUT { get; set; }
}
To instance :
public class Settings
{
public string AUT { get; set; }
}
If you prefer to keep it as static. You should use JObject from Newtonsoft.Json library :
JObject obj = JObject.Parse(jsonString);
Settings.AUT = obj.SelectToken("AUT").Value<string>();
You can always use JsonConvert.Serialize<T>() and JsonConvert.Deserialize<T>() methods instead of the JsonSerialization helper class that I've made but in my opinion the less control you have over your code the bigger the problems will be.

XML Serializing and Deserializing List<string> in c#

i have an object of following class
[XmlRoot("http://schemas.abc.com")]
[DataContract]
public class Employee
{
[XmlAttribute]
[DataMember]
public List<string> Positions { get; set; }
}
Positions contains following two strings "Project Manager" and "Senior Project Manager"
I serialized this object through following method and saved in DB
public static string Serialize(Employee entity)
{
var memoryStream = new MemoryStream();
var serializer = new XmlSerializer(typeof(Employee));
using (var xmlTextWriter = new XmlTextWriter(memoryStream,
Encoding.Unicode))
{
serializer.Serialize(xmlTextWriter, entity);
xmlTextWriter.Close();
}
return UnicodeByteArrayToString(memoryStream.ToArray());
}
private static string UnicodeByteArrayToString(byte[] input)
{
var constructedString = Encoding.Unicode.GetString(input);
return constructedString;
}
Then i am retrieving and deserializing the object through following method
public static Employee Deserialize(string str)
{
var data = StringToUnicodeByteArray(str);
var memoryStream = new MemoryStream(data);
var serializer = new XmlSerializer(typeof(Employee));
var xmlTextReader = new XmlTextReader(memoryStream);
return (Employee)serializer.Deserialize(xmlTextReader);
}
private static byte[] StringToUnicodeByteArray(string input)
{
var byteArray = Encoding.Unicode.GetBytes(input);
return byteArray;
}
Now i am getting 5 objects of strings in position list
"Project", "Manager", "Senior", "Project", "Manager"
deserialization is creating new string object after every space
Quick help will be much appreciated
Answer from Eugene Podskal
think that you should remove the XmlAttribute attribute. XmlSerializer doesn't know how to parse values in attribute string other than by splitting it on whitespace. Or you can create a property that will give you String that allows to unambiguously separate individual items in it (like with commas or whatever), and that can parse individual items from the String it is set to. This propery will be serializable, while actual List will be XmlIgnore

Serialize large amount of objects in C# on the fly rather than all at once?

I have created a couple of classes mean to represent a relational data structure ( parent child structures ). Below is an example of XML representation so far giving you an idea of what I mean
<BillingFile>
<Account>
<acctnum>122344231414</acctnum>
<adjustments>34.44</adjustments>
<Charges>
<lineitem>
<chargetype>PENALTY</chargetype>
<amount>40.50</amount>
<ratecode>E101</ratecode>
</lineitem>
<lineitem>
<chargetype>LATE CHARGE</chargetype>
<amount>445.35</amount>
<ratecode>D101</ratecode>
</lineitem>
</Charges>
</Account>
</BillingFile>
What I'm doing with my application is parsing through a large text file which could have upwards of 50,000+ accounts in it. Each time an account is read, I will create an "Account" object that has the parent objects, etc. The end goal is to be able to create an XML file containing all this account info that is serialized from the objects created.
The problem I see with this, is that if I store all these objects in memory it will cause a performance issue as it runs in those 50k+ record files.
What I'm wondering is, is there a way to sequentially serialize an object in C#, rather than all at once?
I've done some googling and it seems that the built in serialization methods of .NET are a one and done kind of deal. Is there a better way I can do this?
I'd rather avoid having to do any intermediate steps like storing the data in a database, since it's easier to modify code than it is to mess with a bunch of tables and JOIN statements.
Thoughts?
XmlSerializer.Deserialize takes an XmlReader parameter. You could place the XmlReader just at the <Account> tag, and call the XmlSerializer there.
public IEnumerable<Account> ReadAccounts(TextReader source)
{
var ser = new XmlSerializer(typeof(Account));
using (var reader = XmlReader.Create(source))
{
if (!reader.IsStartElement("BillingFile"))
{
yield break;
}
reader.Read();
while (reader.MoveToContent() == XmlNodeType.Element)
{
yield return (Account) ser.Deserialize(reader);
}
}
}
Similarly for serialization
public void WriteAccounts(IEnumerable<Account> data, TextWriter target)
{
// Use XmlSerializerNamespaces to supress xmlns:xsi and xmlns:xsd
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("", "");
var ser = new XmlSerializer(typeof(Account));
using (var writer = XmlWriter.Create(target))
{
writer.WriteStartElement("BillingFile");
foreach (var acct in data)
{
ser.Serialize(writer, acct, namespaces);
writer.Flush();
}
writer.WriteEndElement();
}
}
You could also create a BillingFile class that implements IXmlSerializable, and put this functionality there.
Or if you prefer a push-based model instead:
public class AccountWriter : IDisposable
{
private XmlWriter _writer;
private XmlSerializer _ser;
private XmlSerializerNamespaces _namespaces;
private bool _wroteHeader = false;
private bool _disposed = false;
public bool IsDisposed { get { return _disposed; } }
public AccountWriter(TextWriter target)
{
_namespaces = new XmlSerializerNamespaces();
_namespaces.Add("", "");
_ser = new XmlSerializer(typeof(Account));
_writer = XmlWriter.Create(target);
}
public void Write(Account acct)
{
if (_disposed) throw new ObjectDisposedException("AccountWriter");
if (!_wroteHeader)
{
_writer.WriteStartElement("BillingFile");
_wroteHeader = true;
}
_ser.Serialize(_writer, acct, _namespaces);
}
public void Flush()
{
if (_disposed) throw new ObjectDisposedException("AccountWriter");
_writer.Flush();
}
public void Dispose()
{
if (!_disposed)
{
if (_wroteHeader)
{
_writer.WriteEndElement();
_wroteHeader = true;
}
_writer.Dispose();
_disposed = true;
}
}
}
using (var writer = new AccountWriter(Console.Out))
{
foreach (var acct in accounts)
{
writer.Write(acct);
}
}
The problem I see with this, is that if I store all these objects in memory it will cause a performance issue as it runs in those 50k+ record files.
Test that first. 50k * 1kB is still only 50 MB.
Don't solve problems you don't have.
You can create your own Account objects that would take an XElement and read the data from that node, example:
public class Account
{
XElement self;
public Account(XElement account)
{
if(null == account)
self = new XElement("Account");
else
self = account;
}
public int Number
{
get { return self.Get("acctnum", 0); }
set { self.Set("acctnum", value, false); }
}
public Charges Charges { get { return new Charges(self.GetElement("Charges")); } }
}
I'm using these extensions to get the information that handles empty nodes / default values like above, 0 being the default int value for the Number get. And the GetElement() creates a new Charges node if it doesn't exist.
You will need to create your enumerable Charges class & LineItem classes, but you only create what you need as needed.
You can populate an account with an XPath lookup like:
Account account = new Account(
root.XPathSelectElement("Account[acctnum='"+ someAccount + "']"));
XPath is found with using System.Xml.XPath.

Serialization and Deserialization into an XML file, C#

I have the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication28
{
class Program
{
static void Main()
{
List<string> dirs = FileHelper.GetFilesRecursive(#"c:\Documents and Settings\bob.smith\Desktop\Test");
foreach (string p in dirs)
{
Console.WriteLine(p);
}
//Write Count
Console.WriteLine("Count: {0}", dirs.Count);
Console.Read();
}
static class FileHelper
{
public static List<string> GetFilesRecursive(string b)
{
// 1.
// Store results in the file results list.
List<string> result = new List<string>();
// 2.
// Store a stack of our directories.
Stack<string> stack = new Stack<string>();
// 3.
// Add initial directory.
stack.Push(b);
// 4.
// Continue while there are directories to process
while (stack.Count > 0)
{
// A.
// Get top directory
string dir = stack.Pop();
try
{
// B
// Add all files at this directory to the result List.
result.AddRange(Directory.GetFiles(dir, "*.*"));
// C
// Add all directories at this directory.
foreach (string dn in Directory.GetDirectories(dir))
{
stack.Push(dn);
}
}
catch
{
// D
// Could not open the directory
}
}
return result;
}
}
}
}
The code above works well for recursively finding what files/directories lie in a folder on my c:.
I am trying to serialize the results of what this code does to an XML file but I am not sure how to do this.
My project is this: find all files/ directories w/in a drive, serialize into an XML file. Then, the second time i run this app, i will have two XML files to compare. I then want to deserialize the XML file from the first time i ran this app and compare differences to the current XML file and produce a report of changes (i.e. files that have been added, deleted, updated).
I was hoping to get some help as I am a beginner in C# and i am very very shaky on serializing and deserializing. I'm having lots of trouble coding. Can someone help me?
Thanks
Your result is List<string> and that is not directly serializable. You'll have to wrap it, a minimal approach:
[Serializable]
class Filelist: List<string> { }
And then the (De)Serialization goes like:
Filelist data = new Filelist(); // replaces List<string>
// fill it
using (var stream = File.Create(#".\data.xml"))
{
var formatter = new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
formatter.Serialize(stream, data);
}
data = null; // lose it
using (var stream = File.OpenRead(#".\data.xml"))
{
var formatter = new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
data = (Filelist) formatter.Deserialize(stream);
}
But note that you will not be comparing the XML in any way (not practical). You will compare (deserialzed) List instances. And the XML is SOAP formatted, take a look at it. It may not be very useful in another context.
And therefore you could easily use a different Formatter (binary is a bit more efficient and flexible).
Or maybe you just want to persist the List of files as XML. That is a different question.
For anyone who is having trouble with xml serialization and de-serialization. I have created a sample class to do this below. It works for recursive collections also (like files and directories).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Sample
{
[Serializable()]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false, ElementName = "rootnode")]
public partial class RootNode
{
[System.Xml.Serialization.XmlElementAttribute("collection1")]
public List<OuterCollection> OuterCollections { get; set; }
}
[Serializable()]
public partial class OuterCollection
{
[XmlAttribute("attribute1")]
public string attribute1 { get; set; }
[XmlArray(ElementName = "innercollection1")]
[XmlArrayItem("text", Type = typeof(InnerCollection1))]
public List<InnerCollection1> innerCollection1Stuff { get; set; }
[XmlArray("innercollection2")]
[XmlArrayItem("text", typeof(InnerCollection2))]
public List<InnerCollection2> innerConnection2Stuff { get; set; }
}
[Serializable()]
public partial class InnerCollection2
{
[XmlText()]
public string text { get; set; }
}
public partial class InnerCollection1
{
[XmlText()]
public int number { get; set; }
}
}
This class serializes and deserializes itself....hopefully this helps.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
namespace TestStuff
{
public class Configuration
{
#region properties
public List<string> UIComponents { get; set; }
public List<string> Settings { get; set; }
#endregion
//serialize itself
public string Serialize()
{
MemoryStream memoryStream = new MemoryStream();
XmlSerializer xs = new XmlSerializer(typeof(Configuration));
using (StreamWriter xmlTextWriter = new StreamWriter(memoryStream))
{
xs.Serialize(xmlTextWriter, this);
xmlTextWriter.Flush();
//xmlTextWriter.Close();
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
memoryStream.Seek(0, SeekOrigin.Begin);
StreamReader reader = new StreamReader(memoryStream);
return reader.ReadToEnd();
}
}
//deserialize into itself
public void Deserialize(string xmlString)
{
String XmlizedString = null;
using (MemoryStream memoryStream = new MemoryStream())
{
using (StreamWriter w = new StreamWriter(memoryStream))
{
w.Write(xmlString);
w.Flush();
XmlSerializer xs = new XmlSerializer(typeof(Configuration));
memoryStream.Seek(0, SeekOrigin.Begin);
XmlReader reader = XmlReader.Create(memoryStream);
Configuration currentConfig = (Configuration)xs.Deserialize(reader);
this.Settings = currentConfig.Settings;
this.UIComponents = currentConfig.UIComponents;
w.Close();
}
}
}
static void Main(string[] args)
{
Configuration thisConfig = new Configuration();
thisConfig.Settings = new List<string>(){
"config1", "config2"
};
thisConfig.UIComponents = new List<string>(){
"comp1", "comp2"
};
//serializing the object
string serializedString = thisConfig.Serialize();
Configuration myConfig = new Configuration();
//deserialize into myConfig object
myConfig.Deserialize(serializedString);
}
}
}
John:
May I suggest an improvement? Instead of using filenames, use the FileInfo object. This will allow you to get much more accurate information about each file rather than just if it exists under the same name.
Also, the XmlSerializer class should do you just fine. It won't serialize generic lists, so you'll have to output your List<> to an array or some such, but other than that:
XmlSerializer serial = new XmlSerializer(typeof(FileInfo[]));
StringWriter writer = new StringWriter();
FileInfo[] fileInfoArray = GetFileInfos();
serial.Serialize(writer, fileInfoArrays);
Simple and easy, unless it matters to you how the serialized XML looks.
Whatever you do, lose the empty catch block. You WILL regret swallowing exceptions. Log them or re-throw them.
Help #1. Indent code by four spaces to have it be seen as code when you post here.
2: get rid of that try/catch block, as it will eat all exceptions, including the ones you want to know about.
3: Did you make any attempt at all to serialize your results? Please edit your question to show what you tried. Hint: use the XmlSerializer class.
for xml serialisation, you have several possibilities:
Doing it manually
Using XmlSerializer as detailed above
Using System.Xml.Serialization
Using Linq to Xml
for the last two, see a code example in this answer. (See some gotchas here)
And for your recursive directory visitor, you could consider making it really recursive: here's some interesting code examples.
This question is exactly like this one. I also have a posted answer which will work for you as well:
How to serialize?

Categories