PageRenderTime 87ms CodeModel.GetById 30ms app.highlight 33ms RepoModel.GetById 18ms app.codeStats 0ms

/Utilities/Xml/XmlHelper.cs

#
C# | 586 lines | 355 code | 38 blank | 193 comment | 20 complexity | df38d8aa24c81214118ad42b56556587 MD5 | raw file
  1using System;
  2using System.IO;
  3using System.Xml;
  4using System.Xml.Serialization;
  5using Delta.Utilities.Helpers;
  6using NUnit.Framework;
  7
  8namespace Delta.Utilities.Xml
  9{
 10	/// <summary>
 11	/// Xml helper class to provide some helper functionality for extracting or
 12	/// generating xml data from XmlNodes.
 13	/// </summary>
 14	public class XmlHelper
 15	{
 16		#region Serialize (Static)
 17		/// <summary>
 18		/// Serializes an object
 19		/// This saves an object as an xml structure to a file.
 20		/// Every variable is saved as a node with
 21		/// </summary>
 22		/// <param name="filePath">filepath</param>
 23		/// <param name="obj">obj</param>
 24		public static void Serialize(string filePath, object obj)
 25		{
 26			using (FileStream stream = FileHelper.Open(filePath,
 27				FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
 28			{
 29				StreamWriter fileWriter = new StreamWriter(stream);
 30				// create an Serializer with the objects type so it'll
 31				// know what to serialize
 32				XmlSerializer xmls = new XmlSerializer(obj.GetType());
 33				// create an XmlWriter which will save the data to the file
 34				XmlWriter writer = XmlWriter.Create(fileWriter);
 35				// serialize the object we got as parameter and write it to the
 36				// stream of the above created Xmlwriter.
 37				xmls.Serialize(writer, obj);
 38				// will be called automatically by the "using" block
 39				//// Close the writer so the file isn't locked by the writer any more
 40				////writer.Close();
 41			}
 42		}
 43		#endregion
 44
 45		#region Deserialize (Static)
 46		/// <summary>
 47		/// Deserialize a file to an object
 48		/// This reads an xml structured file if it's of the same type
 49		/// as we provide in the parameters
 50		/// It returns an object by the type we declared and we can
 51		/// simply cast the object after calling this function
 52		/// </summary>
 53		/// <param name="fileName">filename</param>
 54		/// <param name="type">type</param>
 55		/// <returns>Deserialized object</returns>
 56		public static object Deserialize(string fileName, Type type)
 57		{
 58			// create a Serializer which knows how to handle the type we
 59			// give the constructor
 60			XmlSerializer xmls = new XmlSerializer(type);
 61			object obj = null;
 62			using (FileStream stream = FileHelper.Open(fileName,
 63				FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
 64			{
 65				// create an XmlReader that can read the xml file at fileName
 66				XmlReader reader = XmlReader.Create(stream);
 67				// deserialize the data in the xml file and return an object that is of
 68				// the type we provided, or null if the file is not of the provided type.
 69				// The Deserialisation process reads the nodes from the file,
 70				// creates an instance of the type and saves the values from the file
 71				// to the objects variables
 72				obj = xmls.Deserialize(reader);
 73			}
 74			// return our object (null if something went wrong)
 75			return obj;
 76		}
 77		#endregion
 78
 79		#region AddDeclaration (Static)
 80		/// <summary>
 81		/// Add an xml declaration
 82		/// </summary>
 83		/// <param name="version">The current version of the xml file.</param>
 84		/// <param name="encoding">The current encoding of the xml file.</param>
 85		/// <returns>xml declaration</returns>
 86		public static string AddDeclaration(string version, string encoding)
 87		{
 88			return "<?xml version=\"" + version + "\" encoding=\"" + encoding +
 89			       "\"?>";
 90		}
 91		#endregion
 92
 93		#region AddTag (Static)
 94		/// <summary>
 95		/// Create a tag item from the specified string.
 96		/// </summary>
 97		/// <param name="tagName">The name of the tag to create.</param>
 98		/// <returns>string</returns>
 99		public static string AddTag(string tagName)
100		{
101			return "<" + tagName + ">";
102		}
103
104		/// <summary>
105		/// Add a tag with a given depth
106		/// </summary>
107		/// <param name="tagName">Tag name</param>
108		/// <param name="depth">Depth of the node</param>
109		/// <returns>Tag with given depth</returns>
110		public static string AddTag(string tagName, int depth)
111		{
112			string xmlNode = "";
113			for (int i = 0; i < depth; i++)
114			{
115				xmlNode += "  ";
116			}
117			return xmlNode += AddTag(tagName);
118		}
119		#endregion
120
121		#region AddTagEnd (Static)
122		/// <summary>
123		/// Add a tag end
124		/// </summary>
125		/// <param name="tagName">Tag name</param>
126		/// <returns>tag</returns>
127		public static string AddTagEnd(string tagName)
128		{
129			return "</" + tagName + ">";
130		}
131
132		/// <summary>
133		/// Add a tag end with a given depth
134		/// </summary>
135		/// <param name="tagName">Tag name</param>
136		/// <param name="depth">Depth of the node</param>
137		/// <returns>tag</returns>
138		public static string AddTagEnd(string tagName, int depth)
139		{
140			string xmlNode = "";
141			for (int i = 0; i < depth; i++)
142			{
143				xmlNode += "  ";
144			}
145			return xmlNode += AddTagEnd(tagName);
146		}
147		#endregion
148
149		#region AddElement (Static)
150		/// <summary>
151		/// Add an element
152		/// </summary>
153		/// <param name="tagName">Tag name</param>
154		/// <param name="element">Element</param>
155		/// <returns>string</returns>
156		public static string AddElement(string tagName, string element)
157		{
158			return "<" + tagName + ">" + element + "</" + tagName + ">";
159		}
160
161		/// <summary>
162		/// Add an element
163		/// </summary>
164		/// <param name="tagName">Tag name</param>
165		/// <param name="element">Element</param>
166		/// <param name="depth">Depth of the node</param>
167		/// <returns>string</returns>
168		public static string AddElement(string tagName, string element, int depth)
169		{
170			return new string(' ', depth * 2) +
171			       AddElement(tagName, element);
172		}
173		#endregion
174
175		#region AddAttribute (Static)
176		/// <summary>
177		/// Add an attribute. The number of attribute names and values must be 
178		/// equal. Each value will be assigned to a name with the corresponding 
179		/// position in the arrays. (1="1" , 2="2", ...)
180		/// </summary>
181		/// <param name="tagName">Tag name</param>
182		/// <param name="attributeNames">Attribute names</param>
183		/// <param name="attributeValues">Attribute values</param>
184		/// <returns>xmlNode</returns>
185		public static string AddAttribute(string tagName, string[] attributeNames,
186			string[] attributeValues)
187		{
188			// Check if every attribute name has a value or vice versa
189			if (attributeNames.Length !=
190			    attributeValues.Length)
191			{
192				// Return an empty element if this is not the case.
193				return AddTag(tagName) + AddTagEnd(tagName);
194			}
195
196			// Start adding the attributes and their values.
197			string xmlNode = "<" + tagName;
198			for (int i = 0; i < attributeNames.Length; i++)
199			{
200				xmlNode += " " + attributeNames[i] + "=\"" + attributeValues[i] + "\"";
201			}
202			return xmlNode += ">";
203		}
204
205		/// <summary>
206		/// Add an attribute with a given depth. The number of attribute names and 
207		/// values must be equal. 
208		/// Each value will be assigned to a name with the corresponding 
209		/// position in the arrays. (1="1" , 2="2", ...)
210		/// </summary>
211		/// <param name="tagName">Tag name</param>
212		/// <param name="attributeNames">Attribute names</param>
213		/// <param name="attributeValues">Attribute values</param>
214		/// <param name="depth">Depth of the node</param>
215		/// <returns>string</returns>
216		public static string AddAttribute(string tagName, string[] attributeNames,
217			string[] attributeValues, int depth)
218		{
219			return new string(' ', depth * 2) +
220			       AddAttribute(tagName, attributeNames, attributeValues);
221		}
222		#endregion
223
224		#region AddAttributeEnd (Static)
225		/// <summary>
226		/// Add an attribute with an end. The number of attribute names and values 
227		/// must be equal. 
228		/// Each value will be assigned to a name with the corresponding 
229		/// position in the arrays. (1="1" , 2="2", ...)
230		/// </summary>
231		/// <param name="tagName">Tag name</param>
232		/// <param name="attributeNames">Attribute names</param>
233		/// <param name="attributeValues">Attribute values</param>
234		/// <returns>String with the new xmlNode data</returns>
235		public static string AddAttributeEnd(string tagName,
236			string[] attributeNames, string[] attributeValues)
237		{
238			// Check if every attribute name has a value or vice versa
239			if (attributeNames.Length !=
240			    attributeValues.Length)
241			{
242				// Return an empty element if this is not the case.
243				return AddTag(tagName) + AddTagEnd(tagName);
244			}
245
246			// Start adding the attributes and their values.
247			string xmlNode = "<" + tagName;
248			for (int i = 0; i < attributeNames.Length; i++)
249			{
250				xmlNode += " " + attributeNames[i] + "=\"" + attributeValues[i] + "\"";
251			}
252			return xmlNode += "/>";
253		}
254
255		/// <summary>
256		/// Add an attribute with a given depth and an end. The number of
257		/// attribute names and values must be equal. 
258		/// Each value will be assigned to a name with the corresponding 
259		/// position in the arrays. (1="1" , 2="2", ...)
260		/// </summary>
261		/// <param name="tagName">Tag name</param>
262		/// <param name="attributeNames">Attribute names</param>
263		/// <param name="attributeValues">Attribute values</param>
264		/// <param name="depth">Depth of the node</param>
265		/// <returns>string</returns>
266		public static string AddAttributeEnd(string tagName,
267			string[] attributeNames, string[] attributeValues, int depth)
268		{
269			return new string(' ', depth * 2) +
270			       AddAttributeEnd(tagName, attributeNames, attributeValues);
271		}
272		#endregion
273
274		#region AddAttributeElement (Static)
275		/// <summary>
276		/// Add an attribute with a given element between the start and end tag. 
277		/// The number of attribute names and values must be equal. 
278		/// Each value will be assigned to a name with the corresponding 
279		/// position in the arrays. (1="1" , 2="2", ...)
280		/// </summary>
281		/// <param name="tagName">Tag name</param>
282		/// <param name="attributeNames">Attribute names</param>
283		/// <param name="attributeValues">Attribute values</param>
284		/// <param name="element">Element</param>
285		/// <returns>string</returns>
286		public static string AddAttributeElement(string tagName,
287			string[] attributeNames, string[] attributeValues, string element)
288		{
289			// Check if every attribute name has a value or vice versa
290			if (attributeNames.Length !=
291			    attributeValues.Length)
292			{
293				// Return an empty element if this is not the case.
294				return AddTag(tagName) + AddTagEnd(tagName);
295			}
296
297			// Start adding the attributes and their values.
298			string xmlNode = "<" + tagName;
299			for (int i = 0; i < attributeNames.Length; i++)
300			{
301				xmlNode += " " + attributeNames[i] + "=\"" + attributeValues[i] + "\"";
302			}
303			if (String.IsNullOrEmpty(element))
304			{
305				return xmlNode += "/>";
306			}
307			else
308			{
309				return xmlNode += ">" + element + AddTagEnd(tagName);
310			}
311		}
312
313		/// <summary>
314		/// Add an attribute with a given element between the start and end tag and
315		/// a given depth. 
316		/// The number of attribute names and values must be equal. 
317		/// Each value will be assigned to a name with the corresponding 
318		/// position in the arrays. (1="1" , 2="2", ...)
319		/// </summary>
320		/// <param name="tagName">Tag name</param>
321		/// <param name="attributeNames">Attribute names</param>
322		/// <param name="attributeValues">Attribute values</param>
323		/// <param name="element">Element</param>
324		/// <param name="depth">Depth of the node</param>
325		/// <returns>string</returns>
326		public static string AddAttributeElement(string tagName,
327			string[] attributeNames, string[] attributeValues, string element,
328			int depth)
329		{
330			return new string(' ', depth * 2) +
331			       AddAttributeElement(tagName, attributeNames, attributeValues, element);
332		}
333		#endregion
334
335		#region ConvertToXmlSpecialCharacters (Static)
336		/// <summary>
337		/// Convert to xml special characters, will replace all special characters
338		/// that need replacement (&lt; becomes &lt;, " becomes &quot;, etc.).
339		/// See http://xml.silmaril.ie/authors/specials/ for more information.
340		/// Note: The input text should not already contain special xml characters.
341		/// </summary>
342		/// <param name="inputText">input text</param>
343		/// <returns>string</returns>
344		public static string ConvertToXmlSpecialCharacters(string inputText)
345		{
346			#region Replace '&'
347			// We have to check this differently, because it can be an '&' of a
348			// special character which would result in an '&amp;lt;' for '&lt;'.
349
350			int[] ampIndices = inputText.GetAllIndicesOf("&");
351			for (int index = ampIndices.Length - 1; index >= 0; index--)
352			{
353				string checkString = inputText.Substring(ampIndices[index]);
354				if (checkString.StartsWith("&amp;") ||
355					checkString.StartsWith("&lt;") ||
356					checkString.StartsWith("&gt;") ||
357					checkString.StartsWith("&quot;") ||
358					checkString.StartsWith("&apos;"))
359				{
360					continue;
361				}
362
363				inputText = inputText.Insert(ampIndices[index] + 1, "amp;");
364			}
365			#endregion
366
367			return inputText.
368				Replace("<", "&lt;").
369				Replace(">", "&gt;").
370				Replace("\"", "&quot;").
371				Replace("'", "&apos;");
372		}
373		#endregion
374
375		/// <summary>
376		/// Tests
377		/// </summary>
378		internal class XmlHelperTests
379		{
380			#region Helpers
381			private const string TestXmlDocumentPath =
382				@"C:\Windows\System32\icsxml\ipcfg.xml";
383			#endregion
384
385			#region LoadFile (Static)
386			/// <summary>
387			/// Load file. Note: Too slow for a dynamic unit test.
388			/// </summary>
389			[Test]
390			public static void LoadFile()
391			{
392				Assert.NotNull(XmlNode.FromFile(TestXmlDocumentPath));
393			}
394			#endregion
395
396			#region LoadingMultipleXmlFiles (Static)
397			/// <summary>
398			/// Loading multiple xml files. Note: Too slow for dynamic unit tests
399			/// </summary>
400			[Test]
401			public static void LoadingMultipleXmlFiles()
402			{
403				const int TestRuns = 10;
404				for (int i = 0; i < TestRuns; i++)
405				{
406					XmlNode.FromFile(TestXmlDocumentPath);
407					//XmlHelper.LoadXmlFromFile("");
408				}
409			}
410			#endregion
411
412			#region TestSerialization (Static)
413			/// <summary>
414			/// Test serialization. Note: This test is too slow for a dynamic unit
415			/// test. It also is strange that this c:\testfile.xml is created ..
416			/// </summary>
417			[Test]
418			public static void TestSerialization()
419			{
420				string teststring = "This is a test string for Serialization";
421				Serialize("testfile.xml", teststring);
422				Assert.True(File.Exists("testfile.xml"),
423					"Check if file exists (indicates that serialization worked)");
424				object checkObject = Deserialize("testfile.xml",
425					typeof(string));
426				Assert.Equal(teststring, (string)checkObject);
427				FileHelper.SafeDelete("testfile.xml");
428			}
429			#endregion
430
431			#region XmlGeneration (Static)
432			/// <summary>
433			/// XmlGeneration
434			/// </summary>
435			[Test]
436			public static void XmlGeneration()
437			{
438				FileStream saveHandle = FileHelper.Open("Unit-Test.xml",
439					FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
440
441				using (StreamWriter stream = new StreamWriter(saveHandle))
442				{
443					stream.WriteLine(AddDeclaration("1.0", "utf-8"));
444					stream.WriteLine(AddAttribute("COLLADA",
445						new[]
446						{
447							"xmlns", "version"
448						},
449						new[]
450						{
451							"http://www.collada.org/2005/11/COLLADASchema",
452							"1.4.1"
453						}));
454					stream.WriteLine(AddTag("asset", 1));
455					stream.WriteLine(AddTag("contributor", 2));
456					stream.WriteLine(AddElement("author", "Kevin", 3));
457					stream.WriteLine(AddTagEnd("contributor", 2));
458					stream.WriteLine(AddAttributeEnd("unit",
459						new[]
460						{
461							"meter", "name"
462						},
463						new[]
464						{
465							"0.0254", "inch"
466						}, 2));
467					stream.WriteLine(AddAttributeElement("positions",
468						new[]
469						{
470							"id", "type"
471						},
472						new[]
473						{
474							"x", "element"
475						},
476						"0 1 2 3 4 5 6 7 8 9 10", 2));
477					stream.WriteLine(AddTagEnd("asset", 1));
478					stream.WriteLine(AddTagEnd("COLLADA"));
479				}
480
481				Log.Test("File created successfully");
482			}
483			#endregion
484
485			#region ImportFromXml (Static)
486			/// <summary>
487			/// Import from xml
488			/// </summary>
489			[Test]
490			public static void ImportFromXml()
491			{
492				XmlNode workbook = XmlNode.FromFile(@"Enemy Stats.xml");
493				if (workbook == null)
494				{
495					return;
496				}
497
498				Log.Test("Importing SportPlacesGermany.xml");
499				// Get all worksheets (we have many)
500				foreach (XmlNode worksheet in workbook.Children)
501				{
502					if (worksheet.Name == "Worksheet")
503					{
504						string tableName = worksheet.GetAttribute("Name", false);
505						Log.Test("Importing table: " + tableName);
506
507						// Get the inside table with all the data
508						XmlNode table = worksheet.GetChild("Table");
509						// And iterate through all rows
510						foreach (XmlNode row in table.Children)
511						{
512							if (row.Name == "Row")
513							{
514								// Grab all values that could be interesting for us
515								foreach (XmlNode cell in row.Children)
516								{
517									XmlNode data = cell.GetChild("Data");
518									if (data != null)
519									{
520										Console.Write(data.Value + " ");
521									}
522								}
523							}
524						}
525					}
526				}
527			}
528			#endregion
529
530			#region LoadSnippet
531			/// <summary>
532			/// Load snippet
533			/// </summary>
534			[Test]
535			public void LoadSnippet()
536			{
537				string xmlSnippet =
538					@"
539					<Root>
540						<Child>
541							<SubChild1>1</SubChild1>
542							<SubChild2>2</SubChild2>
543						</Child>
544					</Root>					
545					";
546
547				XmlNode rootNode = XmlNode.FromSnippet(xmlSnippet);
548				Assert.NotNull(rootNode);
549				Assert.Equal("Root", rootNode.Name);
550				Assert.Equal(1, rootNode.Children.Length);
551
552				XmlNode childNode = rootNode.Children[0];
553				Assert.Equal("Child", childNode.Name);
554				Assert.Equal(2, childNode.Children.Length);
555
556				XmlNode subChild1 = childNode.Children[0];
557				Assert.Equal(0, subChild1.Children.Length);
558				Assert.Equal("SubChild1", subChild1.Name);
559				Assert.Equal("1", subChild1.Value);
560
561				XmlNode subChild2 = childNode.Children[1];
562				Assert.Equal(0, subChild2.Children.Length);
563				Assert.Equal("SubChild2", subChild2.Name);
564				Assert.Equal("2", subChild2.Value);
565			}
566			#endregion
567
568			#region ConvertToXmlSpecialCharacters
569			/// <summary>
570			/// Convert to xml special characters
571			/// </summary>
572			[Test]
573			public void ConvertToXmlSpecialCharacters()
574			{
575				Assert.Equal("Hello",
576					XmlHelper.ConvertToXmlSpecialCharacters("Hello"));
577				Assert.Equal("&lt;", XmlHelper.ConvertToXmlSpecialCharacters("<"));
578				Assert.Equal("&gt;", XmlHelper.ConvertToXmlSpecialCharacters(">"));
579				Assert.Equal("&quot;&amp;&quot;",
580					XmlHelper.ConvertToXmlSpecialCharacters("\"&\""));
581				Assert.Equal("&apos;", XmlHelper.ConvertToXmlSpecialCharacters("'"));
582			}
583			#endregion
584		}
585	}
586}