How would I go about making this entire class Serializable?

What code would I add to a second constructor that would restore state/values from the XML file (de-serialize)?

using System;
using System.Collections.Generic;
using System.Text;

namespace tgreer
{
	public class indexer
	{
		private Dictionary<string, Int32> _documents;
		private Dictionary<string, Int32> _pages;
		
		public indexer()
		{
			// class constructor
			_documents = new Dictionary<string, Int32>(250);
			_pages = new Dictionary<string, Int32>(1000);
		}

		public void addDocIndex(string _seq, Int32 _offset)
		{
			_documents.Add(_seq, _offset);
			
		}

		public void addPageIndex(string _seq, Int32 _offset)
		{
			_pages.Add(_seq, _offset);

		}
	}
}

There are just two private dictionaries. The application which uses this class library will add values to the dictionaries by calling the public methods. I need a way to persist any values of a particular instance of this class/object, and to potentially restore them if the proper constructor is called.

Recommended Answers

All 7 Replies

Here's my first attempt, for any following along:

using System;
using System.IO;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;

namespace tgreer
{
	[XmlRoot("indexer")]
	public class indexer
	{
		[XmlElement("documents")]
		private Dictionary<string, Int32> _documents;

		[XmlElement("pages")]
		private Dictionary<string, Int32> _pages;
		
		public indexer()
		{
			// class constructor
			_documents = new Dictionary<string, Int32>(250);
			_pages = new Dictionary<string, Int32>(1000);
		}
		public void addDocIndex(string _seq, Int32 _offset)
		{
			_documents.Add(_seq, _offset);
			
		}

		public void addPageIndex(string _seq, Int32 _offset)
		{
			_pages.Add(_seq, _offset);

		}

		public void serializeMe()
		{
			// Serialization
			XmlSerializer s = new XmlSerializer(this.GetType());
			TextWriter w = new StreamWriter(@"c:\index.xml");
			s.Serialize(w, this);
			w.Close();
		}
	}
}

It works, insofar as it creates an XML file. However, the dictionaries are not contained within it. Obviously I was hoping that two elements would be created, with child elements automatically generated for the key-value pairs within each dictionary. That'd be nice, I guess, but too much to hope for.

So I'll have to beef up the SerializeMe method to iterate through each dictionary, create XML elements/attributes within the loop.

the dictionaries are not serialized as they are private. Serializers only work on public properties/fields and even read onlys have problem. In laymans terms for an object to deserialize it basically creates a new object and writes the values back to it. For an object to do this to another object then the fields/properties have to be public. If you have a read only field then you have to be able to fill it in the constructor.

Hope it helps
One messy way around this is create public properties but limit the changes of values in the property set method, but as i said it gets messy

I ended up using standard serialization instead of xml serialization. Also, I used a List instead of Dictionaries... I made a class for the items I wanted to store in the list, then serialized the List object.

Yeah i have been down a similar path (though i still used dictionaries but subclassed them) and used serialization and .net remoting instead of web services just to get round some issues. Hopefully in the future it will be solved as i dont see the point of not being able to fully serialize something without exposing all your private properties.

The main problem I had with Dictionaries is there is no .IndexOf property. I not only had to retrieve a value from the dictionary via a key, but the "next" value as well.

List has an .IndexOf property. The problem is, in a List of Objects, you have to pass in the full object to get a match. The "Find" method is nice, but it took awhile to figure out the Predictor mechanism.

Yeah. I usually make use of foreaching through the keyvaluepair. But that is why i subclassed the generic dictionary so i could do what you wanted. I basically took the keys and values collection and copied them to an array and indexed it that way. I also wrote an append method to add from another list/dictionary into that one by passing it in. It got messy but i needed the key value pair more than anything so the list was out

But you can put anything in a List, including a complete Object. So, you can make a Class that exposes the properties you need (like a key and a value). Mark the class [Serializable]. Then, make a List of that <class> type.

