PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Xml/XmlHelper.cs

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