XmlReaderExtensions.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Xml;
  6. using Jellyfin.Data.Enums;
  7. using MediaBrowser.Controller.Entities;
  8. namespace MediaBrowser.Controller.Extensions;
  9. /// <summary>
  10. /// Provides extension methods for <see cref="XmlReader"/> to parse <see cref="BaseItem"/>'s.
  11. /// </summary>
  12. public static class XmlReaderExtensions
  13. {
  14. /// <summary>
  15. /// Reads a trimmed string from the current node.
  16. /// </summary>
  17. /// <param name="reader">The <see cref="XmlReader"/>.</param>
  18. /// <returns>The trimmed content.</returns>
  19. public static string ReadNormalizedString(this XmlReader reader)
  20. {
  21. ArgumentNullException.ThrowIfNull(reader);
  22. return reader.ReadElementContentAsString().Trim();
  23. }
  24. /// <summary>
  25. /// Reads an int from the current node.
  26. /// </summary>
  27. /// <param name="reader">The <see cref="XmlReader"/>.</param>
  28. /// <param name="value">The parsed <c>int</c>.</param>
  29. /// <returns>A value indicating whether the parsing succeeded.</returns>
  30. public static bool TryReadInt(this XmlReader reader, out int value)
  31. {
  32. ArgumentNullException.ThrowIfNull(reader);
  33. return int.TryParse(reader.ReadElementContentAsString(), CultureInfo.InvariantCulture, out value);
  34. }
  35. /// <summary>
  36. /// Parses a <see cref="DateTime"/> from the current node.
  37. /// </summary>
  38. /// <param name="reader">The <see cref="XmlReader"/>.</param>
  39. /// <param name="value">The parsed <see cref="DateTime"/>.</param>
  40. /// <returns>A value indicating whether the parsing succeeded.</returns>
  41. public static bool TryReadDateTime(this XmlReader reader, out DateTime value)
  42. {
  43. ArgumentNullException.ThrowIfNull(reader);
  44. return DateTime.TryParse(
  45. reader.ReadElementContentAsString(),
  46. CultureInfo.InvariantCulture,
  47. DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
  48. out value);
  49. }
  50. /// <summary>
  51. /// Parses a <see cref="DateTime"/> from the current node.
  52. /// </summary>
  53. /// <param name="reader">The <see cref="XmlReader"/>.</param>
  54. /// <param name="formatString">The date format string.</param>
  55. /// <param name="value">The parsed <see cref="DateTime"/>.</param>
  56. /// <returns>A value indicating whether the parsing succeeded.</returns>
  57. public static bool TryReadDateTimeExact(this XmlReader reader, string formatString, out DateTime value)
  58. {
  59. ArgumentNullException.ThrowIfNull(reader);
  60. ArgumentNullException.ThrowIfNull(formatString);
  61. return DateTime.TryParseExact(
  62. reader.ReadElementContentAsString(),
  63. formatString,
  64. CultureInfo.InvariantCulture,
  65. DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
  66. out value);
  67. }
  68. /// <summary>
  69. /// Parses a <see cref="PersonInfo"/> from the xml node.
  70. /// </summary>
  71. /// <param name="reader">The <see cref="XmlReader"/>.</param>
  72. /// <returns>A <see cref="PersonInfo"/>, or <c>null</c> if none is found.</returns>
  73. public static PersonInfo? GetPersonFromXmlNode(this XmlReader reader)
  74. {
  75. ArgumentNullException.ThrowIfNull(reader);
  76. if (reader.IsEmptyElement)
  77. {
  78. reader.Read();
  79. return null;
  80. }
  81. var name = string.Empty;
  82. var type = PersonKind.Actor; // If type is not specified assume actor
  83. var role = string.Empty;
  84. int? sortOrder = null;
  85. string? imageUrl = null;
  86. using var subtree = reader.ReadSubtree();
  87. subtree.MoveToContent();
  88. subtree.Read();
  89. while (subtree is { EOF: false, ReadState: ReadState.Interactive })
  90. {
  91. if (subtree.NodeType != XmlNodeType.Element)
  92. {
  93. subtree.Read();
  94. continue;
  95. }
  96. switch (subtree.Name)
  97. {
  98. case "name":
  99. case "Name":
  100. name = subtree.ReadNormalizedString();
  101. break;
  102. case "role":
  103. case "Role":
  104. role = subtree.ReadNormalizedString();
  105. break;
  106. case "type":
  107. case "Type":
  108. Enum.TryParse(subtree.ReadElementContentAsString(), true, out type);
  109. break;
  110. case "order":
  111. case "sortorder":
  112. case "SortOrder":
  113. if (subtree.TryReadInt(out var sortOrderVal))
  114. {
  115. sortOrder = sortOrderVal;
  116. }
  117. break;
  118. case "thumb":
  119. imageUrl = subtree.ReadNormalizedString();
  120. break;
  121. default:
  122. subtree.Skip();
  123. break;
  124. }
  125. }
  126. if (string.IsNullOrWhiteSpace(name))
  127. {
  128. return null;
  129. }
  130. return new PersonInfo
  131. {
  132. Name = name,
  133. Role = role,
  134. Type = type,
  135. SortOrder = sortOrder,
  136. ImageUrl = imageUrl
  137. };
  138. }
  139. /// <summary>
  140. /// Used to split names of comma or pipe delimited genres and people.
  141. /// </summary>
  142. /// <param name="reader">The <see cref="XmlReader"/>.</param>
  143. /// <returns>IEnumerable{System.String}.</returns>
  144. public static IEnumerable<string> GetStringArray(this XmlReader reader)
  145. {
  146. ArgumentNullException.ThrowIfNull(reader);
  147. var value = reader.ReadElementContentAsString();
  148. // Only split by comma if there is no pipe in the string
  149. // We have to be careful to not split names like Matthew, Jr.
  150. var separator = !value.Contains('|', StringComparison.Ordinal)
  151. && !value.Contains(';', StringComparison.Ordinal)
  152. ? new[] { ',' }
  153. : new[] { '|', ';' };
  154. foreach (var part in value.Trim().Trim(separator).Split(separator))
  155. {
  156. if (!string.IsNullOrWhiteSpace(part))
  157. {
  158. yield return part.Trim();
  159. }
  160. }
  161. }
  162. /// <summary>
  163. /// Parses a <see cref="PersonInfo"/> array from the xml node.
  164. /// </summary>
  165. /// <param name="reader">The <see cref="XmlReader"/>.</param>
  166. /// <param name="personKind">The <see cref="PersonKind"/>.</param>
  167. /// <returns>The <see cref="IEnumerable{PersonInfo}"/>.</returns>
  168. public static IEnumerable<PersonInfo> GetPersonArray(this XmlReader reader, PersonKind personKind)
  169. => reader.GetStringArray()
  170. .Select(part => new PersonInfo { Name = part, Type = personKind });
  171. }