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.

1154 lines
36 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.Generic;
  27. #if !NET20
  28. using System.Collections.Specialized;
  29. #endif
  30. using System.Threading;
  31. using Newtonsoft.Json.Utilities;
  32. using System.Collections;
  33. using System.Globalization;
  34. using System.ComponentModel;
  35. #if NET20
  36. using Newtonsoft.Json.Utilities.LinqBridge;
  37. #else
  38. using System.Linq;
  39. #endif
  40. namespace Newtonsoft.Json.Linq
  41. {
  42. /// <summary>
  43. /// Represents a token that can contain other tokens.
  44. /// </summary>
  45.     internal abstract partial class JContainer : JToken, IList<JToken>
  46. , ITypedList, IBindingList
  47. , IList
  48. #if !NET20
  49. , INotifyCollectionChanged
  50. #endif
  51. {
  52. internal ListChangedEventHandler _listChanged;
  53. internal AddingNewEventHandler _addingNew;
  54. /// <summary>
  55. /// Occurs when the list changes or an item in the list changes.
  56. /// </summary>
  57. public event ListChangedEventHandler ListChanged
  58. {
  59. add => _listChanged += value;
  60. remove => _listChanged -= value;
  61. }
  62. /// <summary>
  63. /// Occurs before an item is added to the collection.
  64. /// </summary>
  65. public event AddingNewEventHandler AddingNew
  66. {
  67. add => _addingNew += value;
  68. remove => _addingNew -= value;
  69. }
  70. #if !NET20
  71. internal NotifyCollectionChangedEventHandler _collectionChanged;
  72. /// <summary>
  73. /// Occurs when the items list of the collection has changed, or the collection is reset.
  74. /// </summary>
  75. public event NotifyCollectionChangedEventHandler CollectionChanged
  76. {
  77. add { _collectionChanged += value; }
  78. remove { _collectionChanged -= value; }
  79. }
  80. #endif
  81. /// <summary>
  82. /// Gets the container's children tokens.
  83. /// </summary>
  84. /// <value>The container's children tokens.</value>
  85. protected abstract IList<JToken> ChildrenTokens { get; }
  86. private object _syncRoot;
  87. private bool _busy;
  88. internal JContainer()
  89. {
  90. }
  91. internal JContainer(JContainer other)
  92. : this()
  93. {
  94. ValidationUtils.ArgumentNotNull(other, nameof(other));
  95. int i = 0;
  96. foreach (JToken child in other)
  97. {
  98. AddInternal(i, child, false);
  99. i++;
  100. }
  101. }
  102. internal void CheckReentrancy()
  103. {
  104. if (_busy)
  105. {
  106. throw new InvalidOperationException("Cannot change {0} during a collection change event.".FormatWith(CultureInfo.InvariantCulture, GetType()));
  107. }
  108. }
  109. internal virtual IList<JToken> CreateChildrenCollection()
  110. {
  111. return new List<JToken>();
  112. }
  113. /// <summary>
  114. /// Raises the <see cref="AddingNew"/> event.
  115. /// </summary>
  116. /// <param name="e">The <see cref="AddingNewEventArgs"/> instance containing the event data.</param>
  117. protected virtual void OnAddingNew(AddingNewEventArgs e)
  118. {
  119. _addingNew?.Invoke(this, e);
  120. }
  121. /// <summary>
  122. /// Raises the <see cref="ListChanged"/> event.
  123. /// </summary>
  124. /// <param name="e">The <see cref="ListChangedEventArgs"/> instance containing the event data.</param>
  125. protected virtual void OnListChanged(ListChangedEventArgs e)
  126. {
  127. ListChangedEventHandler handler = _listChanged;
  128. if (handler != null)
  129. {
  130. _busy = true;
  131. try
  132. {
  133. handler(this, e);
  134. }
  135. finally
  136. {
  137. _busy = false;
  138. }
  139. }
  140. }
  141. #if !NET20
  142. /// <summary>
  143. /// Raises the <see cref="CollectionChanged"/> event.
  144. /// </summary>
  145. /// <param name="e">The <see cref="NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
  146. protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
  147. {
  148. NotifyCollectionChangedEventHandler handler = _collectionChanged;
  149. if (handler != null)
  150. {
  151. _busy = true;
  152. try
  153. {
  154. handler(this, e);
  155. }
  156. finally
  157. {
  158. _busy = false;
  159. }
  160. }
  161. }
  162. #endif
  163. /// <summary>
  164. /// Gets a value indicating whether this token has child tokens.
  165. /// </summary>
  166. /// <value>
  167. /// <c>true</c> if this token has child values; otherwise, <c>false</c>.
  168. /// </value>
  169. public override bool HasValues => ChildrenTokens.Count > 0;
  170. internal bool ContentsEqual(JContainer container)
  171. {
  172. if (container == this)
  173. {
  174. return true;
  175. }
  176. IList<JToken> t1 = ChildrenTokens;
  177. IList<JToken> t2 = container.ChildrenTokens;
  178. if (t1.Count != t2.Count)
  179. {
  180. return false;
  181. }
  182. for (int i = 0; i < t1.Count; i++)
  183. {
  184. if (!t1[i].DeepEquals(t2[i]))
  185. {
  186. return false;
  187. }
  188. }
  189. return true;
  190. }
  191. /// <summary>
  192. /// Get the first child token of this token.
  193. /// </summary>
  194. /// <value>
  195. /// A <see cref="JToken"/> containing the first child token of the <see cref="JToken"/>.
  196. /// </value>
  197. public override JToken First
  198. {
  199. get
  200. {
  201. IList<JToken> children = ChildrenTokens;
  202. return (children.Count > 0) ? children[0] : null;
  203. }
  204. }
  205. /// <summary>
  206. /// Get the last child token of this token.
  207. /// </summary>
  208. /// <value>
  209. /// A <see cref="JToken"/> containing the last child token of the <see cref="JToken"/>.
  210. /// </value>
  211. public override JToken Last
  212. {
  213. get
  214. {
  215. IList<JToken> children = ChildrenTokens;
  216. int count = children.Count;
  217. return (count > 0) ? children[count - 1] : null;
  218. }
  219. }
  220. /// <summary>
  221. /// Returns a collection of the child tokens of this token, in document order.
  222. /// </summary>
  223. /// <returns>
  224. /// An <see cref="IEnumerable{T}"/> of <see cref="JToken"/> containing the child tokens of this <see cref="JToken"/>, in document order.
  225. /// </returns>
  226. public override JEnumerable<JToken> Children()
  227. {
  228. return new JEnumerable<JToken>(ChildrenTokens);
  229. }
  230. /// <summary>
  231. /// Returns a collection of the child values of this token, in document order.
  232. /// </summary>
  233. /// <typeparam name="T">The type to convert the values to.</typeparam>
  234. /// <returns>
  235. /// A <see cref="IEnumerable{T}"/> containing the child values of this <see cref="JToken"/>, in document order.
  236. /// </returns>
  237. public override IEnumerable<T> Values<T>()
  238. {
  239. return ChildrenTokens.Convert<JToken, T>();
  240. }
  241. /// <summary>
  242. /// Returns a collection of the descendant tokens for this token in document order.
  243. /// </summary>
  244. /// <returns>An <see cref="IEnumerable{T}"/> of <see cref="JToken"/> containing the descendant tokens of the <see cref="JToken"/>.</returns>
  245. public IEnumerable<JToken> Descendants()
  246. {
  247. return GetDescendants(false);
  248. }
  249. /// <summary>
  250. /// Returns a collection of the tokens that contain this token, and all descendant tokens of this token, in document order.
  251. /// </summary>
  252. /// <returns>An <see cref="IEnumerable{T}"/> of <see cref="JToken"/> containing this token, and all the descendant tokens of the <see cref="JToken"/>.</returns>
  253. public IEnumerable<JToken> DescendantsAndSelf()
  254. {
  255. return GetDescendants(true);
  256. }
  257. internal IEnumerable<JToken> GetDescendants(bool self)
  258. {
  259. if (self)
  260. {
  261. yield return this;
  262. }
  263. foreach (JToken o in ChildrenTokens)
  264. {
  265. yield return o;
  266. if (o is JContainer c)
  267. {
  268. foreach (JToken d in c.Descendants())
  269. {
  270. yield return d;
  271. }
  272. }
  273. }
  274. }
  275. internal bool IsMultiContent(object content)
  276. {
  277. return (content is IEnumerable && !(content is string) && !(content is JToken) && !(content is byte[]));
  278. }
  279. internal JToken EnsureParentToken(JToken item, bool skipParentCheck)
  280. {
  281. if (item == null)
  282. {
  283. return JValue.CreateNull();
  284. }
  285. if (skipParentCheck)
  286. {
  287. return item;
  288. }
  289. // to avoid a token having multiple parents or creating a recursive loop, create a copy if...
  290. // the item already has a parent
  291. // the item is being added to itself
  292. // the item is being added to the root parent of itself
  293. if (item.Parent != null || item == this || (item.HasValues && Root == item))
  294. {
  295. item = item.CloneToken();
  296. }
  297. return item;
  298. }
  299. internal abstract int IndexOfItem(JToken item);
  300. internal virtual void InsertItem(int index, JToken item, bool skipParentCheck)
  301. {
  302. IList<JToken> children = ChildrenTokens;
  303. if (index > children.Count)
  304. {
  305. throw new ArgumentOutOfRangeException(nameof(index), "Index must be within the bounds of the List.");
  306. }
  307. CheckReentrancy();
  308. item = EnsureParentToken(item, skipParentCheck);
  309. JToken previous = (index == 0) ? null : children[index - 1];
  310. // haven't inserted new token yet so next token is still at the inserting index
  311. JToken next = (index == children.Count) ? null : children[index];
  312. ValidateToken(item, null);
  313. item.Parent = this;
  314. item.Previous = previous;
  315. if (previous != null)
  316. {
  317. previous.Next = item;
  318. }
  319. item.Next = next;
  320. if (next != null)
  321. {
  322. next.Previous = item;
  323. }
  324. children.Insert(index, item);
  325. if (_listChanged != null)
  326. {
  327. OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, index));
  328. }
  329. #if !NET20
  330. if (_collectionChanged != null)
  331. {
  332. OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
  333. }
  334. #endif
  335. }
  336. internal virtual void RemoveItemAt(int index)
  337. {
  338. IList<JToken> children = ChildrenTokens;
  339. if (index < 0)
  340. {
  341. throw new ArgumentOutOfRangeException(nameof(index), "Index is less than 0.");
  342. }
  343. if (index >= children.Count)
  344. {
  345. throw new ArgumentOutOfRangeException(nameof(index), "Index is equal to or greater than Count.");
  346. }
  347. CheckReentrancy();
  348. JToken item = children[index];
  349. JToken previous = (index == 0) ? null : children[index - 1];
  350. JToken next = (index == children.Count - 1) ? null : children[index + 1];
  351. if (previous != null)
  352. {
  353. previous.Next = next;
  354. }
  355. if (next != null)
  356. {
  357. next.Previous = previous;
  358. }
  359. item.Parent = null;
  360. item.Previous = null;
  361. item.Next = null;
  362. children.RemoveAt(index);
  363. if (_listChanged != null)
  364. {
  365. OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
  366. }
  367. #if !NET20
  368. if (_collectionChanged != null)
  369. {
  370. OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
  371. }
  372. #endif
  373. }
  374. internal virtual bool RemoveItem(JToken item)
  375. {
  376. int index = IndexOfItem(item);
  377. if (index >= 0)
  378. {
  379. RemoveItemAt(index);
  380. return true;
  381. }
  382. return false;
  383. }
  384. internal virtual JToken GetItem(int index)
  385. {
  386. return ChildrenTokens[index];
  387. }
  388. internal virtual void SetItem(int index, JToken item)
  389. {
  390. IList<JToken> children = ChildrenTokens;
  391. if (index < 0)
  392. {
  393. throw new ArgumentOutOfRangeException(nameof(index), "Index is less than 0.");
  394. }
  395. if (index >= children.Count)
  396. {
  397. throw new ArgumentOutOfRangeException(nameof(index), "Index is equal to or greater than Count.");
  398. }
  399. JToken existing = children[index];
  400. if (IsTokenUnchanged(existing, item))
  401. {
  402. return;
  403. }
  404. CheckReentrancy();
  405. item = EnsureParentToken(item, false);
  406. ValidateToken(item, existing);
  407. JToken previous = (index == 0) ? null : children[index - 1];
  408. JToken next = (index == children.Count - 1) ? null : children[index + 1];
  409. item.Parent = this;
  410. item.Previous = previous;
  411. if (previous != null)
  412. {
  413. previous.Next = item;
  414. }
  415. item.Next = next;
  416. if (next != null)
  417. {
  418. next.Previous = item;
  419. }
  420. children[index] = item;
  421. existing.Parent = null;
  422. existing.Previous = null;
  423. existing.Next = null;
  424. if (_listChanged != null)
  425. {
  426. OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));
  427. }
  428. #if !NET20
  429. if (_collectionChanged != null)
  430. {
  431. OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, existing, index));
  432. }
  433. #endif
  434. }
  435. internal virtual void ClearItems()
  436. {
  437. CheckReentrancy();
  438. IList<JToken> children = ChildrenTokens;
  439. foreach (JToken item in children)
  440. {
  441. item.Parent = null;
  442. item.Previous = null;
  443. item.Next = null;
  444. }
  445. children.Clear();
  446. if (_listChanged != null)
  447. {
  448. OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
  449. }
  450. #if !NET20
  451. if (_collectionChanged != null)
  452. {
  453. OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  454. }
  455. #endif
  456. }
  457. internal virtual void ReplaceItem(JToken existing, JToken replacement)
  458. {
  459. if (existing == null || existing.Parent != this)
  460. {
  461. return;
  462. }
  463. int index = IndexOfItem(existing);
  464. SetItem(index, replacement);
  465. }
  466. internal virtual bool ContainsItem(JToken item)
  467. {
  468. return (IndexOfItem(item) != -1);
  469. }
  470. internal virtual void CopyItemsTo(Array array, int arrayIndex)
  471. {
  472. if (array == null)
  473. {
  474. throw new ArgumentNullException(nameof(array));
  475. }
  476. if (arrayIndex < 0)
  477. {
  478. throw new ArgumentOutOfRangeException(nameof(arrayIndex), "arrayIndex is less than 0.");
  479. }
  480. if (arrayIndex >= array.Length && arrayIndex != 0)
  481. {
  482. throw new ArgumentException("arrayIndex is equal to or greater than the length of array.");
  483. }
  484. if (Count > array.Length - arrayIndex)
  485. {
  486. throw new ArgumentException("The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array.");
  487. }
  488. int index = 0;
  489. foreach (JToken token in ChildrenTokens)
  490. {
  491. array.SetValue(token, arrayIndex + index);
  492. index++;
  493. }
  494. }
  495. internal static bool IsTokenUnchanged(JToken currentValue, JToken newValue)
  496. {
  497. if (currentValue is JValue v1)
  498. {
  499. // null will get turned into a JValue of type null
  500. if (v1.Type == JTokenType.Null && newValue == null)
  501. {
  502. return true;
  503. }
  504. return v1.Equals(newValue);
  505. }
  506. return false;
  507. }
  508. internal virtual void ValidateToken(JToken o, JToken existing)
  509. {
  510. ValidationUtils.ArgumentNotNull(o, nameof(o));
  511. if (o.Type == JTokenType.Property)
  512. {
  513. throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType()));
  514. }
  515. }
  516. /// <summary>
  517. /// Adds the specified content as children of this <see cref="JToken"/>.
  518. /// </summary>
  519. /// <param name="content">The content to be added.</param>
  520. public virtual void Add(object content)
  521. {
  522. AddInternal(ChildrenTokens.Count, content, false);
  523. }
  524. internal void AddAndSkipParentCheck(JToken token)
  525. {
  526. AddInternal(ChildrenTokens.Count, token, true);
  527. }
  528. /// <summary>
  529. /// Adds the specified content as the first children of this <see cref="JToken"/>.
  530. /// </summary>
  531. /// <param name="content">The content to be added.</param>
  532. public void AddFirst(object content)
  533. {
  534. AddInternal(0, content, false);
  535. }
  536. internal void AddInternal(int index, object content, bool skipParentCheck)
  537. {
  538. if (IsMultiContent(content))
  539. {
  540. IEnumerable enumerable = (IEnumerable)content;
  541. int multiIndex = index;
  542. foreach (object c in enumerable)
  543. {
  544. AddInternal(multiIndex, c, skipParentCheck);
  545. multiIndex++;
  546. }
  547. }
  548. else
  549. {
  550. JToken item = CreateFromContent(content);
  551. InsertItem(index, item, skipParentCheck);
  552. }
  553. }
  554. internal static JToken CreateFromContent(object content)
  555. {
  556. if (content is JToken token)
  557. {
  558. return token;
  559. }
  560. return new JValue(content);
  561. }
  562. /// <summary>
  563. /// Creates a <see cref="JsonWriter"/> that can be used to add tokens to the <see cref="JToken"/>.
  564. /// </summary>
  565. /// <returns>A <see cref="JsonWriter"/> that is ready to have content written to it.</returns>
  566. public JsonWriter CreateWriter()
  567. {
  568. return new JTokenWriter(this);
  569. }
  570. /// <summary>
  571. /// Replaces the child nodes of this token with the specified content.
  572. /// </summary>
  573. /// <param name="content">The content.</param>
  574. public void ReplaceAll(object content)
  575. {
  576. ClearItems();
  577. Add(content);
  578. }
  579. /// <summary>
  580. /// Removes the child nodes from this token.
  581. /// </summary>
  582. public void RemoveAll()
  583. {
  584. ClearItems();
  585. }
  586. internal abstract void MergeItem(object content, JsonMergeSettings settings);
  587. /// <summary>
  588. /// Merge the specified content into this <see cref="JToken"/>.
  589. /// </summary>
  590. /// <param name="content">The content to be merged.</param>
  591. public void Merge(object content)
  592. {
  593. MergeItem(content, new JsonMergeSettings());
  594. }
  595. /// <summary>
  596. /// Merge the specified content into this <see cref="JToken"/> using <see cref="JsonMergeSettings"/>.
  597. /// </summary>
  598. /// <param name="content">The content to be merged.</param>
  599. /// <param name="settings">The <see cref="JsonMergeSettings"/> used to merge the content.</param>
  600. public void Merge(object content, JsonMergeSettings settings)
  601. {
  602. MergeItem(content, settings);
  603. }
  604. internal void ReadTokenFrom(JsonReader reader, JsonLoadSettings options)
  605. {
  606. int startDepth = reader.Depth;
  607. if (!reader.Read())
  608. {
  609. throw JsonReaderException.Create(reader, "Error reading {0} from JsonReader.".FormatWith(CultureInfo.InvariantCulture, GetType().Name));
  610. }
  611. ReadContentFrom(reader, options);
  612. int endDepth = reader.Depth;
  613. if (endDepth > startDepth)
  614. {
  615. throw JsonReaderException.Create(reader, "Unexpected end of content while loading {0}.".FormatWith(CultureInfo.InvariantCulture, GetType().Name));
  616. }
  617. }
  618. internal void ReadContentFrom(JsonReader r, JsonLoadSettings settings)
  619. {
  620. ValidationUtils.ArgumentNotNull(r, nameof(r));
  621. IJsonLineInfo lineInfo = r as IJsonLineInfo;
  622. JContainer parent = this;
  623. do
  624. {
  625. if ((parent as JProperty)?.Value != null)
  626. {
  627. if (parent == this)
  628. {
  629. return;
  630. }
  631. parent = parent.Parent;
  632. }
  633. switch (r.TokenType)
  634. {
  635. case JsonToken.None:
  636. // new reader. move to actual content
  637. break;
  638. case JsonToken.StartArray:
  639. JArray a = new JArray();
  640. a.SetLineInfo(lineInfo, settings);
  641. parent.Add(a);
  642. parent = a;
  643. break;
  644. case JsonToken.EndArray:
  645. if (parent == this)
  646. {
  647. return;
  648. }
  649. parent = parent.Parent;
  650. break;
  651. case JsonToken.StartObject:
  652. JObject o = new JObject();
  653. o.SetLineInfo(lineInfo, settings);
  654. parent.Add(o);
  655. parent = o;
  656. break;
  657. case JsonToken.EndObject:
  658. if (parent == this)
  659. {
  660. return;
  661. }
  662. parent = parent.Parent;
  663. break;
  664. case JsonToken.StartConstructor:
  665. JConstructor constructor = new JConstructor(r.Value.ToString());
  666. constructor.SetLineInfo(lineInfo, settings);
  667. parent.Add(constructor);
  668. parent = constructor;
  669. break;
  670. case JsonToken.EndConstructor:
  671. if (parent == this)
  672. {
  673. return;
  674. }
  675. parent = parent.Parent;
  676. break;
  677. case JsonToken.String:
  678. case JsonToken.Integer:
  679. case JsonToken.Float:
  680. case JsonToken.Date:
  681. case JsonToken.Boolean:
  682. case JsonToken.Bytes:
  683. JValue v = new JValue(r.Value);
  684. v.SetLineInfo(lineInfo, settings);
  685. parent.Add(v);
  686. break;
  687. case JsonToken.Comment:
  688. if (settings != null && settings.CommentHandling == CommentHandling.Load)
  689. {
  690. v = JValue.CreateComment(r.Value.ToString());
  691. v.SetLineInfo(lineInfo, settings);
  692. parent.Add(v);
  693. }
  694. break;
  695. case JsonToken.Null:
  696. v = JValue.CreateNull();
  697. v.SetLineInfo(lineInfo, settings);
  698. parent.Add(v);
  699. break;
  700. case JsonToken.Undefined:
  701. v = JValue.CreateUndefined();
  702. v.SetLineInfo(lineInfo, settings);
  703. parent.Add(v);
  704. break;
  705. case JsonToken.PropertyName:
  706. string propertyName = r.Value.ToString();
  707. JProperty property = new JProperty(propertyName);
  708. property.SetLineInfo(lineInfo, settings);
  709. JObject parentObject = (JObject)parent;
  710. // handle multiple properties with the same name in JSON
  711. JProperty existingPropertyWithName = parentObject.Property(propertyName);
  712. if (existingPropertyWithName == null)
  713. {
  714. parent.Add(property);
  715. }
  716. else
  717. {
  718. existingPropertyWithName.Replace(property);
  719. }
  720. parent = property;
  721. break;
  722. default:
  723. throw new InvalidOperationException("The JsonReader should not be on a token of type {0}.".FormatWith(CultureInfo.InvariantCulture, r.TokenType));
  724. }
  725. } while (r.Read());
  726. }
  727. internal int ContentsHashCode()
  728. {
  729. int hashCode = 0;
  730. foreach (JToken item in ChildrenTokens)
  731. {
  732. hashCode ^= item.GetDeepHashCode();
  733. }
  734. return hashCode;
  735. }
  736. string ITypedList.GetListName(PropertyDescriptor[] listAccessors)
  737. {
  738. return string.Empty;
  739. }
  740. PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
  741. {
  742. ICustomTypeDescriptor d = First as ICustomTypeDescriptor;
  743. return d?.GetProperties();
  744. }
  745. #region IList<JToken> Members
  746. int IList<JToken>.IndexOf(JToken item)
  747. {
  748. return IndexOfItem(item);
  749. }
  750. void IList<JToken>.Insert(int index, JToken item)
  751. {
  752. InsertItem(index, item, false);
  753. }
  754. void IList<JToken>.RemoveAt(int index)
  755. {
  756. RemoveItemAt(index);
  757. }
  758. JToken IList<JToken>.this[int index]
  759. {
  760. get => GetItem(index);
  761. set => SetItem(index, value);
  762. }
  763. #endregion
  764. #region ICollection<JToken> Members
  765. void ICollection<JToken>.Add(JToken item)
  766. {
  767. Add(item);
  768. }
  769. void ICollection<JToken>.Clear()
  770. {
  771. ClearItems();
  772. }
  773. bool ICollection<JToken>.Contains(JToken item)
  774. {
  775. return ContainsItem(item);
  776. }
  777. void ICollection<JToken>.CopyTo(JToken[] array, int arrayIndex)
  778. {
  779. CopyItemsTo(array, arrayIndex);
  780. }
  781. bool ICollection<JToken>.IsReadOnly => false;
  782. bool ICollection<JToken>.Remove(JToken item)
  783. {
  784. return RemoveItem(item);
  785. }
  786. #endregion
  787. private JToken EnsureValue(object value)
  788. {
  789. if (value == null)
  790. {
  791. return null;
  792. }
  793. if (value is JToken token)
  794. {
  795. return token;
  796. }
  797. throw new ArgumentException("Argument is not a JToken.");
  798. }
  799. #region IList Members
  800. int IList.Add(object value)
  801. {
  802. Add(EnsureValue(value));
  803. return Count - 1;
  804. }
  805. void IList.Clear()
  806. {
  807. ClearItems();
  808. }
  809. bool IList.Contains(object value)
  810. {
  811. return ContainsItem(EnsureValue(value));
  812. }
  813. int IList.IndexOf(object value)
  814. {
  815. return IndexOfItem(EnsureValue(value));
  816. }
  817. void IList.Insert(int index, object value)
  818. {
  819. InsertItem(index, EnsureValue(value), false);
  820. }
  821. bool IList.IsFixedSize => false;
  822. bool IList.IsReadOnly => false;
  823. void IList.Remove(object value)
  824. {
  825. RemoveItem(EnsureValue(value));
  826. }
  827. void IList.RemoveAt(int index)
  828. {
  829. RemoveItemAt(index);
  830. }
  831. object IList.this[int index]
  832. {
  833. get => GetItem(index);
  834. set => SetItem(index, EnsureValue(value));
  835. }
  836. #endregion
  837. #region ICollection Members
  838. void ICollection.CopyTo(Array array, int index)
  839. {
  840. CopyItemsTo(array, index);
  841. }
  842. /// <summary>
  843. /// Gets the count of child JSON tokens.
  844. /// </summary>
  845. /// <value>The count of child JSON tokens.</value>
  846. public int Count => ChildrenTokens.Count;
  847. bool ICollection.IsSynchronized => false;
  848. object ICollection.SyncRoot
  849. {
  850. get
  851. {
  852. if (_syncRoot == null)
  853. {
  854. Interlocked.CompareExchange(ref _syncRoot, new object(), null);
  855. }
  856. return _syncRoot;
  857. }
  858. }
  859. #endregion
  860. #region IBindingList Members
  861. void IBindingList.AddIndex(PropertyDescriptor property)
  862. {
  863. }
  864. object IBindingList.AddNew()
  865. {
  866. AddingNewEventArgs args = new AddingNewEventArgs();
  867. OnAddingNew(args);
  868. if (args.NewObject == null)
  869. {
  870. throw new JsonException("Could not determine new value to add to '{0}'.".FormatWith(CultureInfo.InvariantCulture, GetType()));
  871. }
  872. if (!(args.NewObject is JToken))
  873. {
  874. throw new JsonException("New item to be added to collection must be compatible with {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JToken)));
  875. }
  876. JToken newItem = (JToken)args.NewObject;
  877. Add(newItem);
  878. return newItem;
  879. }
  880. bool IBindingList.AllowEdit => true;
  881. bool IBindingList.AllowNew => true;
  882. bool IBindingList.AllowRemove => true;
  883. void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction)
  884. {
  885. throw new NotSupportedException();
  886. }
  887. int IBindingList.Find(PropertyDescriptor property, object key)
  888. {
  889. throw new NotSupportedException();
  890. }
  891. bool IBindingList.IsSorted => false;
  892. void IBindingList.RemoveIndex(PropertyDescriptor property)
  893. {
  894. }
  895. void IBindingList.RemoveSort()
  896. {
  897. throw new NotSupportedException();
  898. }
  899. ListSortDirection IBindingList.SortDirection => ListSortDirection.Ascending;
  900. PropertyDescriptor IBindingList.SortProperty => null;
  901. bool IBindingList.SupportsChangeNotification => true;
  902. bool IBindingList.SupportsSearching => false;
  903. bool IBindingList.SupportsSorting => false;
  904. #endregion
  905. internal static void MergeEnumerableContent(JContainer target, IEnumerable content, JsonMergeSettings settings)
  906. {
  907. switch (settings.MergeArrayHandling)
  908. {
  909. case MergeArrayHandling.Concat:
  910. foreach (JToken item in content)
  911. {
  912. target.Add(item);
  913. }
  914. break;
  915. case MergeArrayHandling.Union:
  916. #if !NET20
  917. HashSet<JToken> items = new HashSet<JToken>(target, EqualityComparer);
  918. foreach (JToken item in content)
  919. {
  920. if (items.Add(item))
  921. {
  922. target.Add(item);
  923. }
  924. }
  925. #else
  926. Dictionary<JToken, bool> items = new Dictionary<JToken, bool>(EqualityComparer);
  927. foreach (JToken t in target)
  928. {
  929. items[t] = true;
  930. }
  931. foreach (JToken item in content)
  932. {
  933. if (!items.ContainsKey(item))
  934. {
  935. items[item] = true;
  936. target.Add(item);
  937. }
  938. }
  939. #endif
  940. break;
  941. case MergeArrayHandling.Replace:
  942. target.ClearItems();
  943. foreach (JToken item in content)
  944. {
  945. target.Add(item);
  946. }
  947. break;
  948. case MergeArrayHandling.Merge:
  949. int i = 0;
  950. foreach (object targetItem in content)
  951. {
  952. if (i < target.Count)
  953. {
  954. JToken sourceItem = target[i];
  955. if (sourceItem is JContainer existingContainer)
  956. {
  957. existingContainer.Merge(targetItem, settings);
  958. }
  959. else
  960. {
  961. if (targetItem != null)
  962. {
  963. JToken contentValue = CreateFromContent(targetItem);
  964. if (contentValue.Type != JTokenType.Null)
  965. {
  966. target[i] = contentValue;
  967. }
  968. }
  969. }
  970. }
  971. else
  972. {
  973. target.Add(targetItem);
  974. }
  975. i++;
  976. }
  977. break;
  978. default:
  979. throw new ArgumentOutOfRangeException(nameof(settings), "Unexpected merge array handling when merging JSON.");
  980. }
  981. }
  982. }
  983. }