You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

397 lines
15 KiB

  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. //
  4. // Permission is hereby granted, free of charge, to any person
  5. // obtaining a copy of this software and associated documentation
  6. // files (the "Software"), to deal in the Software without
  7. // restriction, including without limitation the rights to use,
  8. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following
  11. // conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. // OTHER DEALINGS IN THE SOFTWARE.
  24. #endregion
  25. using System;
  26. using System.Collections;
  27. using System.Collections.Generic;
  28. using Newtonsoft.Json.Utilities;
  29. using System.IO;
  30. using System.Globalization;
  31. namespace Newtonsoft.Json.Linq
  32. {
  33. /// <summary>
  34. /// Represents a JSON array.
  35. /// </summary>
  36. /// <example>
  37. /// <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\LinqToJsonTests.cs" region="LinqToJsonCreateParseArray" title="Parsing a JSON Array from Text" />
  38. /// </example>
  39. internal partial class JArray : JContainer, IList<JToken>
  40. {
  41. private readonly List<JToken> _values = new List<JToken>();
  42. /// <summary>
  43. /// Gets the container's children tokens.
  44. /// </summary>
  45. /// <value>The container's children tokens.</value>
  46. protected override IList<JToken> ChildrenTokens => _values;
  47. /// <summary>
  48. /// Gets the node type for this <see cref="JToken"/>.
  49. /// </summary>
  50. /// <value>The type.</value>
  51. public override JTokenType Type => JTokenType.Array;
  52. /// <summary>
  53. /// Initializes a new instance of the <see cref="JArray"/> class.
  54. /// </summary>
  55. public JArray()
  56. {
  57. }
  58. /// <summary>
  59. /// Initializes a new instance of the <see cref="JArray"/> class from another <see cref="JArray"/> object.
  60. /// </summary>
  61. /// <param name="other">A <see cref="JArray"/> object to copy from.</param>
  62. public JArray(JArray other)
  63. : base(other)
  64. {
  65. }
  66. /// <summary>
  67. /// Initializes a new instance of the <see cref="JArray"/> class with the specified content.
  68. /// </summary>
  69. /// <param name="content">The contents of the array.</param>
  70. public JArray(params object[] content)
  71. : this((object)content)
  72. {
  73. }
  74. /// <summary>
  75. /// Initializes a new instance of the <see cref="JArray"/> class with the specified content.
  76. /// </summary>
  77. /// <param name="content">The contents of the array.</param>
  78. public JArray(object content)
  79. {
  80. Add(content);
  81. }
  82. internal override bool DeepEquals(JToken node)
  83. {
  84. return (node is JArray t && ContentsEqual(t));
  85. }
  86. internal override JToken CloneToken()
  87. {
  88. return new JArray(this);
  89. }
  90. /// <summary>
  91. /// Loads an <see cref="JArray"/> from a <see cref="JsonReader"/>.
  92. /// </summary>
  93. /// <param name="reader">A <see cref="JsonReader"/> that will be read for the content of the <see cref="JArray"/>.</param>
  94. /// <returns>A <see cref="JArray"/> that contains the JSON that was read from the specified <see cref="JsonReader"/>.</returns>
  95. public new static JArray Load(JsonReader reader)
  96. {
  97. return Load(reader, null);
  98. }
  99. /// <summary>
  100. /// Loads an <see cref="JArray"/> from a <see cref="JsonReader"/>.
  101. /// </summary>
  102. /// <param name="reader">A <see cref="JsonReader"/> that will be read for the content of the <see cref="JArray"/>.</param>
  103. /// <param name="settings">The <see cref="JsonLoadSettings"/> used to load the JSON.
  104. /// If this is <c>null</c>, default load settings will be used.</param>
  105. /// <returns>A <see cref="JArray"/> that contains the JSON that was read from the specified <see cref="JsonReader"/>.</returns>
  106. public new static JArray Load(JsonReader reader, JsonLoadSettings settings)
  107. {
  108. if (reader.TokenType == JsonToken.None)
  109. {
  110. if (!reader.Read())
  111. {
  112. throw JsonReaderException.Create(reader, "Error reading JArray from JsonReader.");
  113. }
  114. }
  115. reader.MoveToContent();
  116. if (reader.TokenType != JsonToken.StartArray)
  117. {
  118. throw JsonReaderException.Create(reader, "Error reading JArray from JsonReader. Current JsonReader item is not an array: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
  119. }
  120. JArray a = new JArray();
  121. a.SetLineInfo(reader as IJsonLineInfo, settings);
  122. a.ReadTokenFrom(reader, settings);
  123. return a;
  124. }
  125. /// <summary>
  126. /// Load a <see cref="JArray"/> from a string that contains JSON.
  127. /// </summary>
  128. /// <param name="json">A <see cref="String"/> that contains JSON.</param>
  129. /// <returns>A <see cref="JArray"/> populated from the string that contains JSON.</returns>
  130. /// <example>
  131. /// <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\LinqToJsonTests.cs" region="LinqToJsonCreateParseArray" title="Parsing a JSON Array from Text" />
  132. /// </example>
  133. public new static JArray Parse(string json)
  134. {
  135. return Parse(json, null);
  136. }
  137. /// <summary>
  138. /// Load a <see cref="JArray"/> from a string that contains JSON.
  139. /// </summary>
  140. /// <param name="json">A <see cref="String"/> that contains JSON.</param>
  141. /// <param name="settings">The <see cref="JsonLoadSettings"/> used to load the JSON.
  142. /// If this is <c>null</c>, default load settings will be used.</param>
  143. /// <returns>A <see cref="JArray"/> populated from the string that contains JSON.</returns>
  144. /// <example>
  145. /// <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\LinqToJsonTests.cs" region="LinqToJsonCreateParseArray" title="Parsing a JSON Array from Text" />
  146. /// </example>
  147. public new static JArray Parse(string json, JsonLoadSettings settings)
  148. {
  149. using (JsonReader reader = new JsonTextReader(new StringReader(json)))
  150. {
  151. JArray a = Load(reader, settings);
  152. while (reader.Read())
  153. {
  154. // Any content encountered here other than a comment will throw in the reader.
  155. }
  156. return a;
  157. }
  158. }
  159. /// <summary>
  160. /// Creates a <see cref="JArray"/> from an object.
  161. /// </summary>
  162. /// <param name="o">The object that will be used to create <see cref="JArray"/>.</param>
  163. /// <returns>A <see cref="JArray"/> with the values of the specified object.</returns>
  164. public new static JArray FromObject(object o)
  165. {
  166. return FromObject(o, JsonSerializer.CreateDefault());
  167. }
  168. /// <summary>
  169. /// Creates a <see cref="JArray"/> from an object.
  170. /// </summary>
  171. /// <param name="o">The object that will be used to create <see cref="JArray"/>.</param>
  172. /// <param name="jsonSerializer">The <see cref="JsonSerializer"/> that will be used to read the object.</param>
  173. /// <returns>A <see cref="JArray"/> with the values of the specified object.</returns>
  174. public new static JArray FromObject(object o, JsonSerializer jsonSerializer)
  175. {
  176. JToken token = FromObjectInternal(o, jsonSerializer);
  177. if (token.Type != JTokenType.Array)
  178. {
  179. throw new ArgumentException("Object serialized to {0}. JArray instance expected.".FormatWith(CultureInfo.InvariantCulture, token.Type));
  180. }
  181. return (JArray)token;
  182. }
  183. /// <summary>
  184. /// Writes this token to a <see cref="JsonWriter"/>.
  185. /// </summary>
  186. /// <param name="writer">A <see cref="JsonWriter"/> into which this method will write.</param>
  187. /// <param name="converters">A collection of <see cref="JsonConverter"/> which will be used when writing the token.</param>
  188. public override void WriteTo(JsonWriter writer, params JsonConverter[] converters)
  189. {
  190. writer.WriteStartArray();
  191. for (int i = 0; i < _values.Count; i++)
  192. {
  193. _values[i].WriteTo(writer, converters);
  194. }
  195. writer.WriteEndArray();
  196. }
  197. /// <summary>
  198. /// Gets the <see cref="JToken"/> with the specified key.
  199. /// </summary>
  200. /// <value>The <see cref="JToken"/> with the specified key.</value>
  201. public override JToken this[object key]
  202. {
  203. get
  204. {
  205. ValidationUtils.ArgumentNotNull(key, nameof(key));
  206. if (!(key is int))
  207. {
  208. throw new ArgumentException("Accessed JArray values with invalid key value: {0}. Int32 array index expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key)));
  209. }
  210. return GetItem((int)key);
  211. }
  212. set
  213. {
  214. ValidationUtils.ArgumentNotNull(key, nameof(key));
  215. if (!(key is int))
  216. {
  217. throw new ArgumentException("Set JArray values with invalid key value: {0}. Int32 array index expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key)));
  218. }
  219. SetItem((int)key, value);
  220. }
  221. }
  222. /// <summary>
  223. /// Gets or sets the <see cref="Newtonsoft.Json.Linq.JToken"/> at the specified index.
  224. /// </summary>
  225. /// <value></value>
  226. public JToken this[int index]
  227. {
  228. get => GetItem(index);
  229. set => SetItem(index, value);
  230. }
  231. internal override int IndexOfItem(JToken item)
  232. {
  233. return _values.IndexOfReference(item);
  234. }
  235. internal override void MergeItem(object content, JsonMergeSettings settings)
  236. {
  237. IEnumerable a = (IsMultiContent(content) || content is JArray)
  238. ? (IEnumerable)content
  239. : null;
  240. if (a == null)
  241. {
  242. return;
  243. }
  244. MergeEnumerableContent(this, a, settings);
  245. }
  246. #region IList<JToken> Members
  247. /// <summary>
  248. /// Determines the index of a specific item in the <see cref="JArray"/>.
  249. /// </summary>
  250. /// <param name="item">The object to locate in the <see cref="JArray"/>.</param>
  251. /// <returns>
  252. /// The index of <paramref name="item"/> if found in the list; otherwise, -1.
  253. /// </returns>
  254. public int IndexOf(JToken item)
  255. {
  256. return IndexOfItem(item);
  257. }
  258. /// <summary>
  259. /// Inserts an item to the <see cref="JArray"/> at the specified index.
  260. /// </summary>
  261. /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
  262. /// <param name="item">The object to insert into the <see cref="JArray"/>.</param>
  263. /// <exception cref="ArgumentOutOfRangeException">
  264. /// <paramref name="index"/> is not a valid index in the <see cref="JArray"/>.
  265. /// </exception>
  266. public void Insert(int index, JToken item)
  267. {
  268. InsertItem(index, item, false);
  269. }
  270. /// <summary>
  271. /// Removes the <see cref="JArray"/> item at the specified index.
  272. /// </summary>
  273. /// <param name="index">The zero-based index of the item to remove.</param>
  274. /// <exception cref="ArgumentOutOfRangeException">
  275. /// <paramref name="index"/> is not a valid index in the <see cref="JArray"/>.
  276. /// </exception>
  277. public void RemoveAt(int index)
  278. {
  279. RemoveItemAt(index);
  280. }
  281. /// <summary>
  282. /// Returns an enumerator that iterates through the collection.
  283. /// </summary>
  284. /// <returns>
  285. /// A <see cref="IEnumerator{T}"/> of <see cref="JToken"/> that can be used to iterate through the collection.
  286. /// </returns>
  287. public IEnumerator<JToken> GetEnumerator()
  288. {
  289. return Children().GetEnumerator();
  290. }
  291. #endregion
  292. #region ICollection<JToken> Members
  293. /// <summary>
  294. /// Adds an item to the <see cref="JArray"/>.
  295. /// </summary>
  296. /// <param name="item">The object to add to the <see cref="JArray"/>.</param>
  297. public void Add(JToken item)
  298. {
  299. Add((object)item);
  300. }
  301. /// <summary>
  302. /// Removes all items from the <see cref="JArray"/>.
  303. /// </summary>
  304. public void Clear()
  305. {
  306. ClearItems();
  307. }
  308. /// <summary>
  309. /// Determines whether the <see cref="JArray"/> contains a specific value.
  310. /// </summary>
  311. /// <param name="item">The object to locate in the <see cref="JArray"/>.</param>
  312. /// <returns>
  313. /// <c>true</c> if <paramref name="item"/> is found in the <see cref="JArray"/>; otherwise, <c>false</c>.
  314. /// </returns>
  315. public bool Contains(JToken item)
  316. {
  317. return ContainsItem(item);
  318. }
  319. /// <summary>
  320. /// Copies the elements of the <see cref="JArray"/> to an array, starting at a particular array index.
  321. /// </summary>
  322. /// <param name="array">The array.</param>
  323. /// <param name="arrayIndex">Index of the array.</param>
  324. public void CopyTo(JToken[] array, int arrayIndex)
  325. {
  326. CopyItemsTo(array, arrayIndex);
  327. }
  328. /// <summary>
  329. /// Gets a value indicating whether the <see cref="JArray"/> is read-only.
  330. /// </summary>
  331. /// <returns><c>true</c> if the <see cref="JArray"/> is read-only; otherwise, <c>false</c>.</returns>
  332. public bool IsReadOnly => false;
  333. /// <summary>
  334. /// Removes the first occurrence of a specific object from the <see cref="JArray"/>.
  335. /// </summary>
  336. /// <param name="item">The object to remove from the <see cref="JArray"/>.</param>
  337. /// <returns>
  338. /// <c>true</c> if <paramref name="item"/> was successfully removed from the <see cref="JArray"/>; otherwise, <c>false</c>. This method also returns <c>false</c> if <paramref name="item"/> is not found in the original <see cref="JArray"/>.
  339. /// </returns>
  340. public bool Remove(JToken item)
  341. {
  342. return RemoveItem(item);
  343. }
  344. #endregion
  345. internal override int GetDeepHashCode()
  346. {
  347. return ContentsHashCode();
  348. }
  349. }
  350. }