1 /**
2  * Copyright: Copyright (c) 2010-2011 Jacob Carlborg.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Jan 26, 2010
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module mambo.serialization.archives.XmlArchive;
8 
9 import std.conv;
10 
11 import mambo.core._;
12 import mambo.serialization.archives._;
13 import mambo.serialization.Serializer;
14 import mambo.util._;
15 import mambo.xml.XmlDocument;
16 
17 private alias ConvException ConversionException;
18 
19 private enum ArchiveMode
20 {
21 	archiving,
22 	unarchiving
23 }
24 
25 /**
26  * This class is a concrete implementation of the Archive interface. This archive
27  * uses XML as the final format for the serialized data.
28  */
29 final class XmlArchive (U = char) : ArchiveBase!(U)
30 {
31 	private alias Archive.Id Id;
32 
33 	private struct Tags
34 	{
35 		static const Data structTag = "struct";
36 		static const Data dataTag = "data";
37 		static const Data archiveTag = "archive";
38 		static const Data arrayTag = "array";
39 		static const Data objectTag = "object";
40 		static const Data baseTag = "base";
41 		static const Data stringTag = "string";
42 		static const Data referenceTag = "reference";
43 		static const Data pointerTag = "pointer";
44 		static const Data associativeArrayTag = "associativeArray";
45 		static const Data typedefTag = "typedef";
46 		static const Data nullTag = "null";
47 		static const Data enumTag = "enum";
48 		static const Data sliceTag = "slice";
49 		static const Data elementTag = "element";
50 		static const Data keyTag = "key";
51 		static const Data valueTag = "value";
52 	}
53 
54 	private struct Attributes
55 	{
56 		static const Data invalidAttribute = "\0";
57 		static const Data typeAttribute = "type";
58 		static const Data versionAttribute = "version";
59 		static const Data lengthAttribute = "length";
60 		static const Data keyAttribute = "key";
61 		static const Data runtimeTypeAttribute = "runtimeType";
62 		static const Data idAttribute = "id";
63 		static const Data keyTypeAttribute = "keyType";
64 		static const Data valueTypeAttribute = "valueType";
65 		static const Data offsetAttribute = "offset";
66 		static const Data baseTypeAttribute = "baseType";
67 	}
68 
69 	private struct Node
70 	{
71 		XmlDocument!(U).Node parent;
72 		XmlDocument!(U).Node node;
73 		Id id;
74 		string key;
75 	}
76 
77 	private
78 	{
79 		Data archiveType = "org.dsource.orange.xml";
80 		Data archiveVersion = "1.0.0";
81 
82 		XmlDocument!(U) doc;
83 		doc.Node lastElement;
84 
85 		bool hasBegunArchiving;
86 		bool hasBegunUnarchiving;
87 
88 		Node[Id] archivedArrays;
89 		Node[Id] archivedPointers;
90 		void[][Data] unarchivedSlices;
91 	}
92 
93 	/**
94 	 * Creates a new instance of this class with the give error callback.
95 	 *
96 	 * Params:
97 	 *     errorCallback = The callback to be called when an error occurs
98 	 */
99 	this (ErrorCallback errorCallback = null)
100 	{
101 		super(errorCallback);
102 		doc = new XmlDocument!(U);
103 	}
104 
105 	/// Starts the archiving process. Call this method before archiving any values.
106 	public void beginArchiving ()
107 	{
108 		if (!hasBegunArchiving)
109 		{
110 			doc.header;
111 			lastElement = doc.tree.element(null, Tags.archiveTag)
112 				.attribute(null, Attributes.typeAttribute, archiveType)
113 				.attribute(null, Attributes.versionAttribute, archiveVersion);
114 			lastElement = lastElement.element(null, Tags.dataTag);
115 
116 			hasBegunArchiving = true;
117 		}
118 	}
119 
120 	/**
121 	 * Begins the unarchiving process. Call this method before unarchiving any values.
122 	 *
123 	 * Params:
124 	 *     untypedData = the data to unarchive
125 	 */
126 	public void beginUnarchiving (UntypedData untypedData)
127 	{
128 		auto data = cast(Data) untypedData;
129 
130 		if (!hasBegunUnarchiving)
131 		{
132 			doc.parse(data);
133 			hasBegunUnarchiving = true;
134 
135 			auto set = doc.query[Tags.archiveTag][Tags.dataTag];
136 
137 			if (set.nodes.length == 1)
138 				lastElement = set.nodes[0];
139 
140 			else
141 			{
142 				auto dataTag = to!(string)(Tags.dataTag);
143 
144 				if (set.nodes.length == 0)
145 					error(errorMessage!(ArchiveMode.unarchiving) ~ `The "` ~ to!(string)(Tags.dataTag) ~ `" tag could not be found.`, __FILE__, __LINE__, [dataTag]);
146 
147 				else
148 					error(errorMessage!(ArchiveMode.unarchiving) ~ `There were more than one "` ~ to!(string)(Tags.dataTag) ~ `" tag.`, __FILE__, __LINE__, [dataTag]);
149 			}
150 		}
151 	}
152 
153 	/// Returns the data stored in the archive in an untyped form.
154 	UntypedData untypedData ()
155 	{
156 		return doc.toString();
157 	}
158 
159 	/// Returns the data stored in the archive in an typed form.
160 	Data data ()
161 	{
162 		return doc.toString;
163 	}
164 
165 	/**
166 	 * Resets the archive. This resets the archive in a state making it ready to start
167 	 * a new archiving process.
168 	 */
169 	void reset ()
170 	{
171 		hasBegunArchiving = false;
172 		hasBegunUnarchiving = false;
173 		doc.reset;
174 	}
175 
176 	/**
177 	 * Archives an array.
178 	 *
179 	 * Examples:
180 	 * ---
181 	 * int[] arr = [1, 2, 3];
182 	 *
183 	 * auto archive = new XmlArchive!();
184 	 *
185 	 * auto a = Array(arr.ptr, arr.length, typeof(a[0]).sizeof);
186 	 *
187 	 * archive.archive(a, typeof(a[0]).string, "arr", 0, {
188 	 * 	// archive the individual elements
189 	 * });
190 	 * ---
191 	 *
192 	 * Params:
193 	 *     array = the array to archive
194 	 *     type = the runtime type of an element of the array
195 	 *     key = the key associated with the array
196 	 *     id = the id associated with the array
197 	 *     dg = a callback that performs the archiving of the individual elements
198 	 */
199 	void archiveArray (Array array, string type, string key, Id id, void delegate () dg)
200 	{
201 		restore(lastElement) in {
202 			internalArchiveArray(array, type, key, id, Tags.arrayTag);
203 			dg();
204 		};
205 	}
206 
207 	private void internalArchiveArray(Array array, string type, string key, Id id, Data tag, Data content = null)
208 	{
209 		auto parent = lastElement;
210 
211 		if (array.length == 0)
212 			lastElement = lastElement.element(null, tag);
213 
214 		else
215 			lastElement = doc.createNode(tag, content);
216 
217 		lastElement.attribute(null, Attributes.typeAttribute, toData(type))
218 		.attribute(null, Attributes.lengthAttribute, toData(array.length))
219 		.attribute(null, Attributes.keyAttribute, toData(key))
220 		.attribute(null, Attributes.idAttribute, toData(id));
221 
222 		addArchivedArray(id, parent, lastElement, key);
223 	}
224 
225 	/**
226 	 * Archives an associative array.
227 	 *
228 	 * Examples:
229 	 * ---
230 	 * int[string] arr = ["a"[] : 1, "b" : 2, "c" : 3];
231 	 *
232 	 * auto archive = new XmlArchive!();
233 	 *
234 	 * archive.archive(string.stringof, int.stringof, arr.length, "arr", 0, {
235 	 * 	// archive the individual keys and values
236 	 * });
237 	 * ---
238 	 *
239 	 *
240 	 * Params:
241 	 *     keyType = the runtime type of the keys
242 	 *     valueType = the runtime type of the values
243 	 *     length = the length of the associative array
244 	 *     key = the key associated with the associative array
245 	 *     id = the id associated with the associative array
246 	 *     dg = a callback that performs the archiving of the individual keys and values
247 	 *
248 	 * See_Also: archiveAssociativeArrayValue
249 	 * See_Also: archiveAssociativeArrayKey
250 	 */
251 	void archiveAssociativeArray (string keyType, string valueType, size_t length, string key, Id id, void delegate () dg)
252 	{
253 		restore(lastElement) in {
254 			lastElement = lastElement.element(null, Tags.associativeArrayTag)
255 			.attribute(null, Attributes.keyTypeAttribute, toData(keyType))
256 			.attribute(null, Attributes.valueTypeAttribute, toData(valueType))
257 			.attribute(null, Attributes.lengthAttribute, toData(length))
258 			.attribute(null, Attributes.keyAttribute, key)
259 			.attribute(null, Attributes.idAttribute, toData(id));
260 
261 			dg();
262 		};
263 	}
264 
265 	/**
266 	 * Archives an associative array key.
267 	 *
268 	 * There are separate methods for archiving associative array keys and values
269 	 * because both the key and the value can be of arbitrary type and needs to be
270 	 * archived on its own.
271 	 *
272 	 * Examples:
273 	 * ---
274 	 * int[string] arr = ["a"[] : 1, "b" : 2, "c" : 3];
275 	 *
276 	 * auto archive = new XmlArchive!();
277 	 *
278 	 * foreach(k, v ; arr)
279 	 * {
280 	 * 	archive.archiveAssociativeArrayKey(to!(string)(i), {
281 	 * 		// archive the key
282 	 * 	});
283 	 * }
284 	 * ---
285 	 *
286 	 * The foreach statement in the above example would most likely be executed in the
287 	 * callback passed to the archiveAssociativeArray method.
288 	 *
289 	 * Params:
290 	 *     key = the key associated with the key
291 	 *     dg = a callback that performs the actual archiving of the key
292 	 *
293 	 * See_Also: archiveAssociativeArray
294 	 * See_Also: archiveAssociativeArrayValue
295 	 */
296 	void archiveAssociativeArrayKey (string key, void delegate () dg)
297 	{
298 		internalArchiveAAKeyValue(key, Tags.keyTag, dg);
299 	}
300 
301 	/**
302 	 * Archives an associative array value.
303 	 *
304 	 * There are separate methods for archiving associative array keys and values
305 	 * because both the key and the value can be of arbitrary type and needs to be
306 	 * archived on its own.
307 	 *
308 	 * Examples:
309 	 * ---
310 	 * int[string] arr = ["a"[] : 1, "b" : 2, "c" : 3];
311 	 *
312 	 * auto archive = new XmlArchive!();
313 	 * size_t i;
314 	 *
315 	 * foreach(k, v ; arr)
316 	 * {
317 	 * 	archive.archiveAssociativeArrayValue(to!(string)(i), {
318 	 * 		// archive the value
319 	 * 	});
320 	 *
321 	 * 	i++;
322 	 * }
323 	 * ---
324 	 *
325 	 * The foreach statement in the above example would most likely be executed in the
326 	 * callback passed to the archiveAssociativeArray method.
327 	 *
328 	 * Params:
329 	 *     key = the key associated with the value
330 	 *     dg = a callback that performs the actual archiving of the value
331 	 *
332 	 * See_Also: archiveAssociativeArray
333 	 * See_Also: archiveAssociativeArrayKey
334 	 */
335 	void archiveAssociativeArrayValue (string key, void delegate () dg)
336 	{
337 		internalArchiveAAKeyValue(key, Tags.valueTag, dg);
338 	}
339 
340 	private void internalArchiveAAKeyValue (string key, Data tag, void delegate () dg)
341 	{
342 		restore(lastElement) in {
343 			lastElement = lastElement.element(null, tag)
344 			.attribute(null, Attributes.keyAttribute, toData(key));
345 
346 			dg();
347 		};
348 	}
349 
350 	/**
351 	 * Archives the given value.
352 	 *
353 	 * Example:
354 	 * ---
355 	 * enum Foo : bool
356 	 * {
357 	 * 	bar
358 	 * }
359 	 *
360 	 * auto foo = Foo.bar;
361 	 * auto archive = new XmlArchive!();
362 	 * archive.archive(foo, "bool", "foo", 0);
363 	 * ---
364 	 *
365 	 * Params:
366 	 *     value = the value to archive
367 	 *     baseType = the base type of the enum
368 	 *     key = the key associated with the value
369 	 *     id = the id associated with the value
370 	 */
371 	void archiveEnum (bool value, string type, string key, Id id)
372 	{
373 		internalArchiveEnum(value, type, key, id);
374 	}
375 
376 	/// Ditto
377 	void archiveEnum (byte value, string type, string key, Id id)
378 	{
379 		internalArchiveEnum(value, type, key, id);
380 	}
381 
382 	/// Ditto
383 	void archiveEnum (char value, string type, string key, Id id)
384 	{
385 		internalArchiveEnum(value, type, key, id);
386 	}
387 
388 	/// Ditto
389 	void archiveEnum (dchar value, string type, string key, Id id)
390 	{
391 		internalArchiveEnum(value, type, key, id);
392 	}
393 
394 	/// Ditto
395 	void archiveEnum (int value, string type, string key, Id id)
396 	{
397 		internalArchiveEnum(value, type, key, id);
398 	}
399 
400 	/// Ditto
401 	void archiveEnum (long value, string type, string key, Id id)
402 	{
403 		internalArchiveEnum(value, type, key, id);
404 	}
405 
406 	/// Ditto
407 	void archiveEnum (short value, string type, string key, Id id)
408 	{
409 		internalArchiveEnum(value, type, key, id);
410 	}
411 
412 	/// Ditto
413 	void archiveEnum (ubyte value, string type, string key, Id id)
414 	{
415 		internalArchiveEnum(value, type, key, id);
416 	}
417 
418 	/// Ditto
419 	void archiveEnum (uint value, string type, string key, Id id)
420 	{
421 		internalArchiveEnum(value, type, key, id);
422 	}
423 
424 	/// Ditto
425 	void archiveEnum (ulong value, string type, string key, Id id)
426 	{
427 		internalArchiveEnum(value, type, key, id);
428 	}
429 
430 	/// Ditto
431 	void archiveEnum (ushort value, string type, string key, Id id)
432 	{
433 		internalArchiveEnum(value, type, key, id);
434 	}
435 
436 	/// Ditto
437 	void archiveEnum (wchar value, string type, string key, Id id)
438 	{
439 		internalArchiveEnum(value, type, key, id);
440 	}
441 
442 	private void internalArchiveEnum (T) (T value, string type, string key, Id id)
443 	{
444 		lastElement.element(null, Tags.enumTag, toData(value))
445 		.attribute(null, Attributes.typeAttribute, toData(type))
446 		.attribute(null, Attributes.baseTypeAttribute, toData(T.stringof))
447 		.attribute(null, Attributes.keyAttribute, toData(key))
448 		.attribute(null, Attributes.idAttribute, toData(id));
449 	}
450 
451 	/**
452 	 * Archives a base class.
453 	 *
454 	 * This method is used to indicate that the all following calls to archive a value
455 	 * should be part of the base class. This method is usually called within the
456 	 * callback passed to archiveObject. The archiveObject method can the mark the end
457 	 * of the class.
458 	 *
459 	 * Examples:
460 	 * ---
461 	 * class ArchiveBase {}
462 	 * class Foo : ArchiveBase {}
463 	 *
464 	 * auto archive = new XmlArchive!();
465 	 * archive.archiveBaseClass("ArchiveBase", "base", 0);
466 	 * ---
467 	 *
468 	 * Params:
469 	 *     type = the type of the base class to archive
470 	 *     key = the key associated with the base class
471 	 *     id = the id associated with the base class
472 	 */
473 	void archiveBaseClass (string type, string key, Id id)
474 	{
475 		lastElement = lastElement.element(null, Tags.baseTag)
476 		.attribute(null, Attributes.typeAttribute, toData(type))
477 		.attribute(null, Attributes.keyAttribute, toData(key))
478 		.attribute(null, Attributes.idAttribute, toData(id));
479 	}
480 
481 	/**
482 	 * Archives a null pointer or reference.
483 	 *
484 	 * Examples:
485 	 * ---
486 	 * int* ptr;
487 	 *
488 	 * auto archive = new XmlArchive!();
489 	 * archive.archiveNull(typeof(ptr).stringof, "ptr");
490 	 * ---
491 	 *
492 	 * Params:
493 	 *     type = the runtime type of the pointer or reference to archive
494 	 *     key = the key associated with the null pointer
495 	 */
496 	void archiveNull (string type, string key)
497 	{
498 		lastElement.element(null, Tags.nullTag)
499 		.attribute(null, Attributes.typeAttribute, toData(type))
500 		.attribute(null, Attributes.keyAttribute, toData(key));
501 	}
502 
503 	/**
504 	 * Archives an object, either a class or an interface.
505 	 *
506 	 * Examples:
507 	 * ---
508 	 * class Foo
509 	 * {
510 	 * 	int a;
511 	 * }
512 	 *
513 	 * auto foo = new Foo;
514 	 *
515 	 * auto archive = new XmlArchive!();
516 	 * archive.archiveObject(Foo.classinfo.name, "Foo", "foo", 0, {
517 	 * 	// archive the fields of Foo
518 	 * });
519 	 * ---
520 	 *
521 	 * Params:
522 	 *     runtimeType = the runtime type of the object
523 	 *     type = the static type of the object
524 	 *     key = the key associated with the object
525 	 *     id = the id associated with the object
526 	 *     dg = a callback that performs the archiving of the individual fields
527 	 */
528 	void archiveObject (string runtimeType, string type, string key, Id id, void delegate () dg)
529 	{
530 		restore(lastElement) in {
531 			lastElement = lastElement.element(null, Tags.objectTag)
532 			.attribute(null, Attributes.runtimeTypeAttribute, toData(runtimeType))
533 			.attribute(null, Attributes.typeAttribute, toData(type))
534 			.attribute(null, Attributes.keyAttribute, toData(key))
535 			.attribute(null, Attributes.idAttribute, toData(id));
536 
537 			dg();
538 		};
539 	}
540 
541 	/**
542 	 * Archives a pointer.
543 	 *
544 	 * If a pointer points to a value that is serialized as well, the pointer should be
545 	 * archived as a reference. Otherwise the value that the pointer points to should be
546 	 * serialized as a regular value.
547 	 *
548 	 * Examples:
549 	 * ---
550 	 * class Foo
551 	 * {
552 	 * 	int a;
553 	 * 	int* b;
554 	 * }
555 	 *
556 	 * auto foo = new Foo;
557 	 * foo.a = 3;
558 	 * foo.b = &foo.a;
559 	 *
560 	 * archive = new XmlArchive!();
561 	 * archive.archivePointer("b", 0, {
562 	 * 	// archive "foo.b" as a reference
563 	 * });
564 	 * ---
565 	 *
566 	 * ---
567 	 * int a = 3;
568 	 *
569 	 * class Foo
570 	 * {
571 	 * 	int* b;
572 	 * }
573 	 *
574 	 * auto foo = new Foo;
575 	 * foo.b = &a;
576 	 *
577 	 * archive = new XmlArchive!();
578 	 * archive.archivePointer("b", 0, {
579 	 * 	// archive "foo.b" as a regular value
580 	 * });
581 	 * ---
582 	 *
583 	 * Params:
584 	 *     key = the key associated with the pointer
585 	 *     id = the id associated with the pointer
586 	 *     dg = a callback that performs the archiving of value pointed to by the pointer
587 	 */
588 	void archivePointer (string key, Id id, void delegate () dg)
589 	{
590 		restore(lastElement) in {
591 			lastElement = lastElement.element(null, Tags.pointerTag)
592 			.attribute(null, Attributes.keyAttribute, toData(key))
593 			.attribute(null, Attributes.idAttribute, toData(id));
594 
595 			dg();
596 		};
597 	}
598 
599 	/**
600 	 * Archives a reference.
601 	 *
602 	 * A reference is reference to another value. For example, if an object is archived
603 	 * more than once, the first time it's archived it will actual archive the object.
604 	 * The second time the object will be archived a reference will be archived instead
605 	 * of the actual object.
606 	 *
607 	 * This method is also used when archiving a pointer that points to a value that has
608 	 * been or will be archived as well.
609 	 *
610 	 * Examples:
611 	 * ---
612 	 * class Foo {}
613 	 *
614 	 * class Bar
615 	 * {
616 	 * 	Foo f;
617 	 * 	Foo f2;
618 	 * }
619 	 *
620 	 * auto bar = new Bar;
621 	 * bar.f = new Foo;
622 	 * bar.f2 = bar.f;
623 	 *
624 	 * auto archive = new XmlArchive!();
625 	 *
626 	 * // when achiving "bar"
627 	 * archive.archiveObject(Foo.classinfo.name, "Foo", "f", 0, {});
628 	 * archive.archiveReference("f2", 0); // archive a reference to "f"
629 	 * ---
630 	 *
631 	 * Params:
632 	 *     key = the key associated with the reference
633 	 *     id = the id of the value this reference refers to
634 	 */
635 	void archiveReference (string key, Id id)
636 	{
637 		lastElement.element(null, Tags.referenceTag, toData(id))
638 		.attribute(null, Attributes.keyAttribute, toData(key));
639 	}
640 
641 	/**
642 	 * Archives a slice.
643 	 *
644 	 * This method should be used when archiving an array that is a slice of an
645 	 * already archived array or an array that has not yet been archived.
646 	 *
647 	 * Examples:
648 	 * ---
649 	 * auto arr = [1, 2, 3, 4];
650 	 * auto slice = arr[1 .. 3];
651 	 *
652 	 * auto archive = new XmlArchive!();
653 	 * // archive "arr" with id 0
654 	 *
655 	 * auto s = Slice(slice.length, 1);
656 	 * archive.archiveSlice(s, 1, 0);
657 	 * ---
658 	 *
659 	 * Params:
660 	 *     slice = the slice to be archived
661 	 *     sliceId = the id associated with the slice
662 	 *     arrayId = the id associated with the array this slice is a slice of
663 	 */
664 	void archiveSlice (Slice slice, Id sliceId, Id arrayId)
665 	{
666 		if (auto sliceNode = getArchivedArray(sliceId))
667 		{
668 			if (auto arrayNode = getArchivedArray(arrayId))
669 			{
670 				sliceNode.parent.element(null, Tags.sliceTag, toData(arrayNode.id))
671 				.attribute(null, Attributes.keyAttribute, toData(sliceNode.key))
672 				.attribute(null, Attributes.offsetAttribute, toData(slice.offset))
673 				.attribute(null, Attributes.lengthAttribute, toData(slice.length));
674 			}
675 		}
676 	}
677 
678 	/**
679 	 * Archives a struct.
680 	 *
681 	 * Examples:
682 	 * ---
683 	 * struct Foo
684 	 * {
685 	 * 	int a;
686 	 * }
687 	 *
688 	 * auto foo = Foo(3);
689 	 *
690 	 * auto archive = new XmlArchive!();
691 	 * archive.archiveStruct(Foo.stringof, "foo", 0, {
692 	 * 	// archive the fields of Foo
693 	 * });
694 	 * ---
695 	 *
696 	 * Params:
697 	 *     type = the type of the struct
698 	 *     key = the key associated with the struct
699 	 *     id = the id associated with the struct
700 	 *     dg = a callback that performs the archiving of the individual fields
701 	 */
702 	void archiveStruct (string type, string key, Id id, void delegate () dg)
703 	{
704 		restore(lastElement) in {
705 			lastElement = lastElement.element(null, Tags.structTag)
706 			.attribute(null, Attributes.typeAttribute, toData(type))
707 			.attribute(null, Attributes.keyAttribute, toData(key))
708 			.attribute(null, Attributes.idAttribute, toData(id));
709 
710 			dg();
711 		};
712 	}
713 
714 	/**
715 	 * Archives a typedef.
716 	 *
717 	 * Examples:
718 	 * ---
719 	 * typedef int Foo;
720 	 * Foo a = 3;
721 	 *
722 	 * auto archive = new XmlArchive!();
723 	 * archive.archiveTypedef(Foo.stringof, "a", 0, {
724 	 * 	// archive "a" as the base type of Foo, i.e. int
725 	 * });
726 	 * ---
727 	 *
728 	 * Params:
729 	 *     type = the type of the typedef
730 	 *     key = the key associated with the typedef
731 	 *     id = the id associated with the typedef
732 	 *     dg = a callback that performs the archiving of the value as the base
733 	 *     		type of the typedef
734 	 */
735 	void archiveTypedef (string type, string key, Id id, void delegate () dg)
736 	{
737 		restore(lastElement) in {
738 			lastElement = lastElement.element(null, Tags.typedefTag)
739 			.attribute(null, Attributes.typeAttribute, toData(type))
740 			.attribute(null, Attributes.keyAttribute, toData(key))
741 			.attribute(null, Attributes.idAttribute, toData(id));
742 
743 			dg();
744 		};
745 	}
746 
747 	/**
748 	 * Archives the given value.
749 	 *
750 	 * Params:
751 	 *     value = the value to archive
752 	 *     key = the key associated with the value
753 	 *     id = the id associated wit the value
754 	 */
755 	void archive (string value, string key, Id id)
756 	{
757 		archiveString(value, key, id);
758 	}
759 
760 	/// Ditto
761 	void archive (wstring value, string key, Id id)
762 	{
763 		archiveString(value, key, id);
764 	}
765 
766 	/// Ditto
767 	void archive (dstring value, string key, Id id)
768 	{
769 		archiveString(value, key, id);
770 	}
771 
772 	private void archiveString (T) (T value, string key, Id id)
773 	{
774 		restore(lastElement) in {
775 			alias ElementTypeOfArray!(T) ElementType;
776 			auto array = Array(value.ptr, value.length, ElementType.sizeof);
777 
778 			internalArchiveArray(array, ElementType.stringof, key, id, Tags.stringTag, toData(value));
779 		};
780 	}
781 
782 	/// Ditto
783 	void archive (bool value, string key, Id id)
784 	{
785 		archivePrimitive(value, key, id);
786 	}
787 
788 	/// Ditto
789 	void archive (byte value, string key, Id id)
790 	{
791 		archivePrimitive(value, key, id);
792 	}
793 
794 	//currently not suppported by to!()
795 	/*void archive (cdouble value, string key, Id id)
796 	{
797 		archivePrimitive(value, key, id);
798 	}*/
799 
800 	//currently not implemented but a reserved keyword
801 	/*void archive (cent value, string key, Id id)
802 	{
803 		archivePrimitive(value, key, id);
804 	}*/
805 
806 	//currently not suppported by to!()
807 	/*void archive (cfloat value, string key, Id id)
808 	{
809 		archivePrimitive(value, key, id);
810 	}*/
811 
812 	/// Ditto
813 	void archive (char value, string key, Id id)
814 	{
815 		archivePrimitive(value, key, id);
816 	}
817 
818 	//currently not suppported by to!()
819 	/*void archive (creal value, string key, Id id)
820 	{
821 		archivePrimitive(value, key, id);
822 	}*/
823 
824 	/// Ditto
825 	void archive (dchar value, string key, Id id)
826 	{
827 		archivePrimitive(value, key, id);
828 	}
829 
830 	/// Ditto
831 	void archive (double value, string key, Id id)
832 	{
833 		archivePrimitive(value, key, id);
834 	}
835 
836 	/// Ditto
837 	void archive (float value, string key, Id id)
838 	{
839 		archivePrimitive(value, key, id);
840 	}
841 
842 	//currently not suppported by to!()
843 	/*void archive (idouble value, string key, Id id)
844 	{
845 		archivePrimitive(value, key, id);
846 	}*/
847 
848 	//currently not suppported by to!()
849 	/*void archive (ifloat value, string key, Id id)
850 	{
851 		archivePrimitive(value, key, id);
852 	}*/
853 
854 	/// Ditto
855 	void archive (int value, string key, Id id)
856 	{
857 		archivePrimitive(value, key, id);
858 	}
859 
860 	//currently not suppported by to!()
861 	/*void archive (ireal value, string key, Id id)
862 	{
863 		archivePrimitive(value, key, id);
864 	}*/
865 
866 	/// Ditto
867 	void archive (long value, string key, Id id)
868 	{
869 		archivePrimitive(value, key, id);
870 	}
871 
872 	/// Ditto
873 	void archive (real value, string key, Id id)
874 	{
875 		archivePrimitive(value, key, id);
876 	}
877 
878 	/// Ditto
879 	void archive (short value, string key, Id id)
880 	{
881 		archivePrimitive(value, key, id);
882 	}
883 
884 	/// Ditto
885 	void archive (ubyte value, string key, Id id)
886 	{
887 		archivePrimitive(value, key, id);
888 	}
889 
890 	//currently not implemented but a reserved keyword
891 	/*void archive (ucent value, string key, Id id)
892 	{
893 		archivePrimitive(value, key, id);
894 	}*/
895 
896 	/// Ditto
897 	void archive (uint value, string key, Id id)
898 	{
899 		archivePrimitive(value, key, id);
900 	}
901 
902 	/// Ditto
903 	void archive (ulong value, string key, Id id)
904 	{
905 		archivePrimitive(value, key, id);
906 	}
907 
908 	/// Ditto
909 	void archive (ushort value, string key, Id id)
910 	{
911 		archivePrimitive(value, key, id);
912 	}
913 
914 	/// Ditto
915 	void archive (wchar value, string key, Id id)
916 	{
917 		archivePrimitive(value, key, id);
918 	}
919 
920 	private void archivePrimitive (T) (T value, string key, Id id)
921 	{
922 		lastElement.element(null, toData(T.stringof), toData(value))
923 		.attribute(null, Attributes.keyAttribute, toData(key))
924 		.attribute(null, Attributes.idAttribute, toData(id));
925 	}
926 
927 	/**
928 	 * Unarchives the value associated with the given key as an array.
929 	 *
930 	 * Examples:
931 	 * ---
932 	 * auto archive = new XmlArchive!();
933 	 * archive.beginUnarchiving(data);
934 	 * auto id = archive.unarchiveArray("arr", (size_t length) {
935 	 * 	auto arr = new int[length]; // pre-allocate the array
936 	 * 	// unarchive the individual elements of "arr"
937 	 * });
938 	 * ---
939 	 *
940 	 * Params:
941 	 *     key = the key associated with the array
942 	 *     dg = a callback that performs the unarchiving of the individual elements.
943 	 *     		$(I length) is the length of the archived array
944 	 *
945 	 * Returns: the id associated with the array
946 	 *
947 	 * See_Also: unarchiveArray
948 	 */
949 	Id unarchiveArray (string key, void delegate (size_t) dg)
950 	{
951 		return restore!(Id)(lastElement) in {
952 			auto element = getElement(Tags.arrayTag, key);
953 
954 			if (!element)
955 				return Id.max;
956 
957 			lastElement = element;
958 			auto len = getValueOfAttribute(Attributes.lengthAttribute);
959 
960 			if (!len)
961 				return Id.max;
962 
963 			auto length = fromData!(size_t)(len);
964 			auto id = getValueOfAttribute(Attributes.idAttribute);
965 
966 			if (!id)
967 				return Id.max;
968 
969 			dg(length);
970 
971 			return toId(id);
972 		};
973 	}
974 
975 	/**
976 	 * Unarchives the value associated with the given id as an array.
977 	 *
978 	 * Examples:
979 	 * ---
980 	 * auto archive = new XmlArchive!();
981 	 * archive.beginUnarchiving(data);
982 	 * archive.unarchiveArray(0, (size_t length) {
983 	 * 	auto arr = new int[length]; // pre-allocate the array
984 	 * 	// unarchive the individual elements of "arr"
985 	 * });
986 	 * ---
987 	 *
988 	 * Params:
989 	 *     id = the id associated with the value
990 	 *     dg = a callback that performs the unarchiving of the individual elements.
991 	 *     		$(I length) is the length of the archived array
992 	 *
993 	 * See_Also: unarchiveArray
994 	 */
995 	void unarchiveArray (Id id, void delegate (size_t) dg)
996 	{
997 		restore(lastElement) in {
998 			auto element = getElement(Tags.arrayTag, to!(string)(id), Attributes.idAttribute);
999 
1000 			if (!element)
1001 				return;
1002 
1003 			lastElement = element;
1004 			auto len = getValueOfAttribute(Attributes.lengthAttribute);
1005 
1006 			if (!len)
1007 				return;
1008 
1009 			auto length = fromData!(size_t)(len);
1010 			auto stringId = getValueOfAttribute(Attributes.idAttribute);
1011 
1012 			if (!stringId)
1013 				return;
1014 
1015 			dg(length);
1016 		};
1017 	}
1018 
1019 	/**
1020 	 * Unarchives the value associated with the given id as an associative array.
1021 	 *
1022 	 * Examples:
1023 	 * ---
1024 	 * auto archive = new XmlArchive!();
1025 	 * archive.beginUnarchiving(data);
1026 	 *
1027 	 * auto id = archive.unarchiveAssociativeArray("aa", (size_t length) {
1028 	 * 	// unarchive the individual keys and values
1029 	 * });
1030 	 * ---
1031 	 *
1032 	 * Params:
1033 	 *     key = the key associated with the associative array
1034 	 *     dg = a callback that performs the unarchiving of the individual keys and values.
1035 	 *     		$(I length) is the length of the archived associative array
1036 	 *
1037 	 * Returns: the id associated with the associative array
1038 	 *
1039 	 * See_Also: unarchiveAssociativeArrayKey
1040 	 * See_Also: unarchiveAssociativeArrayValue
1041 	 */
1042 	Id unarchiveAssociativeArray (string key, void delegate (size_t length) dg)
1043 	{
1044 		return restore!(Id)(lastElement) in {
1045 			auto element = getElement(Tags.associativeArrayTag, key);
1046 
1047 			if (!element)
1048 				return Id.max;
1049 
1050 			lastElement = element;
1051 			auto len = getValueOfAttribute(Attributes.lengthAttribute);
1052 
1053 			if (!len)
1054 				return Id.max;
1055 
1056 			auto length = fromData!(size_t)(len);
1057 			auto id = getValueOfAttribute(Attributes.idAttribute);
1058 
1059 			if (!id)
1060 				return Id.max;
1061 
1062 			dg(length);
1063 
1064 			return toId(id);
1065 		};
1066 	}
1067 
1068 	/**
1069 	 * Unarchives an associative array key.
1070 	 *
1071 	 * There are separate methods for unarchiving associative array keys and values
1072 	 * because both the key and the value can be of arbitrary type and needs to be
1073 	 * unarchived on its own.
1074 	 *
1075 	 * Examples:
1076 	 * ---
1077 	 * auto archive = new XmlArchive!();
1078 	 * archive.beginUnarchiving(data);
1079 	 *
1080 	 * for (size_t i = 0; i < length; i++)
1081 	 * {
1082 	 * 	unarchiveAssociativeArrayKey(to!(string(i), {
1083 	 * 		// unarchive the key
1084 	 * 	});
1085 	 * }
1086 	 * ---
1087 	 *
1088 	 * The for statement in the above example would most likely be executed in the
1089 	 * callback passed to the unarchiveAssociativeArray method.
1090 	 *
1091 	 * Params:
1092 	 *     key = the key associated with the key
1093 	 *     dg = a callback that performs the actual unarchiving of the key
1094 	 *
1095 	 * See_Also: unarchiveAssociativeArrayValue
1096 	 * See_Also: unarchiveAssociativeArray
1097 	 */
1098 	void unarchiveAssociativeArrayKey (string key, void delegate () dg)
1099 	{
1100 		internalUnarchiveAAKeyValue(key, Tags.keyTag, dg);
1101 	}
1102 
1103 	/**
1104 	 * Unarchives an associative array value.
1105 	 *
1106 	 * There are separate methods for unarchiving associative array keys and values
1107 	 * because both the key and the value can be of arbitrary type and needs to be
1108 	 * unarchived on its own.
1109 	 *
1110 	 * Examples:
1111 	 * ---
1112 	 * auto archive = new XmlArchive!();
1113 	 * archive.beginUnarchiving(data);
1114 	 *
1115 	 * for (size_t i = 0; i < length; i++)
1116 	 * {
1117 	 * 	unarchiveAssociativeArrayValue(to!(string(i), {
1118 	 * 		// unarchive the value
1119 	 * 	});
1120 	 * }
1121 	 * ---
1122 	 *
1123 	 * The for statement in the above example would most likely be executed in the
1124 	 * callback passed to the unarchiveAssociativeArray method.
1125 	 *
1126 	 * Params:
1127 	 *     key = the key associated with the value
1128 	 *     dg = a callback that performs the actual unarchiving of the value
1129 	 *
1130 	 * See_Also: unarchiveAssociativeArrayKey
1131 	 * See_Also: unarchiveAssociativeArray
1132 	 */
1133 	void unarchiveAssociativeArrayValue (string key, void delegate () dg)
1134 	{
1135 		internalUnarchiveAAKeyValue(key, Tags.valueTag, dg);
1136 	}
1137 
1138 	private void internalUnarchiveAAKeyValue (string key, Data tag, void delegate () dg)
1139 	{
1140 		restore(lastElement) in {
1141 			auto element = getElement(tag, key);
1142 
1143 			if (!element)
1144 				return;
1145 
1146 			lastElement = element;
1147 
1148 			dg();
1149 		};
1150 	}
1151 
1152 	/**
1153 	 * Unarchives the value associated with the given key as a bool.
1154 	 *
1155 	 * This method is used when the unarchiving a enum value with the base type bool.
1156 	 *
1157 	 * Params:
1158 	 *     key = the key associated with the value
1159 	 *
1160 	 * Returns: the unarchived value
1161 	 */
1162 	bool unarchiveEnumBool (string key)
1163 	{
1164 		return unarchiveEnum!(bool)(key);
1165 	}
1166 
1167 	/// Ditto
1168 	byte unarchiveEnumByte (string key)
1169 	{
1170 		return unarchiveEnum!(byte)(key);
1171 	}
1172 
1173 	/// Ditto
1174 	char unarchiveEnumChar (string key)
1175 	{
1176 		return unarchiveEnum!(char)(key);
1177 	}
1178 
1179 	/// Ditto
1180 	dchar unarchiveEnumDchar (string key)
1181 	{
1182 		return unarchiveEnum!(dchar)(key);
1183 	}
1184 
1185 	/// Ditto
1186 	int unarchiveEnumInt (string key)
1187 	{
1188 		return unarchiveEnum!(int)(key);
1189 	}
1190 
1191 	/// Ditto
1192 	long unarchiveEnumLong (string key)
1193 	{
1194 		return unarchiveEnum!(long)(key);
1195 	}
1196 
1197 	/// Ditto
1198 	short unarchiveEnumShort (string key)
1199 	{
1200 		return unarchiveEnum!(short)(key);
1201 	}
1202 
1203 	/// Ditto
1204 	ubyte unarchiveEnumUbyte (string key)
1205 	{
1206 		return unarchiveEnum!(ubyte)(key);
1207 	}
1208 
1209 	/// Ditto
1210 	uint unarchiveEnumUint (string key)
1211 	{
1212 		return unarchiveEnum!(uint)(key);
1213 	}
1214 
1215 	/// Ditto
1216 	ulong unarchiveEnumUlong (string key)
1217 	{
1218 		return unarchiveEnum!(ulong)(key);
1219 	}
1220 
1221 	/// Ditto
1222 	ushort unarchiveEnumUshort (string key)
1223 	{
1224 		return unarchiveEnum!(ushort)(key);
1225 	}
1226 
1227 	/// Ditto
1228 	wchar unarchiveEnumWchar (string key)
1229 	{
1230 		return unarchiveEnum!(wchar)(key);
1231 	}
1232 
1233 	/**
1234 	 * Unarchives the value associated with the given id as a bool.
1235 	 *
1236 	 * This method is used when the unarchiving a enum value with the base type bool.
1237 	 *
1238 	 * Params:
1239 	 *     id = the id associated with the value
1240 	 *
1241 	 * Returns: the unarchived value
1242 	 */
1243 	bool unarchiveEnumBool (Id id)
1244 	{
1245 		return unarchiveEnum!(bool)(id);
1246 	}
1247 
1248 	/// Ditto
1249 	byte unarchiveEnumByte (Id id)
1250 	{
1251 		return unarchiveEnum!(byte)(id);
1252 	}
1253 
1254 	/// Ditto
1255 	char unarchiveEnumChar (Id id)
1256 	{
1257 		return unarchiveEnum!(char)(id);
1258 	}
1259 
1260 	/// Ditto
1261 	dchar unarchiveEnumDchar (Id id)
1262 	{
1263 		return unarchiveEnum!(dchar)(id);
1264 	}
1265 
1266 	/// Ditto
1267 	int unarchiveEnumInt (Id id)
1268 	{
1269 		return unarchiveEnum!(int)(id);
1270 	}
1271 
1272 	/// Ditto
1273 	long unarchiveEnumLong (Id id)
1274 	{
1275 		return unarchiveEnum!(long)(id);
1276 	}
1277 
1278 	/// Ditto
1279 	short unarchiveEnumShort (Id id)
1280 	{
1281 		return unarchiveEnum!(short)(id);
1282 	}
1283 
1284 	/// Ditto
1285 	ubyte unarchiveEnumUbyte (Id id)
1286 	{
1287 		return unarchiveEnum!(ubyte)(id);
1288 	}
1289 
1290 	/// Ditto
1291 	uint unarchiveEnumUint (Id id)
1292 	{
1293 		return unarchiveEnum!(uint)(id);
1294 	}
1295 
1296 	/// Ditto
1297 	ulong unarchiveEnumUlong (Id id)
1298 	{
1299 		return unarchiveEnum!(ulong)(id);
1300 	}
1301 
1302 	/// Ditto
1303 	ushort unarchiveEnumUshort (Id id)
1304 	{
1305 		return unarchiveEnum!(ushort)(id);
1306 	}
1307 
1308 	/// Ditto
1309 	wchar unarchiveEnumWchar (Id id)
1310 	{
1311 		return unarchiveEnum!(wchar)(id);
1312 	}
1313 
1314 	private T unarchiveEnum (T, U) (U keyOrId)
1315 	{
1316 		auto tag = Tags.enumTag;
1317 
1318 		static if (isString!(U))
1319 			auto element = getElement(Tags.enumTag, keyOrId);
1320 
1321 		else static if (is(U == Id))
1322 			auto element = getElement(tag, toData(keyOrId), Attributes.idAttribute);
1323 
1324 		else
1325 			static assert (false, format!(`Invalid type "`, U, `". Valid types are "string" and "Id"`));
1326 
1327 		if (!element)
1328 			return T.init;
1329 
1330 		return fromData!(T)(element.value);
1331 	}
1332 
1333 	/**
1334 	 * Unarchives the base class associated with the given key.
1335 	 *
1336 	 * This method is used to indicate that the all following calls to unarchive a
1337 	 * value should be part of the base class. This method is usually called within the
1338 	 * callback passed to unarchiveObject. The unarchiveObject method can the mark the
1339 	 * end of the class.
1340 	 *
1341 	 * Examples:
1342 	 * ---
1343 	 * auto archive = new XmlArchive!();
1344 	 * archive.beginUnarchiving(data);
1345 	 * archive.unarchiveBaseClass("base");
1346 	 * ---
1347 	 *
1348 	 * Params:
1349 	 *     key = the key associated with the base class.
1350 	 *
1351 	 * See_Also: unarchiveObject
1352 	 */
1353 	void unarchiveBaseClass (string key)
1354 	{
1355 		auto element = getElement(Tags.baseTag, key);
1356 
1357 		if (element)
1358 			lastElement = element;
1359 	}
1360 
1361 	/**
1362 	 * Unarchives the object associated with the given key.
1363 	 *
1364 	 * Examples:
1365 	 * ---
1366 	 * class Foo
1367 	 * {
1368 	 * 	int a;
1369 	 * }
1370 	 *
1371 	 * auto archive = new XmlArchive!();
1372 	 * archive.beginUnarchiving(data);
1373 	 *
1374 	 * Id id;
1375 	 * Object o;
1376 	 *
1377 	 * archive.unarchiveObject("foo", id, o, {
1378 	 * 	// unarchive the fields of Foo
1379 	 * });
1380 	 *
1381 	 * auto foo = cast(Foo) o;
1382 	 * ---
1383 	 *
1384 	 * Params:
1385 	 *     key = the key associated with the object
1386 	 *     id = the id associated with the object
1387 	 *     result = the unarchived object
1388 	 *     dg = a callback the performs the unarchiving of the individual fields
1389 	 */
1390 	void unarchiveObject (string key, out Id id, out Object result, void delegate () dg)
1391 	{
1392 		restore(lastElement) in {
1393 			auto tmp = getElement(Tags.objectTag, key, Attributes.keyAttribute, false);
1394 
1395 			if (!tmp)
1396 			{
1397 				lastElement = getElement(Tags.nullTag, key);
1398 				return;
1399 			}
1400 
1401 			lastElement = tmp;
1402 
1403 			auto runtimeType = getValueOfAttribute(Attributes.runtimeTypeAttribute);
1404 
1405 			if (!runtimeType)
1406 				return;
1407 
1408 			auto name = fromData!(string)(runtimeType);
1409 			auto stringId = getValueOfAttribute(Attributes.idAttribute);
1410 
1411 			if (!stringId)
1412 				return;
1413 
1414 			id = toId(stringId);
1415 			result = newInstance(name);
1416 			dg();
1417 		};
1418 	}
1419 
1420 	/**
1421 	 * Unarchives the pointer associated with the given key.
1422 	 *
1423 	 * Examples:
1424 	 * ---
1425 	 * auto archive = new XmlArchive!();
1426 	 * archive.beginUnarchiving(data);
1427 	 * auto id = unarchivePointer("ptr", {
1428 	 * 	// unarchive the value pointed to by the pointer
1429 	 * });
1430 	 * ---
1431 	 *
1432 	 * Params:
1433 	 *     key = the key associated with the pointer
1434 	 *     dg = a callback that performs the unarchiving of value pointed to by the pointer
1435 	 *
1436 	 * Returns: the id associated with the pointer
1437 	 */
1438 	Id unarchivePointer (string key, void delegate () dg)
1439 	{
1440 		return restore!(Id)(lastElement) in {
1441 			auto tmp = getElement(Tags.pointerTag, key, Attributes.keyAttribute, false);
1442 
1443 			if (!tmp)
1444 			{
1445 				lastElement = getElement(Tags.nullTag, key);
1446 				return Id.max;
1447 			}
1448 
1449 			lastElement = tmp;
1450 			auto id = getValueOfAttribute(Attributes.idAttribute);
1451 
1452 			if (!id)
1453 				return Id.max;
1454 
1455 			dg();
1456 
1457 			return toId(id);
1458 		};
1459 	}
1460 
1461 	/**
1462 	 * Unarchives the reference associated with the given key.
1463 	 *
1464 	 * A reference is reference to another value. For example, if an object is archived
1465 	 * more than once, the first time it's archived it will actual archive the object.
1466 	 * The second time the object will be archived a reference will be archived instead
1467 	 * of the actual object.
1468 	 *
1469 	 * This method is also used when unarchiving a pointer that points to a value that has
1470 	 * been or will be unarchived as well.
1471 	 *
1472 	 * Examples:
1473 	 * ---
1474 	 * auto archive = new XmlArchive!();
1475 	 * archive.beginUnarchiving(data);
1476 	 * auto id = unarchiveReference("foo");
1477 	 *
1478 	 * // unarchive the value with the associated id
1479 	 * ---
1480 	 *
1481 	 * Params:
1482 	 *     key = the key associated with the reference
1483 	 *
1484 	 * Returns: the id the reference refers to
1485 	 */
1486 	Id unarchiveReference (string key)
1487 	{
1488 		auto element = getElement(Tags.referenceTag, key, Attributes.keyAttribute, false);
1489 
1490 		if (element)
1491 			return toId(element.value);
1492 
1493 		return Id.max;
1494 	}
1495 
1496 	/**
1497 	 * Unarchives the slice associated with the given key.
1498 	 *
1499 	 * This method should be used when unarchiving an array that is a slice of an
1500 	 * already unarchived array or an array that has not yet been unarchived.
1501 	 *
1502 	 * Examples:
1503 	 * ---
1504 	 * auto archive = new XmlArchive!();
1505 	 * archive.beginUnarchiving(data);
1506 	 * auto slice = unarchiveSlice("slice");
1507 	 *
1508 	 * // slice the original array with the help of the unarchived slice
1509 	 * ---
1510 	 *
1511 	 * Params:
1512 	 *     key = the key associated with the slice
1513 	 *
1514 	 * Returns: the unarchived slice
1515 	 */
1516 	Slice unarchiveSlice (string key)
1517 	{
1518 		auto element = getElement(Tags.sliceTag, key, Attributes.keyAttribute, false);
1519 
1520 		if (element)
1521 		{
1522 			auto length = fromData!(size_t)(getValueOfAttribute(Attributes.lengthAttribute, element));
1523 			auto offset = fromData!(size_t)(getValueOfAttribute(Attributes.offsetAttribute, element));
1524 			auto id = toId(element.value);
1525 
1526 			return Slice(length, offset, id);
1527 		}
1528 
1529 		return Slice.init;
1530 	}
1531 
1532 	/**
1533 	 * Unarchives the struct associated with the given key.
1534 	 *
1535 	 * Examples:
1536 	 * ---
1537 	 * struct Foo
1538 	 * {
1539 	 * 	int a;
1540 	 * }
1541 	 *
1542 	 * auto archive = new XmlArchive!();
1543 	 * archive.beginUnarchiving(data);
1544 	 * archive.unarchiveStruct("foo", {
1545 	 * 	// unarchive the fields of Foo
1546 	 * });
1547 	 * ---
1548 	 *
1549 	 * Params:
1550 	 *     key = the key associated with the struct
1551 	 *     dg = a callback that performs the unarchiving of the individual fields
1552 	 */
1553 	void unarchiveStruct (string key, void delegate () dg)
1554 	{
1555 		restore(lastElement) in {
1556 			auto element = getElement(Tags.structTag, key);
1557 
1558 			if (!element)
1559 				return;
1560 
1561 			lastElement = element;
1562 			dg();
1563 		};
1564 	}
1565 
1566 	/**
1567 	 * Unarchives the struct associated with the given id.
1568 	 *
1569 	 * Examples:
1570 	 * ---
1571 	 * struct Foo
1572 	 * {
1573 	 * 	int a;
1574 	 * }
1575 	 *
1576 	 * auto archive = new XmlArchive!();
1577 	 * archive.beginUnarchiving(data);
1578 	 * archive.unarchiveStruct(0, {
1579 	 * 	// unarchive the fields of Foo
1580 	 * });
1581 	 * ---
1582 	 *
1583 	 * Params:
1584 	 *     id = the id associated with the struct
1585 	 *     dg = a callback that performs the unarchiving of the individual fields.
1586 	 * 	   		The callback will receive the key the struct was archived with.
1587 	 */
1588 	void unarchiveStruct (Id id, void delegate () dg)
1589 	{
1590 		restore(lastElement) in {
1591 			auto element = getElement(Tags.structTag, toData(id), Attributes.idAttribute);
1592 
1593 			if (!element)
1594 				return;
1595 
1596 			lastElement = element;
1597 			dg();
1598 		};
1599 	}
1600 
1601 	private T unarchiveTypeDef (T) (DataType key)
1602 	{
1603 		auto element = getElement(Tags.typedefTag, key);
1604 
1605 		if (element)
1606 			lastElement = element;
1607 
1608 		return T.init;
1609 	}
1610 
1611 	/**
1612 	 * Unarchives the typedef associated with the given key.
1613 	 *
1614 	 * Examples:
1615 	 * ---
1616 	 * typedef int Foo;
1617 	 * Foo foo = 3;
1618 	 *
1619 	 * auto archive = new XmlArchive!();
1620 	 * archive.beginUnarchiving(data);
1621 	 * archive.unarchiveTypedef("foo", {
1622 	 * 	// unarchive "foo" as the base type of Foo, i.e. int
1623 	 * });
1624 	 * ---
1625 	 *
1626 	 * Params:
1627 	 *     key = the key associated with the typedef
1628 	 *     dg = a callback that performs the unarchiving of the value as
1629 	 *     		 the base type of the typedef
1630 	 */
1631 	void unarchiveTypedef (string key, void delegate () dg)
1632 	{
1633 		restore(lastElement) in {
1634 			auto element = getElement(Tags.typedefTag, key);
1635 
1636 			if (!element)
1637 				return;
1638 
1639 			lastElement = element;
1640 			dg();
1641 		};
1642 	}
1643 
1644 	/**
1645 	 * Unarchives the string associated with the given id.
1646 	 *
1647 	 * Examples:
1648 	 * ---
1649 	 * auto archive = new XmlArchive!();
1650 	 * archive.beginUnarchiving(data);
1651 	 * auto str = archive.unarchiveString(0);
1652 	 * ---
1653 	 *
1654 	 * Params:
1655 	 *     id = the id associated with the string
1656 	 *
1657 	 * Returns: the unarchived string
1658 	 */
1659 	string unarchiveString (string key, out Id id)
1660 	{
1661 		return internalUnarchiveString!(string)(key, id);
1662 	}
1663 
1664 	/// Ditto
1665 	wstring unarchiveWstring (string key, out Id id)
1666 	{
1667 		return internalUnarchiveString!(wstring)(key, id);
1668 	}
1669 
1670 	/// Ditto
1671 	dstring unarchiveDstring (string key, out Id id)
1672 	{
1673 		return internalUnarchiveString!(dstring)(key, id);
1674 	}
1675 
1676 	private T internalUnarchiveString (T) (string key, out Id id)
1677 	{
1678 		auto element = getElement(Tags.stringTag, key);
1679 
1680 		if (!element)
1681 			return T.init;
1682 
1683 		auto value = fromData!(T)(element.value);
1684 		auto stringId = getValueOfAttribute(Attributes.idAttribute, element);
1685 
1686 		if (!stringId)
1687 			return T.init;
1688 
1689 		id = toId(stringId);
1690 		return value;
1691 	}
1692 
1693 	/**
1694 	 * Unarchives the string associated with the given key.
1695 	 *
1696 	 * Examples:
1697 	 * ---
1698 	 * auto archive = new XmlArchive!();
1699 	 * archive.beginUnarchiving(data);
1700 	 *
1701 	 * Id id;
1702 	 * auto str = archive.unarchiveString("str", id);
1703 	 * ---
1704 	 *
1705 	 * Params:
1706 	 *     id = the id associated with the string
1707 	 *
1708 	 * Returns: the unarchived string
1709 	 */
1710 	string unarchiveString (Id id)
1711 	{
1712 		return internalUnarchiveString!(string)(id);
1713 	}
1714 
1715 	/// Ditto
1716 	wstring unarchiveWstring (Id id)
1717 	{
1718 		return internalUnarchiveString!(wstring)(id);
1719 	}
1720 
1721 	/// Ditto
1722 	dstring unarchiveDstring (Id id)
1723 	{
1724 		return internalUnarchiveString!(dstring)(id);
1725 	}
1726 
1727 	private T internalUnarchiveString (T) (Id id)
1728 	{
1729 		auto element = getElement(Tags.stringTag, to!(string)(id), Attributes.idAttribute);
1730 
1731 		if (!element)
1732 			return T.init;
1733 
1734 		return fromData!(T)(element.value);
1735 	}
1736 
1737 	/**
1738 	 * Unarchives the value associated with the given key.
1739 	 *
1740 	 * Examples:
1741 	 * ---
1742 	 * auto archive = new XmlArchive!();
1743 	 * archive.beginUnarchiving(data);
1744 	 * auto foo = unarchiveBool("foo");
1745 	 * ---
1746 	 * Params:
1747 	 *     key = the key associated with the value
1748 	 *
1749 	 * Returns: the unarchived value
1750 	 */
1751 	bool unarchiveBool (string key)
1752 	{
1753 		return unarchivePrimitive!(bool)(key);
1754 	}
1755 
1756 	/// Ditto
1757 	byte unarchiveByte (string key)
1758 	{
1759 		return unarchivePrimitive!(byte)(key);
1760 	}
1761 
1762 	//currently not suppported by to!()
1763     /*cdouble unarchiveCdouble (string key)
1764 	{
1765 		return unarchivePrimitive!(cdouble)(key);
1766 	}*/
1767 
1768 	 //currently not implemented but a reserved keyword
1769     /*cent unarchiveCent (string key)
1770 	{
1771 		return unarchivePrimitive!(cent)(key);
1772 	}*/
1773 
1774 	// currently not suppported by to!()
1775     /*cfloat unarchiveCfloat (string key)
1776 	{
1777 		return unarchivePrimitive!(cfloat)(key);
1778 	}*/
1779 
1780 	/// Ditto
1781 	char unarchiveChar (string key)
1782 	{
1783 		return unarchivePrimitive!(char)(key);
1784 	}
1785 
1786 	 //currently not implemented but a reserved keyword
1787 	/*creal unarchiveCreal (string key)
1788 	{
1789 		return unarchivePrimitive!(creal)(key);
1790 	}*/
1791 
1792 	/// Ditto
1793 	dchar unarchiveDchar (string key)
1794 	{
1795 		return unarchivePrimitive!(dchar)(key);
1796 	}
1797 
1798 	/// Ditto
1799 	double unarchiveDouble (string key)
1800 	{
1801 		return unarchivePrimitive!(double)(key);
1802 	}
1803 
1804 	/// Ditto
1805 	float unarchiveFloat (string key)
1806 	{
1807 		return unarchivePrimitive!(float)(key);
1808 	}
1809 
1810 	//currently not suppported by to!()
1811     /*idouble unarchiveIdouble (string key)
1812 	{
1813 		return unarchivePrimitive!(idouble)(key);
1814 	}*/
1815 
1816     // currently not suppported by to!()*/
1817     /*ifloat unarchiveIfloat (string key)
1818 	{
1819 		return unarchivePrimitive!(ifloat)(key);
1820 	}*/
1821 
1822 	/// Ditto
1823 	int unarchiveInt (string key)
1824 	{
1825 		return unarchivePrimitive!(int)(key);
1826 	}
1827 
1828 	// currently not suppported by to!()
1829     /*ireal unarchiveIreal (string key)
1830 	{
1831 		return unarchivePrimitive!(ireal)(key);
1832 	}*/
1833 
1834 	/// Ditto
1835 	long unarchiveLong (string key)
1836 	{
1837 		return unarchivePrimitive!(long)(key);
1838 	}
1839 
1840 	/// Ditto
1841 	real unarchiveReal (string key)
1842 	{
1843 		return unarchivePrimitive!(real)(key);
1844 	}
1845 
1846 	/// Ditto
1847 	short unarchiveShort (string key)
1848 	{
1849 		return unarchivePrimitive!(short)(key);
1850 	}
1851 
1852 	/// Ditto
1853 	ubyte unarchiveUbyte (string key)
1854 	{
1855 		return unarchivePrimitive!(ubyte)(key);
1856 	}
1857 
1858 	// currently not implemented but a reserved keyword
1859     /*ucent unarchiveCcent (string key)
1860 	{
1861 		return unarchivePrimitive!(ucent)(key);
1862 	}*/
1863 
1864 	/// Ditto
1865 	uint unarchiveUint (string key)
1866 	{
1867 		return unarchivePrimitive!(uint)(key);
1868 	}
1869 
1870 	/// Ditto
1871 	ulong unarchiveUlong (string key)
1872 	{
1873 		return unarchivePrimitive!(ulong)(key);
1874 	}
1875 
1876 	/// Ditto
1877 	ushort unarchiveUshort (string key)
1878 	{
1879 		return unarchivePrimitive!(ushort)(key);
1880 	}
1881 
1882 	/// Ditto
1883 	wchar unarchiveWchar (string key)
1884 	{
1885 		return unarchivePrimitive!(wchar)(key);
1886 	}
1887 
1888 	/**
1889 	 * Unarchives the value associated with the given id.
1890 	 *
1891 	 * Examples:
1892 	 * ---
1893 	 * auto archive = new XmlArchive!();
1894 	 * archive.beginUnarchiving(data);
1895 	 * auto foo = unarchiveBool(0);
1896 	 * ---
1897 	 * Params:
1898 	 *     id = the id associated with the value
1899 	 *
1900 	 * Returns: the unarchived value
1901 	 */
1902 	bool unarchiveBool (Id id)
1903 	{
1904 		return unarchivePrimitive!(bool)(id);
1905 	}
1906 
1907 	/// Ditto
1908 	byte unarchiveByte (Id id)
1909 	{
1910 		return unarchivePrimitive!(byte)(id);
1911 	}
1912 
1913 	//currently not suppported by to!()
1914 	/*cdouble unarchiveCdouble (Id id)
1915 	{
1916 		return unarchivePrimitive!(cdouble)(id);
1917 	}*/
1918 
1919 	 //currently not implemented but a reserved keyword
1920 	/*cent unarchiveCent (Id id)
1921 	{
1922 		return unarchivePrimitive!(cent)(id);
1923 	}*/
1924 
1925 	// currently not suppported by to!()
1926 	/*cfloat unarchiveCfloat (Id id)
1927 	{
1928 		return unarchivePrimitive!(cfloat)(id);
1929 	}*/
1930 
1931 	/// Ditto
1932 	char unarchiveChar (Id id)
1933 	{
1934 		return unarchivePrimitive!(char)(id);
1935 	}
1936 
1937 	 //currently not implemented but a reserved keyword
1938 	/*creal unarchiveCreal (Id id)
1939 	{
1940 		return unarchivePrimitive!(creal)(id);
1941 	}*/
1942 
1943 	/// Ditto
1944 	dchar unarchiveDchar (Id id)
1945 	{
1946 		return unarchivePrimitive!(dchar)(id);
1947 	}
1948 
1949 	/// Ditto
1950 	double unarchiveDouble (Id id)
1951 	{
1952 		return unarchivePrimitive!(double)(id);
1953 	}
1954 
1955 	/// Ditto
1956 	float unarchiveFloat (Id id)
1957 	{
1958 		return unarchivePrimitive!(float)(id);
1959 	}
1960 
1961 	//currently not suppported by to!()
1962 	/*idouble unarchiveIdouble (Id id)
1963 	{
1964 		return unarchivePrimitive!(idouble)(id);
1965 	}*/
1966 
1967 	// currently not suppported by to!()*/
1968 	/*ifloat unarchiveIfloat (Id id)
1969 	{
1970 		return unarchivePrimitive!(ifloat)(id);
1971 	}*/
1972 
1973 	/// Ditto
1974 	int unarchiveInt (Id id)
1975 	{
1976 		return unarchivePrimitive!(int)(id);
1977 	}
1978 
1979 	// currently not suppported by to!()
1980 	/*ireal unarchiveIreal (Id id)
1981 	{
1982 		return unarchivePrimitive!(ireal)(id);
1983 	}*/
1984 
1985 	/// Ditto
1986 	long unarchiveLong (Id id)
1987 	{
1988 		return unarchivePrimitive!(long)(id);
1989 	}
1990 
1991 	/// Ditto
1992 	real unarchiveReal (Id id)
1993 	{
1994 		return unarchivePrimitive!(real)(id);
1995 	}
1996 
1997 	/// Ditto
1998 	short unarchiveShort (Id id)
1999 	{
2000 		return unarchivePrimitive!(short)(id);
2001 	}
2002 
2003 	/// Ditto
2004 	ubyte unarchiveUbyte (Id id)
2005 	{
2006 		return unarchivePrimitive!(ubyte)(id);
2007 	}
2008 
2009 	// currently not implemented but a reserved keyword
2010 	/*ucent unarchiveCcent (Id id)
2011 	{
2012 		return unarchivePrimitive!(ucent)(id);
2013 	}*/
2014 
2015 	/// Ditto
2016 	uint unarchiveUint (Id id)
2017 	{
2018 		return unarchivePrimitive!(uint)(id);
2019 	}
2020 
2021 	/// Ditto
2022 	ulong unarchiveUlong (Id id)
2023 	{
2024 		return unarchivePrimitive!(ulong)(id);
2025 	}
2026 
2027 	/// Ditto
2028 	ushort unarchiveUshort (Id id)
2029 	{
2030 		return unarchivePrimitive!(ushort)(id);
2031 	}
2032 
2033 	/// Ditto
2034 	wchar unarchiveWchar (Id id)
2035 	{
2036 		return unarchivePrimitive!(wchar)(id);
2037 	}
2038 
2039 	private T unarchivePrimitive (T, U) (U keyOrId)
2040 	{
2041 		auto tag = toData(T.stringof);
2042 
2043 		static if (isString!(U))
2044 			auto element = getElement(tag, keyOrId);
2045 
2046 		else static if (is(U == Id))
2047 			auto element = getElement(tag, to!(string)(keyOrId), Attributes.idAttribute);
2048 
2049 		else
2050 			static assert (false, format!(`Invalid type "`, U, `". Valid types are "string" and "Id"`));
2051 
2052 		if (!element)
2053 			return T.init;
2054 
2055 		return fromData!(T)(element.value);
2056 	}
2057 
2058 	/**
2059 	 * Performs post processing of the array associated with the given id.
2060 	 *
2061 	 * Post processing can basically be anything that the archive wants to do. This
2062 	 * method is called by the serializer once for each serialized array at the end of
2063 	 * the serialization process when all values have been serialized.
2064 	 *
2065 	 * With this method the archive has a last chance of changing an archived array to
2066 	 * an archived slice instead.
2067 	 *
2068 	 * Params:
2069 	 *     id = the id associated with the array
2070 	 */
2071 	void postProcessArray (Id id)
2072 	{
2073 		if (auto array = getArchivedArray(id))
2074 			array.parent.move(array.node);
2075 	}
2076 
2077 	private void addArchivedArray (Id id, doc.Node parent, doc.Node element, string key)
2078 	{
2079 		archivedArrays[id] = Node(parent, element, id, key);
2080 	}
2081 
2082 	private Node* getArchivedArray (Id id)
2083 	{
2084 		if (auto array = id in archivedArrays)
2085 			return array;
2086 
2087 		error(`Could not continue archiving due to no array with the Id "` ~ to!(string)(id) ~ `" was found.`, __FILE__, __LINE__, [to!(string)(id)]);
2088 
2089 		return null;
2090 	}
2091 
2092 	private Node* getArchivedPointer (Id id)
2093 	{
2094 		if (auto pointer = id in archivedPointers)
2095 			return pointer;
2096 
2097 		error(`Could not continue archiving due to no pointer with the Id "` ~ to!(string)(id) ~ `" was found.`, __FILE__, __LINE__, [to!(string)(id)]);
2098 
2099 		return null;
2100 	}
2101 
2102 	private doc.Node getElement (Data tag, string key, Data attribute = Attributes.keyAttribute, bool throwOnError = true)
2103 	{
2104 		auto set = lastElement.query[tag].attribute((doc.Node node) {
2105 			if (node.name == attribute && node.value == key)
2106 				return true;
2107 
2108 			return false;
2109 		});
2110 
2111 		if (set.nodes.length == 1)
2112 			return set.nodes[0].parent;
2113 
2114 		if (throwOnError)
2115 		{
2116 			if (set.nodes.length == 0)
2117 				error(`Could not find an element "` ~ to!(string)(tag) ~ `" with the attribute "` ~ to!(string)(attribute) ~ `" with the value "` ~ to!(string)(key) ~ `".`, __FILE__, __LINE__, [tag, Attributes.keyAttribute, key]);
2118 
2119 			else
2120 				error(`Could not unarchive the value with the key "` ~ to!(string)(key) ~ `" due to malformed data.`, __FILE__, __LINE__, [tag, Attributes.keyAttribute, key]);
2121 		}
2122 
2123 		return null;
2124 	}
2125 
2126 	private Data getValueOfAttribute (Data attribute, doc.Node element = null)
2127 	{
2128 		if (!element)
2129 			element = lastElement;
2130 
2131 		auto set = element.query.attribute(attribute);
2132 
2133 		if (set.nodes.length == 1)
2134 			return set.nodes[0].value.assumeUnique;
2135 
2136 		else
2137 		{
2138 			if (set.nodes.length == 0)
2139 				error(`Could not find the attribute "` ~ to!(string)(attribute) ~ `".`, __FILE__, __LINE__, [attribute]);
2140 
2141 			else
2142 				error(`Could not unarchive the value of the attribute "` ~ to!(string)(attribute) ~ `" due to malformed data.`, __FILE__, __LINE__, [attribute]);
2143 		}
2144 
2145 		return null;
2146 	}
2147 
2148 	private template errorMessage (ArchiveMode mode = ArchiveMode.archiving)
2149 	{
2150 		static if (mode == ArchiveMode.archiving)
2151 			enum errorMessage = "Could not continue archiving due to unrecognized data format: ";
2152 
2153 		else static if (mode == ArchiveMode.unarchiving)
2154 			enum errorMessage = "Could not continue unarchiving due to unrecognized data format: ";
2155 	}
2156 }