Heck, I'll just show you the whole thing. Two classes, Document and Pages. The idea is that another program parses a large PostScript file, finding the start of each document and each page. Later, I need to recreate this index in order to randomly retrieve any document or page. So, this class has two contstructors, the original which is used to create the index information, and another to de-serialize the previous values:

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace TGREER
{
	[Serializable]
	public class Document
	{
		private string _seq;
		private Int32 _offset;

		public Document(string _seq, Int32 _offset)
		{
			this._seq = _seq;
			this._offset = _offset;
		}

		public override string ToString()
		{
			return _seq;
		}

		public string GenericSequenceNo
		{
			get { return _seq; }
			set { _seq = value; }
		}

		public Int32 ByteOffset
		{
			get { return _offset; }
			set { _offset = value; }
		}
	}

	[Serializable]
	public class Page
	{
		private string _seq;
		private Int32 _offset;

		public Page(string _seq, Int32 _offset)
		{
			this._seq = _seq;
			this._offset = _offset;
		}

		public override string ToString()
		{
			return _seq;
		}

		public string GenericSequenceNo
		{
			get { return _seq; }
			set { _seq = value; }
		}

		public Int32 ByteOffset
		{
			get { return _offset; }
			set { _offset = value; }
		}
	}

	public class Indexer
	{
		private string _psFilename;
		private string _key;

		private List<Document> _documents;
		private List<Page> _pages;

		private Document _document;
		private Page _page;

		// constructor, creates empty lists
		public Indexer(string _filename)
		{
			_psFilename = _filename;
			_documents = new List<Document>();
			_pages = new List<Page>();
		}

		// constructor, re-creates lists from serialized files
		public Indexer(string _filename, string _docName, string _pgName)
		{
			_psFilename = _filename;

			FileStream _s = new FileStream(_docName, FileMode.Open);
			BinaryFormatter formatter = new BinaryFormatter();
			_documents = (List<Document>)formatter.Deserialize(_s);
			_s.Close();

			_s = new FileStream(_pgName, FileMode.Open);
			formatter = new BinaryFormatter();
			_pages = (List<Page>)formatter.Deserialize(_s);
			_s.Close();
		}

		// public method to add entry to documents list
		public void addDocIndex(string _seq, Int32 _offset)
		{
			_document = new Document(_seq, _offset);
			_documents.Add(_document);
		}

		// public method to add entry to pages list
		public void addPageIndex(string _seq, Int32 _offset)
		{
			_page = new Page(_seq, _offset);
			_pages.Add(_page);
		}

		// public method to serialize list contents to named files
		public void serialize(string _docName, string _pgName)
		{
			FileStream _s = new FileStream(_docName, FileMode.Create);
			BinaryFormatter formatter = new BinaryFormatter();
			formatter.Serialize(_s, _documents);
			_s.Close();

			_s = new FileStream(_pgName, FileMode.Create);
			formatter = new BinaryFormatter();
			formatter.Serialize(_s, _pages);
			_s.Close();
		}

		// public method to return a string containing a document
		public string getDocument(string _key)
		{
			this._key = _key;
			_document = _documents.Find(isKey);

			Int32 _i = _documents.IndexOf(_document);
			Int32 _byteStart = _document.ByteOffset;
			Int32 _byteEnd = _documents[_i + 1].ByteOffset - 1;
			Int32 _bytesToRead = _byteEnd - _byteStart;

			FileStream _s = new FileStream(_psFilename, FileMode.Open);
			StreamReader _sr = new StreamReader(_s);
			_s.Seek(_byteStart, SeekOrigin.Begin);

			char[] _buffer = new char[_bytesToRead];
			Int32 _bytesRead = _sr.ReadBlock(_buffer, 0, _bytesToRead);
			_s.Close();

			return new string(_buffer);
		}

		// public method to return a string containing a page
		public string getPage(string _key)
		{
			this._key = _key;
			_page = _pages.Find(isKey);

			Int32 _i = _pages.IndexOf(_page);
			Int32 _byteStart = _page.ByteOffset;
			Int32 _byteEnd = _pages[_i + 1].ByteOffset - 1;
			Int32 _bytesToRead = _byteEnd - _byteStart;

			FileStream _s = new FileStream(_psFilename, FileMode.Open);
			StreamReader _sr = new StreamReader(_s);
			_s.Seek(_byteStart, SeekOrigin.Begin);

			char[] _buffer = new char[_bytesToRead];
			Int32 _bytesRead = _sr.ReadBlock(_buffer, 0, _bytesToRead);
			_s.Close();

			return new string(_buffer);

		}

		// Predicate used for searching the List. Presumably, this is optimized so is faster than a forall loop?
		private bool isKey(Document _d)
		{
			if (_d.GenericSequenceNo == this._key)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		private bool isKey(Page _p)
		{
			if (_p.GenericSequenceNo == this._key)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
	}
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.