XmlReaderExtensions.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. // Copyright (c) Craftwork Games. All rights reserved.
  2. // Licensed under the MIT license.
  3. // See LICENSE file in the project root for full license information.
  4. using System;
  5. using System.Xml;
  6. using Microsoft.Xna.Framework;
  7. namespace MonoGame.Extended.Serialization.Xml;
  8. /// <summary>
  9. /// Provides extension methods for <see cref="XmlReader"/> to simplify reading and parsing XML Attributes into
  10. /// strong-typed values.
  11. /// </summary>
  12. public static class XmlReaderExtensions
  13. {
  14. /// <summary>
  15. /// Reads an XML attribute as an <see langword="int"/> value.
  16. /// </summary>
  17. /// <param name="reader">The XML reader instance.</param>
  18. /// <param name="attributeName">The name of the attribute to read.</param>
  19. /// <returns>The <see langword="int"/> value parsed from the value of the specified attribute.</returns>
  20. /// <exception cref="XmlException">
  21. /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as an <see langword="int"/>.
  22. /// </exception>
  23. public static int GetAttributeInt(this XmlReader reader, string attributeName)
  24. {
  25. string value = reader.GetAttribute(attributeName);
  26. if (value == null)
  27. {
  28. throw new XmlException($"Required attribute '{attributeName}' is missing.");
  29. }
  30. try
  31. {
  32. return int.Parse(value);
  33. }
  34. catch (Exception ex) when (ex is FormatException || ex is OverflowException)
  35. {
  36. throw new XmlException(
  37. $"Invalid integer format for attribute '{attributeName}'. Expected integer, but got '{value}'",
  38. ex
  39. );
  40. }
  41. }
  42. /// <summary>
  43. /// Reads an XML attribute as an <see langword="int"/> value, returning a default value if the attribute is missing or invalid.
  44. /// </summary>
  45. /// <param name="reader">The XML reader instance.</param>
  46. /// <param name="attributeName">The name of the attribute to read.</param>
  47. /// <param name="defaultValue">The default value to return if the attribute is missing or cannot be parsed.</param>
  48. /// <returns>The <see langword="int"/> value parsed from the value of the specified attribute, or the default value if parsing fails.</returns>
  49. public static int GetAttributeInt(this XmlReader reader, string attributeName, int defaultValue)
  50. {
  51. string value = reader.GetAttribute(attributeName);
  52. if (value == null)
  53. {
  54. return defaultValue;
  55. }
  56. try
  57. {
  58. return int.Parse(value);
  59. }
  60. catch
  61. {
  62. return defaultValue;
  63. }
  64. }
  65. /// <summary>
  66. /// Reads an XML attribute as a <see langword="float"/> value.
  67. /// </summary>
  68. /// <param name="reader">The XML reader instance.</param>
  69. /// <param name="attributeName">The name of the attribute to read.</param>
  70. /// <returns>The <see langword="float"/> value parsed from the specified attribute.</returns>
  71. /// <exception cref="XmlException">
  72. /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as a <see langword="float"/>.
  73. /// </exception>
  74. public static float GetAttributeFloat(this XmlReader reader, string attributeName)
  75. {
  76. string value = reader.GetAttribute(attributeName);
  77. if (value == null)
  78. {
  79. throw new XmlException($"Required attribute '{attributeName}' is missing.");
  80. }
  81. try
  82. {
  83. return float.Parse(value);
  84. }
  85. catch (Exception ex) when (ex is FormatException || ex is OverflowException)
  86. {
  87. throw new XmlException(
  88. $"Invalid float format for attribute '{attributeName}'. Expected float, but got '{value}'",
  89. ex
  90. );
  91. }
  92. }
  93. /// <summary>
  94. /// Reads an XML attribute as a <see langword="float"/> value, returning a default value if the attribute is missing or invalid.
  95. /// </summary>
  96. /// <param name="reader">The XML reader instance.</param>
  97. /// <param name="attributeName">The name of the attribute to read.</param>
  98. /// <param name="defaultValue">The default value to return if the attribute is missing or cannot be parsed.</param>
  99. /// <returns>The <see langword="float"/> value parsed from the specified attribute, or the default value if parsing fails.</returns>
  100. public static float GetAttributeFloat(this XmlReader reader, string attributeName, float defaultValue)
  101. {
  102. string value = reader.GetAttribute(attributeName);
  103. if (value == null)
  104. {
  105. return defaultValue;
  106. }
  107. try
  108. {
  109. return float.Parse(value);
  110. }
  111. catch
  112. {
  113. return defaultValue;
  114. }
  115. }
  116. /// <summary>
  117. /// Reads an XML attribute as a <see langword="bool"/> value.
  118. /// </summary>
  119. /// <param name="reader">The XML reader instance.</param>
  120. /// <param name="attributeName">The name of the attribute to read.</param>
  121. /// <returns>The <see langword="bool"/> value parsed from the value of the specified attribute.</returns>
  122. /// <exception cref="XmlException">
  123. /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as a <see langword="bool"/>.
  124. /// </exception>
  125. public static bool GetAttributeBool(this XmlReader reader, string attributeName)
  126. {
  127. string value = reader.GetAttribute(attributeName);
  128. if (value == null)
  129. {
  130. throw new XmlException($"Required attribute '{attributeName}' is missing.");
  131. }
  132. try
  133. {
  134. return bool.Parse(value);
  135. }
  136. catch (Exception ex) when (ex is FormatException)
  137. {
  138. throw new XmlException(
  139. $"Invalid bool format for attribute '{attributeName}'. Expected 'true' or 'false' but got '{value}'",
  140. ex
  141. );
  142. }
  143. }
  144. /// <summary>
  145. /// Reads an XML attribute as a <see langword="bool"/> value, returning a default value if the attribute is missing or invalid.
  146. /// </summary>
  147. /// <param name="reader">The XML reader instance.</param>
  148. /// <param name="attributeName">The name of the attribute to read.</param>
  149. /// <param name="defaultValue">The default value to return if the attribute is missing or cannot be parsed.</param>
  150. /// <returns>The <see langword="bool"/> value parsed from the value of the specified attribute, or the default value if parsing fails.</returns>
  151. public static bool GetAttributeBool(this XmlReader reader, string attributeName, bool defaultValue)
  152. {
  153. string value = reader.GetAttribute(attributeName);
  154. if (value == null)
  155. {
  156. return defaultValue;
  157. }
  158. try
  159. {
  160. return bool.Parse(value);
  161. }
  162. catch
  163. {
  164. return defaultValue;
  165. }
  166. }
  167. /// <summary>
  168. /// Reads an XML attribute as an enumeration value.
  169. /// </summary>
  170. /// <typeparam name="T">The enumeration type to parse.</typeparam>
  171. /// <param name="reader">The XML reader instance.</param>
  172. /// <param name="attributeName">The name of the attribute to read.</param>
  173. /// <returns>The enumeration value parsed from the value of the specified attribute.</returns>
  174. /// <exception cref="XmlException">
  175. /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as the specified enumeration type.
  176. /// </exception>
  177. public static T GetAttributeEnum<T>(this XmlReader reader, string attributeName) where T : struct, Enum
  178. {
  179. string value = reader.GetAttribute(attributeName);
  180. if (value == null)
  181. {
  182. throw new XmlException($"Required attribute '{attributeName}' is missing.");
  183. }
  184. try
  185. {
  186. return Enum.Parse<T>(value);
  187. }
  188. catch (Exception ex) when (ex is ArgumentException)
  189. {
  190. throw new XmlException(
  191. $"Invalid {typeof(T).Name} format for attribute '{attributeName}'. Expected a {typeof(T).Name} value but got '{value}'",
  192. ex
  193. );
  194. }
  195. }
  196. /// <summary>
  197. /// Reads an XML attribute as an enumeration value, returning a default value if the attribute is missing or invalid.
  198. /// </summary>
  199. /// <typeparam name="T">The enumeration type to parse.</typeparam>
  200. /// <param name="reader">The XML reader instance.</param>
  201. /// <param name="attributeName">The name of the attribute to read.</param>
  202. /// <param name="defaultValue">The default value to return if the attribute is missing or cannot be parsed.</param>
  203. /// <returns>The enumeration value parsed from the value of the specified attribute, or the default value if parsing fails.</returns>
  204. public static T GetAttributeEnum<T>(this XmlReader reader, string attributeName, T defaultValue) where T : struct, Enum
  205. {
  206. string value = reader.GetAttribute(attributeName);
  207. if (value == null)
  208. {
  209. return defaultValue;
  210. }
  211. try
  212. {
  213. return Enum.Parse<T>(value);
  214. }
  215. catch
  216. {
  217. return defaultValue;
  218. }
  219. }
  220. /// <summary>
  221. /// Reads an XML attribute as a <see cref="Rectangle"/> value.
  222. /// </summary>
  223. /// <param name="reader">The XML reader instance.</param>
  224. /// <param name="attributeName">The name of the attribute to read.</param>
  225. /// <returns>The <see cref="Rectangle"/> value parsed from the value of the specified attribute.</returns>
  226. /// <exception cref="XmlException">
  227. /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as a <see cref="Rectangle"/>.
  228. /// </exception>
  229. public static Rectangle GetAttributeRectangle(this XmlReader reader, string attributeName)
  230. {
  231. string value = reader.GetAttribute(attributeName);
  232. if (value == null)
  233. {
  234. throw new XmlException($"Required attribute '{attributeName}' is missing.");
  235. }
  236. string[] split = value.Split(',', StringSplitOptions.RemoveEmptyEntries);
  237. try
  238. {
  239. if (split.Length != 4)
  240. {
  241. throw new InvalidOperationException($"{nameof(Rectangle)} attribute must contain four integer values separated by a comma");
  242. }
  243. int x = int.Parse(split[0]);
  244. int y = int.Parse(split[1]);
  245. int width = int.Parse(split[2]);
  246. int height = int.Parse(split[3]);
  247. return new Rectangle(x, y, width, height);
  248. }
  249. catch (Exception ex) when (ex is FormatException || ex is OverflowException || ex is InvalidOperationException)
  250. {
  251. throw new XmlException(
  252. $"Invalid {nameof(Rectangle)} format for attribute '{attributeName}'. Expected 'x,y,width,height' but got '{value}'",
  253. ex
  254. );
  255. }
  256. }
  257. /// <summary>
  258. /// Reads an XML attribute as a <see cref="Rectangle"/> value, returning a default value if the attribute is missing or invalid.
  259. /// </summary>
  260. /// <param name="reader">The XML reader instance.</param>
  261. /// <param name="attributeName">The name of the attribute to read.</param>
  262. /// <param name="defaultValue">The default value to return if the attribute is missing or cannot be parsed.</param>
  263. /// <returns>The <see cref="Rectangle"/> value parsed from the value of the specified attribute, or the default value if parsing fails.</returns>
  264. public static Rectangle GetAttributeRectangle(this XmlReader reader, string attributeName, Rectangle defaultValue)
  265. {
  266. string value = reader.GetAttribute(attributeName);
  267. if (value == null)
  268. {
  269. return defaultValue;
  270. }
  271. string[] split = value.Split(',', StringSplitOptions.RemoveEmptyEntries);
  272. try
  273. {
  274. if (split.Length != 4)
  275. {
  276. return defaultValue;
  277. }
  278. int x = int.Parse(split[0]);
  279. int y = int.Parse(split[1]);
  280. int width = int.Parse(split[2]);
  281. int height = int.Parse(split[3]);
  282. return new Rectangle(x, y, width, height);
  283. }
  284. catch
  285. {
  286. return defaultValue;
  287. }
  288. }
  289. /// <summary>
  290. /// Reads an XML attribute as a <see cref="Vector2"/> value.
  291. /// </summary>
  292. /// <param name="reader">The XML reader instance.</param>
  293. /// <param name="attributeName">The name of the attribute to read.</param>
  294. /// <returns>The <see cref="Vector2"/> value parsed from the value of the specified attribute.</returns>
  295. /// <exception cref="XmlException">
  296. /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as a <see cref="Vector2"/>.
  297. /// </exception>
  298. public static Vector2 GetAttributeVector2(this XmlReader reader, string attributeName)
  299. {
  300. string value = reader.GetAttribute(attributeName);
  301. if (value == null)
  302. {
  303. throw new XmlException($"Required attribute '{attributeName}' is missing.");
  304. }
  305. string[] split = value.Split(',', StringSplitOptions.RemoveEmptyEntries);
  306. try
  307. {
  308. if (split.Length != 2)
  309. {
  310. throw new InvalidOperationException($"{nameof(Vector2)} attribute must contain two float values separated by a comma");
  311. }
  312. float x = float.Parse(split[0]);
  313. float y = float.Parse(split[1]);
  314. return new Vector2(x, y);
  315. }
  316. catch (Exception ex) when (ex is FormatException || ex is OverflowException || ex is InvalidOperationException)
  317. {
  318. throw new XmlException(
  319. $"Invalid {nameof(Vector2)} format for attribute '{attributeName}'. Expected 'x,y', but got '{value}'",
  320. ex
  321. );
  322. }
  323. }
  324. /// <summary>
  325. /// Reads an XML attribute as a <see cref="Vector2"/> value, returning a default value if the attribute is missing or invalid.
  326. /// </summary>
  327. /// <param name="reader">The XML reader instance.</param>
  328. /// <param name="attributeName">The name of the attribute to read.</param>
  329. /// <param name="defaultValue">The default value to return if the attribute is missing or cannot be parsed.</param>
  330. /// <returns>The <see cref="Vector2"/> value parsed from the value of the specified attribute, or the default value if parsing fails.</returns>
  331. public static Vector2 GetAttributeVector2(this XmlReader reader, string attributeName, Vector2 defaultValue)
  332. {
  333. string value = reader.GetAttribute(attributeName);
  334. if (value == null)
  335. {
  336. return defaultValue;
  337. }
  338. string[] split = value.Split(',', StringSplitOptions.RemoveEmptyEntries);
  339. try
  340. {
  341. if (split.Length != 2)
  342. {
  343. return defaultValue;
  344. }
  345. float x = float.Parse(split[0]);
  346. float y = float.Parse(split[1]);
  347. return new Vector2(x, y);
  348. }
  349. catch
  350. {
  351. return defaultValue;
  352. }
  353. }
  354. /// <summary>
  355. /// Reads an XML attribute as a <see cref="Vector3"/> value.
  356. /// </summary>
  357. /// <param name="reader">The XML reader instance.</param>
  358. /// <param name="attributeName">The name of the attribute to read.</param>
  359. /// <returns>The <see cref="Vector3"/> value parsed from the value of the specified attribute.</returns>
  360. /// <exception cref="XmlException">
  361. /// Thrown when the attribute is missing, or when the attribute value cannot be parsed as a <see cref="Vector3"/>.
  362. /// </exception>
  363. public static Vector3 GetAttributeVector3(this XmlReader reader, string attributeName)
  364. {
  365. string value = reader.GetAttribute(attributeName);
  366. if (value == null)
  367. {
  368. throw new XmlException($"Required attribute '{attributeName}' is missing.");
  369. }
  370. string[] split = value.Split(',', StringSplitOptions.RemoveEmptyEntries);
  371. try
  372. {
  373. if (split.Length != 3)
  374. {
  375. throw new InvalidOperationException($"{nameof(Vector3)} attribute must contain three float values separated by a comma");
  376. }
  377. float x = float.Parse(split[0]);
  378. float y = float.Parse(split[1]);
  379. float z = float.Parse(split[2]);
  380. return new Vector3(x, y, z);
  381. }
  382. catch (Exception ex) when (ex is FormatException || ex is OverflowException || ex is InvalidOperationException)
  383. {
  384. throw new XmlException(
  385. $"Invalid {nameof(Vector3)} format for attribute '{attributeName}'. Expected 'x,y,z', but got '{value}'",
  386. ex
  387. );
  388. }
  389. }
  390. /// <summary>
  391. /// Reads an XML attribute as a <see cref="Vector3"/> value, returning a default value if the attribute is missing or invalid.
  392. /// </summary>
  393. /// <param name="reader">The XML reader instance.</param>
  394. /// <param name="attributeName">The name of the attribute to read.</param>
  395. /// <param name="defaultValue">The default value to return if the attribute is missing or cannot be parsed.</param>
  396. /// <returns>The <see cref="Vector3"/> value parsed from the value of the specified attribute, or the default value if parsing fails.</returns>
  397. public static Vector3 GetAttributeVector3(this XmlReader reader, string attributeName, Vector3 defaultValue)
  398. {
  399. string value = reader.GetAttribute(attributeName);
  400. if (value == null)
  401. {
  402. return defaultValue;
  403. }
  404. string[] split = value.Split(',', StringSplitOptions.RemoveEmptyEntries);
  405. try
  406. {
  407. if (split.Length != 3)
  408. {
  409. return defaultValue;
  410. }
  411. float x = float.Parse(split[0]);
  412. float y = float.Parse(split[1]);
  413. float z = float.Parse(split[2]);
  414. return new Vector3(x, y, z);
  415. }
  416. catch
  417. {
  418. return defaultValue;
  419. }
  420. }
  421. }