// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.Serialization; using System.Xml; namespace System.Json { /// /// A JsonArray is an ordered sequence of zero or more objects. /// [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "Array already conveys the meaning of collection")] [DataContract] public sealed class JsonArray : JsonValue, IList { [DataMember] private List values = new List(); /// /// Creates an instance of the class initialized by /// an enumeration of /// objects of type . /// /// The enumeration /// of objects of type used to initialize the JavaScript Object Notation (JSON) /// array. /// If items is null. /// If any of the items in the collection /// is a with property of /// value . public JsonArray(IEnumerable items) { AddRange(items); } /// /// Creates an instance of the class, initialized by an array of type . /// /// The array of type used to initialize the /// JavaScript Object Notation (JSON) array. /// If any of the items in the collection /// is a with property of /// value . public JsonArray(params JsonValue[] items) { if (items != null) { AddRange(items); } } /// /// Gets the JSON type of this . The return value /// is always . /// public override JsonType JsonType { get { return JsonType.Array; } } /// /// Gets a value indicating whether the is read-only. /// public bool IsReadOnly { get { return ((IList)values).IsReadOnly; } } /// /// Returns the number of elements in the array. /// public override int Count { get { return values.Count; } } /// /// Gets or sets the JSON value at a specified index. /// /// The zero-based index of the element to get or set. /// The element at the specified index. /// If index is not a valid index for this array. /// The property is set and the value is a /// with /// property of value . public override JsonValue this[int index] { get { return values[index]; } set { if (value != null && value.JsonType == JsonType.Default) { throw new ArgumentNullException("value", Properties.Resources.UseOfDefaultNotAllowed); } JsonValue oldValue = values[index]; RaiseItemChanging(value, JsonValueChange.Replace, index); values[index] = value; RaiseItemChanged(oldValue, JsonValueChange.Replace, index); } } /// /// Adds the elements from a collection of type to this instance. /// /// Collection of items to add. /// If items is null. /// If any of the items in the collection /// is a with property of /// value . public void AddRange(IEnumerable items) { if (items == null) { throw new ArgumentNullException("items"); } if (ChangingListenersCount > 0) { int index = Count; foreach (JsonValue toBeAdded in items) { RaiseItemChanging(toBeAdded, JsonValueChange.Add, index++); } } foreach (JsonValue item in items) { if (item != null && item.JsonType == JsonType.Default) { throw new ArgumentNullException("items", Properties.Resources.UseOfDefaultNotAllowed); } values.Add(item); RaiseItemChanged(item, JsonValueChange.Add, values.Count - 1); } } /// /// Adds the elements from an array of type to this instance. /// /// The array of type JsonValue to be added to this instance. /// If items is null. /// If any of the items in the array /// is a with property of /// value . public void AddRange(params JsonValue[] items) { AddRange(items as IEnumerable); } /// /// Searches for a specified object and returns the zero-based index of its first /// occurrence within this . /// /// The object to look up. /// The zero-based index of the first occurrence of item within the /// , if found; otherwise, -1. public int IndexOf(JsonValue item) { return values.IndexOf(item); } /// /// Insert a JSON CLR type into the array at a specified index. /// /// The zero-based index at which the item should be inserted. /// The object to insert. /// If index is less than zero or larger than /// the size of the array. /// If the object to insert has a /// property of value /// . public void Insert(int index, JsonValue item) { if (item != null && item.JsonType == JsonType.Default) { throw new ArgumentNullException("item", Properties.Resources.UseOfDefaultNotAllowed); } RaiseItemChanging(item, JsonValueChange.Add, index); values.Insert(index, item); RaiseItemChanged(item, JsonValueChange.Add, index); } /// /// Remove the JSON value at a specified index of . /// /// The zero-based index at which to remove the . /// If index is less than zero or index /// is equal or larger than the size of the array. public void RemoveAt(int index) { JsonValue item = values[index]; RaiseItemChanging(item, JsonValueChange.Remove, index); values.RemoveAt(index); RaiseItemChanged(item, JsonValueChange.Remove, index); } /// /// Adds a object to the end of the array. /// /// The object to add. /// If the object to add has a /// property of value /// . public void Add(JsonValue item) { if (item != null && item.JsonType == JsonType.Default) { throw new ArgumentNullException("item", Properties.Resources.UseOfDefaultNotAllowed); } int index = Count; RaiseItemChanging(item, JsonValueChange.Add, index); values.Add(item); RaiseItemChanged(item, JsonValueChange.Add, index); } /// /// Removes all JSON CLR types from the . /// public void Clear() { RaiseItemChanging(null, JsonValueChange.Clear, 0); values.Clear(); RaiseItemChanged(null, JsonValueChange.Clear, 0); } /// /// Checks whether a specified JSON CLR type is in the . /// /// The to check for in the array. /// true if item is found in the ; otherwise, false. public bool Contains(JsonValue item) { return values.Contains(item); } /// /// Copies the contents of the current JSON CLR array instance into a specified /// destination array beginning at the specified index. /// /// The destination array to which the elements of the current /// object are copied. /// The zero-based index in the destination array at which the /// copying of the elements of the JSON CLR array begins. public void CopyTo(JsonValue[] array, int arrayIndex) { values.CopyTo(array, arrayIndex); } /// /// Removes the first occurrence of the specified JSON value from the array. /// /// The to remove from the . /// true if item is successfully removed; otherwise, false. This method /// also returns false if item was not found in the . public bool Remove(JsonValue item) { int index = -1; if (ChangingListenersCount > 0 || ChangedListenersCount > 0) { index = IndexOf(item); } if (index >= 0) { RaiseItemChanging(item, JsonValueChange.Remove, index); } bool result = values.Remove(item); if (index >= 0) { RaiseItemChanged(item, JsonValueChange.Remove, index); } return result; } /// /// Returns an enumerator that iterates through the objects in the array. /// /// Returns an object that /// iterates through the elements in this . IEnumerator IEnumerable.GetEnumerator() { return values.GetEnumerator(); } /// /// Safe indexer for the type. /// /// The zero-based index of the element to get. /// If the index is within the array bounds and the value corresponding to the /// index is not null, then it will return that value. Otherwise it will return a /// instance with /// equals to . public override JsonValue ValueOrDefault(int index) { if (index >= 0 && index < Count && this[index] != null) { return this[index]; } return base.ValueOrDefault(index); } /// /// Returns an enumerator that iterates through the objects in the array. /// /// Returns an object that /// iterates through the elements in this . public new IEnumerator GetEnumerator() { return values.GetEnumerator(); } /// /// Returns an enumerator which iterates through the values in this object. /// /// An which iterates through the values in this object. /// The enumerator returned by this class contains one pair for each element /// in this array, whose key is the element index (as a string), and the value is the /// element itself. protected override IEnumerator> GetKeyValuePairEnumerator() { for (int i = 0; i < values.Count; i++) { yield return new KeyValuePair(i.ToString(CultureInfo.InvariantCulture), values[i]); } } /// /// Callback method called to let an instance write the proper JXML attribute when saving this /// instance. /// /// The JXML writer used to write JSON. internal override void WriteAttributeString(XmlDictionaryWriter jsonWriter) { if (jsonWriter == null) { throw new ArgumentNullException("jsonWriter"); } jsonWriter.WriteAttributeString(JXmlToJsonValueConverter.TypeAttributeName, JXmlToJsonValueConverter.ArrayAttributeValue); } /// /// Callback method called during Save operations to let the instance write the start element /// and return the next element in the collection. /// /// The JXML writer used to write JSON. /// The index within this collection. /// The next item in the collection, or null of there are no more items. internal override JsonValue WriteStartElementAndGetNext(XmlDictionaryWriter jsonWriter, int currentIndex) { if (jsonWriter == null) { throw new ArgumentNullException("jsonWriter"); } jsonWriter.WriteStartElement(JXmlToJsonValueConverter.ItemElementName); JsonValue nextValue = this[currentIndex]; return nextValue; } private void RaiseItemChanging(JsonValue child, JsonValueChange change, int index) { if (ChangingListenersCount > 0) { RaiseChangingEvent(this, new JsonValueChangeEventArgs(child, change, index)); } } private void RaiseItemChanged(JsonValue child, JsonValueChange change, int index) { if (ChangedListenersCount > 0) { RaiseChangedEvent(this, new JsonValueChangeEventArgs(child, change, index)); } } } }