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