From 260f676190c6b7209f4f137b67bdcf9d981314a2 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Thu, 15 Aug 2019 07:29:39 -0700 Subject: [PATCH 01/29] JsonArray added --- .../src/System/Text/Json/Node/JsonArray.cs | 657 ++++++++++++++++++ .../src/System/Text/Json/Node/JsonObject.cs | 66 ++ 2 files changed, 723 insertions(+) create mode 100644 src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs new file mode 100644 index 000000000000..11513faee8ae --- /dev/null +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs @@ -0,0 +1,657 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Collections.Generic; + +namespace System.Text.Json +{ + /// + /// Represents a JSON array. + /// + public partial class JsonArray : JsonNode, IList, IReadOnlyList + { + private readonly List _list; + + /// + /// Initializes a new instance of the class representing the empty array. + /// + public JsonArray() => _list = new List(); + + /// + /// Initializes a new instance of the class representing the specified collection. + /// + /// Collection to represent. + public JsonArray(IEnumerable values) => new List(values); + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + /// + /// Some of provided values are null. + /// + public JsonArray(IEnumerable values) : this() + { + foreach (string value in values) + { + _list.Add(new JsonString(value)); + } + } + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + public JsonArray(IEnumerable values) : this() + { + foreach (bool value in values) + { + _list.Add(new JsonBoolean(value)); + } + } + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + public JsonArray(IEnumerable values) : this() + { + foreach (byte value in values) + { + _list.Add(new JsonNumber(value)); + } + } + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + public JsonArray(IEnumerable values) : this() + { + foreach (short value in values) + { + _list.Add(new JsonNumber(value)); + } + } + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + public JsonArray(IEnumerable values) : this() + { + foreach (int value in values) + { + _list.Add(new JsonNumber(value)); + } + } + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + public JsonArray(IEnumerable values) : this() + { + foreach (long value in values) + { + _list.Add(new JsonNumber(value)); + } + } + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + /// + /// Some of provided values are not in a legal JSON number format. + /// + public JsonArray(IEnumerable values) : this() + { + foreach (float value in values) + { + _list.Add(new JsonNumber(value)); + } + } + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + /// + /// Some of provided values are not in a legal JSON number format. + /// + public JsonArray(IEnumerable values) : this() + { + foreach (double value in values) + { + _list.Add(new JsonNumber(value)); + } + } + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + [CLSCompliant(false)] + public JsonArray(IEnumerable values) : this() + { + foreach (sbyte value in values) + { + _list.Add(new JsonNumber(value)); + } + } + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + [CLSCompliant(false)] + public JsonArray(IEnumerable values) : this() + { + foreach (ushort value in values) + { + _list.Add(new JsonNumber(value)); + } + } + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + [CLSCompliant(false)] + public JsonArray(IEnumerable values) : this() + { + foreach (uint value in values) + { + _list.Add(new JsonNumber(value)); + } + } + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + [CLSCompliant(false)] + public JsonArray(IEnumerable values) : this() + { + foreach (ulong value in values) + { + _list.Add(new JsonNumber(value)); + } + } + + /// + /// Initializes a new instance of the class representing the specified collection of s. + /// + /// Collection to represent. + public JsonArray(IEnumerable values) : this() + { + foreach (decimal value in values) + { + _list.Add(new JsonNumber(value)); + } + } + + /// + /// Gets or sets the element at the specified index. + /// + /// The zero-based index of the element to get or set. + /// + /// Index is less than 0. + /// + public JsonNode this[int idx] + { + get => 0 <= idx && idx < _list.Count ? _list[idx] : throw new ArgumentOutOfRangeException(); + set + { + if (idx < 0 || idx >= _list.Count) + { + throw new ArgumentOutOfRangeException(); + } + _list[idx] = value; + } + } + + /// + /// Adds the specified value to the JSON array. + /// + /// The value to add. + /// Null value is allowed and represents a null JSON node. + public void Add(JsonNode value) => _list.Add(value); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + /// + /// Provided value is null. + /// + public void Add(string value) => Add(new JsonString(value)); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + public void Add(bool value) => Add(new JsonBoolean(value)); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + public void Add(byte value) => Add(new JsonNumber(value)); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + public void Add(short value) => Add(new JsonNumber(value)); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + public void Add(int value) => Add(new JsonNumber(value)); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + public void Add(long value) => Add(new JsonNumber(value)); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + /// + /// Provided value is not in a legal JSON number format. + /// + public void Add(float value) => Add(new JsonNumber(value)); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + /// + /// Provided value is not in a legal JSON number format. + /// + public void Add(double value) => Add(new JsonNumber(value)); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + [CLSCompliant(false)] + public void Add(sbyte value) => Add(new JsonNumber(value)); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + [CLSCompliant(false)] + public void Add(ushort value) => Add(new JsonNumber(value)); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + [CLSCompliant(false)] + public void Add(uint value) => Add(new JsonNumber(value)); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + [CLSCompliant(false)] + public void Add(ulong value) => Add(new JsonNumber(value)); + + /// + /// Adds the specified value as a to the JSON array. + /// + /// The value to add. + public void Add(decimal value) => Add(new JsonNumber(value)); + + /// + /// Inserts the specified item at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + /// Null item is allowed and represents a null JSON node. + public void Insert(int index, JsonNode item) => _list.Insert(index, item); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + public void Insert(int index, string item) => Insert(index, new JsonString(item)); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + public void Insert(int index, bool item) => Insert(index, new JsonBoolean(item)); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + public void Insert(int index, byte item) => Insert(index, new JsonNumber(item)); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + public void Insert(int index, short item) => Insert(index, new JsonNumber(item)); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + public void Insert(int index, int item) => Insert(index, new JsonNumber(item)); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + public void Insert(int index, long item) => Insert(index, new JsonNumber(item)); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + /// + /// Provided value is not in a legal JSON number format. + /// + public void Insert(int index, float item) => Insert(index, new JsonNumber(item)); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + /// + /// Provided value is not in a legal JSON number format. + /// + public void Insert(int index, double item) => Insert(index, new JsonNumber(item)); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + [CLSCompliant(false)] + public void Insert(int index, sbyte item) => Insert(index, new JsonNumber(item)); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + [CLSCompliant(false)] + public void Insert(int index, ushort item) => Insert(index, new JsonNumber(item)); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + [CLSCompliant(false)] + public void Insert(int index, uint item) => Insert(index, new JsonNumber(item)); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + [CLSCompliant(false)] + public void Insert(int index, ulong item) => Insert(index, new JsonNumber(item)); + + /// + /// Inserts the specified item as a at the specified index of the JSON array. + /// + /// The zero-based index at which item should be inserted. + /// The item to add. + public void Insert(int index, decimal item) => Insert(index, new JsonNumber(item)); + + /// + /// Determines whether a specified element is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + public bool Contains(JsonNode value) => _list.Contains(value); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + /// + /// Provided value is null. + /// + public bool Contains(string value) => Contains(new JsonString(value)); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + public bool Contains(bool value) => Contains(new JsonBoolean(value)); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + public bool Contains(byte value) => Contains(new JsonNumber(value)); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + public bool Contains(short value) => Contains(new JsonNumber(value)); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + public bool Contains(int value) => Contains(new JsonNumber(value)); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + public bool Contains(long value) => Contains(new JsonNumber(value)); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + /// + /// Provided value is not in a legal JSON number format. + /// + public bool Contains(float value) => Contains(new JsonNumber(value)); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + /// + /// Provided value is not in a legal JSON number format. + /// + public bool Contains(double value) => Contains(new JsonNumber(value)); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + [CLSCompliant(false)] + public bool Contains(sbyte value) => Contains(new JsonNumber(value)); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + [CLSCompliant(false)] + public bool Contains(ushort value) => Contains(new JsonNumber(value)); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + [CLSCompliant(false)] + public bool Contains(uint value) => Contains(new JsonNumber(value)); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + [CLSCompliant(false)] + public bool Contains(ulong value) => Contains(new JsonNumber(value)); + + /// + /// Determines whether a representing provided value is in a JSON array. + /// + /// Value to check. + /// + /// if the value is successfully found in a JSON array, + /// otherwise. + /// + public bool Contains(decimal value) => Contains(new JsonNumber(value)); + + /// + /// Gets the number of elements contained in the JSON array. + /// + public int Count => _list.Count; + + /// + /// Gets a value indicating whether the JSON array is read-only. + /// + public bool IsReadOnly => false; + + /// + /// Returns the zero-based index of the first occurrence of a specified item in the JSON array. + /// + /// Item to find. + /// The zero-based starting index of the search. 0 (zero) is valid in an empty list. + public int IndexOf(JsonNode item) => _list.IndexOf(item); + + /// + /// Returns the zero-based index of the last occurrence of a specified item in the JSON array. + /// + /// Item to find. + /// The zero-based starting index of the search. 0 (zero) is valid in an empty list. + public int LastIndexOf(JsonNode item) => _list.LastIndexOf(item); + + /// + /// Removes all elements from the JSON array. + /// + public void Clear() => _list.Clear(); + + /// + /// Removes the first occurrence of a specific object from the JSON array. + /// + /// + /// The object to remove from the JSON array. The value can be null and it represents null JSON node. + /// + /// + /// if the item is successfully found in a JSON array and removed, + /// otherwise. + /// + public bool Remove(JsonNode item) => _list.Remove(item); + + /// + /// Removes all the elements that match the conditions defined by the specified predicate. + /// + /// + /// Thepredicate delegate that defines the conditions of the elements to remove. + /// + /// The number of elements removed from the JSOJN array. + public int RemoveAll(Predicate match) => _list.RemoveAll(match); + + /// + /// Removes the item at the specified index of the JSON array. + /// + /// + /// The zero-based index of the element to remove. + /// + public void RemoveAt(int index) => _list.RemoveAt(index); + + /// + /// Copies the JSON array or a portion of it to an array. + /// + /// + /// The one-dimensional Array that is the destination of the elements copied from JSON array. + /// The Array must have zero-based indexing. + /// + /// The zero-based index in array at which copying begins. + void ICollection.CopyTo(JsonNode[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex); + + /// + /// Returns an enumerator that iterates through the JSON array values. + /// + /// An enumerator structure for the . + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + + /// + /// Returns an enumerator that iterates through the JSON array values. + /// + /// An enumerator structure for the . + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + } +} diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs index 19a35a163fc1..3886be98c770 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs @@ -371,6 +371,27 @@ public void AddRange(IEnumerable> jsonProperties) } } + /// + /// Adds the property values from the specified collection as a property to the JSON object. + /// + /// Name of the property to add. + /// Properties to add. + /// + /// Provided collection contains duplicates if handling duplicates is set to . + /// + /// + /// Some of property names are null. + /// + public void Add(string propertyName, IEnumerable propertyValues) + { + var jsonArray = new JsonArray(); + foreach (JsonNode value in propertyValues) + { + jsonArray.Add(value); + } + Add(propertyName, (JsonNode)jsonArray); + } + /// /// Removes the property with the specified name. /// @@ -475,6 +496,51 @@ public bool TryGetJsonObjectPropertyValue(string propertyName, out JsonObject js return false; } + /// + /// Returns the JSON array value of a property with the specified name. + /// + /// Name of the property to return. + /// JSON objectvalue of a property with the specified name. + /// + /// Property with specified name is not found in JSON array. + /// + /// + /// Property with specified name is not a JSON array. + /// + public JsonArray GetJsonArrayProperty(string propertyName) + { + if (GetPropertyValue(propertyName) is JsonArray jsonArray) + { + return jsonArray; + } + + throw new InvalidCastException(SR.Format(SR.PropertyTypeMismatch, propertyName)); + } + + /// + /// Returns the JSON array value of a property with the specified name. + /// + /// Name of the property to return. + /// JSON array value of the property with specified name. + /// + /// if JSON array property with specified name was found; + /// otherwise, + /// + public bool TryGetArrayProperty(string propertyName, out JsonArray jsonArray) + { + if (TryGetPropertyValue(propertyName, out JsonNode jsonNode)) + { + if (jsonNode is JsonArray jsonNodeCasted) + { + jsonArray = jsonNodeCasted; + return true; + } + } + + jsonArray = null; + return false; + } + /// /// A collection containing the property names of JSON object. /// From 70263cbf4c523b5aa07bd232353443dd29164110 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Fri, 16 Aug 2019 14:45:17 -0700 Subject: [PATCH 02/29] JsonArray scenario-tests added --- src/System.Text.Json/ref/System.Text.Json.cs | 91 +++++++ .../src/System.Text.Json.csproj | 1 + .../src/System/Text/Json/Node/JsonArray.cs | 2 +- .../src/System/Text/Json/Node/JsonObject.cs | 2 +- src/System.Text.Json/tests/JsonArrayTests.cs | 247 ++++++++++++++++++ .../tests/JsonNodeTestData.cs | 111 ++++++++ .../tests/JsonObjectTests.TestData.cs | 120 --------- .../tests/System.Text.Json.Tests.csproj | 3 +- 8 files changed, 454 insertions(+), 123 deletions(-) create mode 100644 src/System.Text.Json/tests/JsonArrayTests.cs create mode 100644 src/System.Text.Json/tests/JsonNodeTestData.cs delete mode 100644 src/System.Text.Json/tests/JsonObjectTests.TestData.cs diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index 2c9c06d4451b..bdc7f25da27a 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -13,6 +13,94 @@ public enum DuplicatePropertyNameHandling Ignore = 1, Error = 2, } + public partial class JsonArray : System.Text.Json.JsonNode, System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.IEnumerable + { + public JsonArray() { } + public JsonArray(System.Collections.Generic.IEnumerable values) { } + public JsonArray(System.Collections.Generic.IEnumerable values) { } + public JsonArray(System.Collections.Generic.IEnumerable values) { } + public JsonArray(System.Collections.Generic.IEnumerable values) { } + public JsonArray(System.Collections.Generic.IEnumerable values) { } + public JsonArray(System.Collections.Generic.IEnumerable values) { } + public JsonArray(System.Collections.Generic.IEnumerable values) { } + [System.CLSCompliantAttribute(false)] + public JsonArray(System.Collections.Generic.IEnumerable values) { } + public JsonArray(System.Collections.Generic.IEnumerable values) { } + public JsonArray(System.Collections.Generic.IEnumerable values) { } + public JsonArray(System.Collections.Generic.IEnumerable values) { } + [System.CLSCompliantAttribute(false)] + public JsonArray(System.Collections.Generic.IEnumerable values) { } + [System.CLSCompliantAttribute(false)] + public JsonArray(System.Collections.Generic.IEnumerable values) { } + [System.CLSCompliantAttribute(false)] + public JsonArray(System.Collections.Generic.IEnumerable values) { } + public int Count { get { throw null; } } + public bool IsReadOnly { get { throw null; } } + public System.Text.Json.JsonNode this[int idx] { get { throw null; } set { } } + public void Add(bool value) { } + public void Add(byte value) { } + public void Add(decimal value) { } + public void Add(double value) { } + public void Add(short value) { } + public void Add(int value) { } + public void Add(long value) { } + [System.CLSCompliantAttribute(false)] + public void Add(sbyte value) { } + public void Add(float value) { } + public void Add(string value) { } + public void Add(System.Text.Json.JsonNode value) { } + [System.CLSCompliantAttribute(false)] + public void Add(ushort value) { } + [System.CLSCompliantAttribute(false)] + public void Add(uint value) { } + [System.CLSCompliantAttribute(false)] + public void Add(ulong value) { } + public void Clear() { } + public bool Contains(bool value) { throw null; } + public bool Contains(byte value) { throw null; } + public bool Contains(decimal value) { throw null; } + public bool Contains(double value) { throw null; } + public bool Contains(short value) { throw null; } + public bool Contains(int value) { throw null; } + public bool Contains(long value) { throw null; } + [System.CLSCompliantAttribute(false)] + public bool Contains(sbyte value) { throw null; } + public bool Contains(float value) { throw null; } + public bool Contains(string value) { throw null; } + public bool Contains(System.Text.Json.JsonNode value) { throw null; } + [System.CLSCompliantAttribute(false)] + public bool Contains(ushort value) { throw null; } + [System.CLSCompliantAttribute(false)] + public bool Contains(uint value) { throw null; } + [System.CLSCompliantAttribute(false)] + public bool Contains(ulong value) { throw null; } + public System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + public int IndexOf(System.Text.Json.JsonNode item) { throw null; } + public void Insert(int index, bool item) { } + public void Insert(int index, byte item) { } + public void Insert(int index, decimal item) { } + public void Insert(int index, double item) { } + public void Insert(int index, short item) { } + public void Insert(int index, int item) { } + public void Insert(int index, long item) { } + [System.CLSCompliantAttribute(false)] + public void Insert(int index, sbyte item) { } + public void Insert(int index, float item) { } + public void Insert(int index, string item) { } + public void Insert(int index, System.Text.Json.JsonNode item) { } + [System.CLSCompliantAttribute(false)] + public void Insert(int index, ushort item) { } + [System.CLSCompliantAttribute(false)] + public void Insert(int index, uint item) { } + [System.CLSCompliantAttribute(false)] + public void Insert(int index, ulong item) { } + public int LastIndexOf(System.Text.Json.JsonNode item) { throw null; } + public bool Remove(System.Text.Json.JsonNode item) { throw null; } + public int RemoveAll(System.Predicate match) { throw null; } + public void RemoveAt(int index) { } + void System.Collections.Generic.ICollection.CopyTo(System.Text.Json.JsonNode[] array, int arrayIndex) { } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + } public sealed partial class JsonBoolean : System.Text.Json.JsonNode, System.IEquatable { public JsonBoolean() { } @@ -276,6 +364,7 @@ public JsonObject(System.Text.Json.DuplicatePropertyNameHandling duplicateProper public void Add(System.Collections.Generic.KeyValuePair jsonProperty) { } public void Add(string propertyName, bool propertyValue) { } public void Add(string propertyName, byte propertyValue) { } + public void Add(string propertyName, System.Collections.Generic.IEnumerable propertyValues) { } public void Add(string propertyName, System.DateTime propertyValue) { } public void Add(string propertyName, System.DateTimeOffset propertyValue) { } public void Add(string propertyName, decimal propertyValue) { } @@ -299,10 +388,12 @@ public void Add(string propertyName, ulong propertyValue) { } public void AddRange(System.Collections.Generic.IEnumerable> jsonProperties) { } public bool ContainsProperty(string propertyName) { throw null; } public System.Collections.Generic.IEnumerator> GetEnumerator() { throw null; } + public System.Text.Json.JsonArray GetJsonArrayProperty(string propertyName) { throw null; } public System.Text.Json.JsonObject GetJsonObjectPropertyValue(string propertyName) { throw null; } public System.Text.Json.JsonNode GetPropertyValue(string propertyName) { throw null; } public bool Remove(string propertyName) { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + public bool TryGetArrayProperty(string propertyName, out System.Text.Json.JsonArray jsonArray) { throw null; } public bool TryGetJsonObjectPropertyValue(string propertyName, out System.Text.Json.JsonObject jsonObject) { throw null; } public bool TryGetPropertyValue(string propertyName, out System.Text.Json.JsonNode jsonNode) { throw null; } } diff --git a/src/System.Text.Json/src/System.Text.Json.csproj b/src/System.Text.Json/src/System.Text.Json.csproj index bbbae86a515b..c39adf93873e 100644 --- a/src/System.Text.Json/src/System.Text.Json.csproj +++ b/src/System.Text.Json/src/System.Text.Json.csproj @@ -23,6 +23,7 @@ + diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs index 11513faee8ae..708e012ce170 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs @@ -23,7 +23,7 @@ public partial class JsonArray : JsonNode, IList, IReadOnlyList class representing the specified collection. /// /// Collection to represent. - public JsonArray(IEnumerable values) => new List(values); + public JsonArray(IEnumerable values) => _list = new List(values); /// /// Initializes a new instance of the class representing the specified collection of s. diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs index 3886be98c770..5737999265e9 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs @@ -139,7 +139,7 @@ public void Add(string propertyName, JsonNode propertyValue) /// Adds the specified property as a to the JSON object. /// /// Name of the property to add. - /// value of the property to add. + /// value of the property to add. /// /// Property name to set already exists if handling duplicates is set to . /// diff --git a/src/System.Text.Json/tests/JsonArrayTests.cs b/src/System.Text.Json/tests/JsonArrayTests.cs new file mode 100644 index 000000000000..26be7bf3d53c --- /dev/null +++ b/src/System.Text.Json/tests/JsonArrayTests.cs @@ -0,0 +1,247 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace System.Text.Json.Tests +{ + public static partial class JsonArrayTests + { + [Fact] + public static void TestAddingJsonArray() + { + var preferences = new JsonObject() + { + { "colours", (JsonNode) new JsonArray{ "red", "green", "blue" } } + }; + + Assert.IsType(preferences["colours"]); + var colours = preferences["colours"] as JsonArray; + Assert.Equal(3, colours.Count); + + string[] expected = { "red", "green", "blue" }; + + for (int i = 0; i < colours.Count; i++) + { + Assert.IsType(colours[i]); + Assert.Equal(expected[i], colours[i] as JsonString); + } + } + + [Fact] + public static void TestCretingJsonArrayFromStringArray() + { + string[] expected = { "sushi", "pasta", "cucumber soup" }; + var preferences = new JsonObject() + { + { "dishes", (JsonNode) new JsonArray(expected) } + }; + + Assert.IsType(preferences["dishes"]); + var dishesJson = preferences["dishes"] as JsonArray; + Assert.Equal(3, dishesJson.Count); + + for (int i = 0; i < dishesJson.Count; i++) + { + Assert.IsType(dishesJson[i]); + Assert.Equal(expected[i], dishesJson[i] as JsonString); + } + } + + /// + /// Adding JsonArray to JsonObject by passing JsonNumber array + /// + [Fact] + public static void TestAddingJsonArrayFromJsonNumberArray() + { + var preferences = new JsonObject() + { + { "prime numbers", new JsonNumber[] { 19, 37 } } + }; + + Assert.IsType(preferences["prime numbers"]); + var primeNumbers = preferences["prime numbers"] as JsonArray; + Assert.Equal(2, primeNumbers.Count); + + int[] expected = { 19, 37 }; + + for (int i = 0; i < primeNumbers.Count; i++) + { + Assert.IsType(primeNumbers[i]); + Assert.Equal(expected[i], primeNumbers[i] as JsonNumber); + } + } + + [Fact] + public static void TestAddingJsonArrayFromIEnumerableOfStrings() + { + var sportsExperienceYears = new JsonObject() + { + { "skiing", 5 }, + { "cycling", 8 }, + { "hiking", 6 }, + { "chess", 2 }, + { "skating", 1 }, + }; + + // choose only sports with > 2 experience years + IEnumerable sports = sportsExperienceYears.Where(sport => ((JsonNumber)sport.Value).GetInt32() > 2).Select(sport => sport.Key); + + var preferences = new JsonObject() + { + { "sports", (JsonNode) new JsonArray(sports) } + }; + + Assert.IsType(preferences["sports"]); + var sportsJsonArray = preferences["sports"] as JsonArray; + Assert.Equal(3, sportsJsonArray.Count); + + for (int i = 0; i < sportsJsonArray.Count; i++) + { + Assert.IsType(sportsJsonArray[i]); + Assert.Equal(sports.ElementAt(i), sportsJsonArray[i] as JsonString); + } + } + + [Fact] + public static void TestAddingJsonArrayFromIEnumerableOfJsonNodes() + { + var strangeWords = new JsonArray() + { + "supercalifragilisticexpialidocious", + "gladiolus", + "albumen", + "smaragdine" + }; + + var preferences = new JsonObject() + { + { "strange words", strangeWords.Where(word => ((JsonString)word).Value.Length < 10) } + }; + + Assert.IsType(preferences["strange words"]); + var strangeWordsJsonArray = preferences["strange words"] as JsonArray; + Assert.Equal(2, strangeWordsJsonArray.Count); + + string[] expected = { "gladiolus", "albumen" }; + + for (int i = 0; i < strangeWordsJsonArray.Count; i++) + { + Assert.IsType(strangeWordsJsonArray[i]); + Assert.Equal(expected[i], strangeWordsJsonArray[i] as JsonString); + } + } + + [Fact] + public static void TestCreatingNestedJsonArray() + { + var vertices = new JsonArray() + { + new JsonArray + { + new JsonArray + { + new JsonArray { 0, 0, 0 }, + new JsonArray { 0, 0, 1 } + }, + new JsonArray + { + new JsonArray { 0, 1, 0 }, + new JsonArray { 0, 1, 1 } + } + }, + new JsonArray + { + new JsonArray + { + new JsonArray { 1, 0, 0 }, + new JsonArray { 1, 0, 1 } + }, + new JsonArray + { + new JsonArray { 1, 1, 0 }, + new JsonArray { 1, 1, 1 } + } + }, + }; + + Assert.IsType(vertices[0]); + var innerJsonArray = vertices[0] as JsonArray; + Assert.IsType(innerJsonArray[0]); + innerJsonArray = innerJsonArray[0] as JsonArray; + Assert.IsType(innerJsonArray[0]); + } + + [Fact] + public static void TestCreatingJsonArrayFromCollection() + { + var employeesIds = new JsonArray(EmployeesDatabase.GetTenBestEmployees().Select(employee => new JsonString(employee.Key))); + + JsonString prevId = new JsonString(); + foreach (JsonNode employeeId in employeesIds) + { + Assert.IsType(employeeId); + var employeeIdString = employeeId as JsonString; + Assert.NotEqual(prevId, employeeIdString); + prevId = employeeIdString; + } + } + + [Fact] + public static void TestCreatingJsonArrayFromCollectionOfString() + { + var employeesIds = new JsonArray(EmployeesDatabase.GetTenBestEmployees().Select(employee => employee.Key)); + + JsonString prevId = new JsonString(); + foreach (JsonNode employeeId in employeesIds) + { + Assert.IsType(employeeId); + var employeeIdString = employeeId as JsonString; + Assert.NotEqual(prevId, employeeIdString); + prevId = employeeIdString; + } + } + + [Fact] + public static void TestAddingToJsonArray() + { + var employeesIds = new JsonArray(); + + foreach (KeyValuePair employee in EmployeesDatabase.GetTenBestEmployees()) + { + employeesIds.Add(employee.Key); + } + + JsonString prevId = new JsonString(); + foreach (JsonNode employeeId in employeesIds) + { + Assert.IsType(employeeId); + var employeeIdString = employeeId as JsonString; + Assert.NotEqual(prevId, employeeIdString); + prevId = employeeIdString; + } + } + + [Fact] + public static void TestAccesingNestedJsonArrayGetPropertyMethod() + { + var issues = new JsonObject() + { + { "features", new JsonString [] { "new functionality 1", "new functionality 2" } }, + { "bugs", new JsonString [] { "bug 123", "bug 4566", "bug 821" } }, + { "tests", new JsonString [] { "code coverage" } }, + }; + + issues.GetJsonArrayProperty("bugs").Add("bug 12356"); + ((JsonString)issues.GetJsonArrayProperty("features")[0]).Value = "feature 1569"; + ((JsonString)issues.GetJsonArrayProperty("features")[1]).Value = "feature 56134"; + + Assert.True(((JsonArray)issues["bugs"]).Contains("bug 12356")); + Assert.Equal("feature 1569", (JsonString)((JsonArray)issues["features"])[0]); + Assert.Equal("feature 56134", (JsonString)((JsonArray)issues["features"])[1]); + } + } +} diff --git a/src/System.Text.Json/tests/JsonNodeTestData.cs b/src/System.Text.Json/tests/JsonNodeTestData.cs new file mode 100644 index 000000000000..8f5ce9a07bab --- /dev/null +++ b/src/System.Text.Json/tests/JsonNodeTestData.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace System.Text.Json.Tests +{ + /// + /// Helper class simulating external library + /// + internal static class EmployeesDatabase + { + private static int s_id = 0; + public static KeyValuePair GetNextEmployee() + { + var employee = new JsonObject() + { + { "name", "John" } , + { "surname", "Smith"}, + { "age", 45 } + }; + + return new KeyValuePair("employee" + s_id++, employee); + } + + public static IEnumerable> GetTenBestEmployees() + { + for (int i = 0; i < 10; i++) + yield return GetNextEmployee(); + } + + /// + /// Returns following JsonObject: + /// { + /// { "name" : "John" } + /// { "phone numbers" : { "work" : "123-456-7890", "home": "123-456-7890" } } + /// { + /// "reporting employees" : + /// { + /// "software developers" : + /// { + /// "full time employees" : /JsonObject of 3 employees fromk database/ + /// "intern employees" : /JsonObject of 2 employees fromk database/ + /// }, + /// "HR" : /JsonObject of 10 employees fromk database/ + /// } + /// + /// + public static JsonObject GetManager() + { + var manager = GetNextEmployee().Value as JsonObject; + + manager.Add + ( + "phone numbers", + new JsonObject() + { + { "work", "123-456-7890" }, { "home", "123-456-7890" } + } + ); + + manager.Add + ( + "reporting employees", new JsonObject() + { + { + "software developers", new JsonObject() + { + { + "full time employees", new JsonObject() + { + EmployeesDatabase.GetNextEmployee(), + EmployeesDatabase.GetNextEmployee(), + EmployeesDatabase.GetNextEmployee(), + } + }, + { + "intern employees", new JsonObject() + { + EmployeesDatabase.GetNextEmployee(), + EmployeesDatabase.GetNextEmployee(), + } + } + } + }, + { + "HR", new JsonObject() + { + { + "full time employees", new JsonObject(EmployeesDatabase.GetTenBestEmployees()) + } + } + } + } + ); + + return manager; + } + } + + /// + /// Helper class simulating enum + /// + internal enum AvailableStateCodes + { + WA, + CA, + NY, + } +} diff --git a/src/System.Text.Json/tests/JsonObjectTests.TestData.cs b/src/System.Text.Json/tests/JsonObjectTests.TestData.cs deleted file mode 100644 index fb324ed49400..000000000000 --- a/src/System.Text.Json/tests/JsonObjectTests.TestData.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; - -namespace System.Text.Json.Tests -{ - public static partial class JsonObjectTests - { - /// - /// Helper class simulating external library - /// - private static class EmployeesDatabase - { - private static int s_id = 0; - public static KeyValuePair GetNextEmployee() - { - var employee = new JsonObject() - { - { "name", "John" } , - { "surname", "Smith"}, - { "age", 45 } - }; - - return new KeyValuePair("employee" + s_id++, employee); - } - - public static IEnumerable> GetTenBestEmployees() - { - for (int i = 0; i < 10; i++) - yield return GetNextEmployee(); - } - - /// - /// Returns following JsonObject: - /// { - /// { "name" : "John" } - /// { "phone numbers" : { "work" : "123-456-7890", "home": "123-456-7890" } } - /// { - /// "reporting employees" : - /// { - /// "software developers" : - /// { - /// "full time employees" : /JsonObject of 3 employees fromk database/ - /// "intern employees" : /JsonObject of 2 employees fromk database/ - /// }, - /// "HR" : /JsonObject of 10 employees fromk database/ - /// } - /// - /// - public static JsonObject GetManager() - { - var manager = GetNextEmployee().Value as JsonObject; - - manager.Add - ( - "phone numbers", - new JsonObject() - { - { "work", "123-456-7890" }, { "home", "123-456-7890" } - } - ); - - manager.Add - ( - "reporting employees", new JsonObject() - { - { - "software developers", new JsonObject() - { - { - "full time employees", new JsonObject() - { - EmployeesDatabase.GetNextEmployee(), - EmployeesDatabase.GetNextEmployee(), - EmployeesDatabase.GetNextEmployee(), - } - }, - { - "intern employees", new JsonObject() - { - EmployeesDatabase.GetNextEmployee(), - EmployeesDatabase.GetNextEmployee(), - } - } - } - }, - { - "HR", new JsonObject() - { - { - "full time employees", new JsonObject(EmployeesDatabase.GetTenBestEmployees()) - } - } - } - } - ); - - return manager; - } - public static void PerformHeavyOperations(JsonElement employee) { } - } - - private static class HealthCare - { - public static void CreateMedicalAppointment(string personName) { } - } - - /// - /// Helper class simulating enum - /// - private enum AvailableStateCodes - { - WA, - CA, - NY, - } - } -} diff --git a/src/System.Text.Json/tests/System.Text.Json.Tests.csproj b/src/System.Text.Json/tests/System.Text.Json.Tests.csproj index 3f6fe6d737c3..d2417839eb88 100644 --- a/src/System.Text.Json/tests/System.Text.Json.Tests.csproj +++ b/src/System.Text.Json/tests/System.Text.Json.Tests.csproj @@ -114,10 +114,11 @@ + - + From b9f6d19e7d246130f6290598587112a83334cf7d Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Fri, 16 Aug 2019 15:25:00 -0700 Subject: [PATCH 03/29] work on tests --- src/System.Text.Json/tests/JsonArrayTests.cs | 97 ++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/System.Text.Json/tests/JsonArrayTests.cs b/src/System.Text.Json/tests/JsonArrayTests.cs index 26be7bf3d53c..af3a8aab42e3 100644 --- a/src/System.Text.Json/tests/JsonArrayTests.cs +++ b/src/System.Text.Json/tests/JsonArrayTests.cs @@ -10,6 +10,103 @@ namespace System.Text.Json.Tests { public static partial class JsonArrayTests { + [Fact] + public static void TestAdd() + { + var jsonArray = new JsonArray(); + int idx = 0; + + jsonArray.Add("value"); + Assert.Equal("value", (JsonString)jsonArray[idx++]); + Assert.True(jsonArray.Contains("value")); + + jsonArray.Add(true); + Assert.True(((JsonBoolean)jsonArray[idx++]).Value); + Assert.True(jsonArray.Contains(true)); + + jsonArray.Add(byte.MaxValue); + Assert.Equal(byte.MaxValue, ((JsonNumber)jsonArray[idx++]).GetByte()); + Assert.True(jsonArray.Contains(byte.MaxValue)); + + jsonArray.Add(short.MaxValue); + Assert.Equal(short.MaxValue, ((JsonNumber)jsonArray[idx++]).GetInt16()); + Assert.True(jsonArray.Contains(short.MaxValue)); + + jsonArray.Add(int.MaxValue); + Assert.Equal(int.MaxValue, ((JsonNumber)jsonArray[idx++]).GetInt32()); + Assert.True(jsonArray.Contains(int.MaxValue)); + + jsonArray.Add(long.MaxValue); + Assert.Equal(long.MaxValue, ((JsonNumber)jsonArray[idx++]).GetInt64()); + Assert.True(jsonArray.Contains(long.MaxValue)); + + jsonArray.Add(3.14f); + Assert.Equal(3.14f, ((JsonNumber)jsonArray[idx++]).GetSingle()); + + jsonArray.Add(3.14); + Assert.Equal(3.14, ((JsonNumber)jsonArray[idx++]).GetDouble()); + + jsonArray.Add(sbyte.MaxValue); + Assert.Equal(sbyte.MaxValue, ((JsonNumber)jsonArray[idx++]).GetSByte()); + + jsonArray.Add(ushort.MaxValue); + Assert.Equal(ushort.MaxValue, ((JsonNumber)jsonArray[idx++]).GetUInt16()); + + jsonArray.Add(uint.MaxValue); + Assert.Equal(uint.MaxValue, ((JsonNumber)jsonArray[idx++]).GetUInt32()); + + jsonArray.Add(ulong.MaxValue); + Assert.Equal(ulong.MaxValue, ((JsonNumber)jsonArray[idx++]).GetUInt64()); + + jsonArray.Add(decimal.One); + Assert.Equal(decimal.One, ((JsonNumber)jsonArray[idx++]).GetDecimal()); + } + + [Fact] + public static void TestInsert() + { + var jsonArray = new JsonArray(); + + jsonArray.Insert(0, "value"); + Assert.Equal("value", (JsonString)jsonArray[0]); + + jsonArray.Insert(0, true); + Assert.True(((JsonBoolean)jsonArray[0]).Value); + + jsonArray.Insert(0, byte.MaxValue); + Assert.Equal(byte.MaxValue, ((JsonNumber)jsonArray[0]).GetByte()); + + jsonArray.Insert(0, short.MaxValue); + Assert.Equal(short.MaxValue, ((JsonNumber)jsonArray[0]).GetInt16()); + + jsonArray.Insert(0, int.MaxValue); + Assert.Equal(int.MaxValue, ((JsonNumber)jsonArray[0]).GetInt32()); + + jsonArray.Insert(0, long.MaxValue); + Assert.Equal(long.MaxValue, ((JsonNumber)jsonArray[0]).GetInt64()); + + jsonArray.Insert(0, 3.14f); + Assert.Equal(3.14f, ((JsonNumber)jsonArray[0]).GetSingle()); + + jsonArray.Insert(0, 3.14); + Assert.Equal(3.14, ((JsonNumber)jsonArray[0]).GetDouble()); + + jsonArray.Insert(0, sbyte.MaxValue); + Assert.Equal(sbyte.MaxValue, ((JsonNumber)jsonArray[0]).GetSByte()); + + jsonArray.Insert(0, ushort.MaxValue); + Assert.Equal(ushort.MaxValue, ((JsonNumber)jsonArray[0]).GetUInt16()); + + jsonArray.Insert(0, uint.MaxValue); + Assert.Equal(uint.MaxValue, ((JsonNumber)jsonArray[0]).GetUInt32()); + + jsonArray.Insert(0, ulong.MaxValue); + Assert.Equal(ulong.MaxValue, ((JsonNumber)jsonArray[0]).GetUInt64()); + + jsonArray.Insert(0, decimal.One); + Assert.Equal(decimal.One, ((JsonNumber)jsonArray[0]).GetDecimal()); + } + [Fact] public static void TestAddingJsonArray() { From 41d798c253dbb0be86adf82f2df9b993cc3e158b Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Mon, 19 Aug 2019 09:50:38 -0700 Subject: [PATCH 04/29] DeepCopy methods implemented --- src/System.Text.Json/ref/System.Text.Json.cs | 17 ++++ .../src/System/Text/Json/Node/JsonArray.cs | 6 ++ .../src/System/Text/Json/Node/JsonBoolean.cs | 6 ++ .../src/System/Text/Json/Node/JsonNode.cs | 60 ++++++++++++ .../src/System/Text/Json/Node/JsonNumber.cs | 6 ++ .../src/System/Text/Json/Node/JsonObject.cs | 16 ++++ .../src/System/Text/Json/Node/JsonString.cs | 6 ++ src/System.Text.Json/tests/JsonArrayTests.cs | 2 +- .../tests/JsonNodeTransformationsTests.cs | 94 +++++++++++++++++++ src/System.Text.Json/tests/JsonObjectTests.cs | 2 +- .../tests/System.Text.Json.Tests.csproj | 1 + 11 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 src/System.Text.Json/tests/JsonNodeTransformationsTests.cs diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index bdc7f25da27a..f2257c3610c7 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -56,6 +56,7 @@ public void Add(uint value) { } [System.CLSCompliantAttribute(false)] public void Add(ulong value) { } public void Clear() { } + public override System.Text.Json.JsonNode Clone() { throw null; } public bool Contains(bool value) { throw null; } public bool Contains(byte value) { throw null; } public bool Contains(decimal value) { throw null; } @@ -106,6 +107,7 @@ public sealed partial class JsonBoolean : System.Text.Json.JsonNode, System.IEqu public JsonBoolean() { } public JsonBoolean(bool value) { } public bool Value { get { throw null; } set { } } + public override System.Text.Json.JsonNode Clone() { throw null; } public override bool Equals(object obj) { throw null; } public bool Equals(System.Text.Json.JsonBoolean other) { throw null; } public override int GetHashCode() { throw null; } @@ -266,6 +268,18 @@ protected JsonNamingPolicy() { } public abstract partial class JsonNode { internal JsonNode() { } + public System.Text.Json.JsonElement AsJsonElement() { throw null; } + public abstract System.Text.Json.JsonNode Clone(); + public static System.Text.Json.JsonNode DeepCopy(System.Text.Json.JsonDocument jsonDocument) { throw null; } + public static System.Text.Json.JsonNode DeepCopy(System.Text.Json.JsonElement jsonElement) { throw null; } + public static System.Text.Json.JsonNode DeepCopy(System.Text.Json.JsonNode jsonNode) { throw null; } + public static System.Text.Json.JsonNode GetNode(System.Text.Json.JsonElement jsonElement) { throw null; } + public static System.Text.Json.JsonNode Parse(System.Buffers.ReadOnlySequence utf8Json) { throw null; } + public static System.Text.Json.JsonNode Parse(System.IO.Stream utf8Json) { throw null; } + public static System.Text.Json.JsonNode Parse(System.ReadOnlyMemory utf8Json) { throw null; } + public static System.Text.Json.JsonNode Parse(System.ReadOnlyMemory json) { throw null; } + public static System.Text.Json.JsonNode Parse(string json) { throw null; } + public static bool TryGetNode(System.Text.Json.JsonElement jsonElement, out System.Text.Json.JsonNode jsonNode) { throw null; } } public sealed partial class JsonNumber : System.Text.Json.JsonNode, System.IEquatable { @@ -286,6 +300,7 @@ public JsonNumber(ushort value) { } public JsonNumber(uint value) { } [System.CLSCompliantAttribute(false)] public JsonNumber(ulong value) { } + public override System.Text.Json.JsonNode Clone() { throw null; } public override bool Equals(object obj) { throw null; } public bool Equals(System.Text.Json.JsonNumber other) { throw null; } public byte GetByte() { throw null; } @@ -386,6 +401,7 @@ public void Add(string propertyName, uint propertyValue) { } [System.CLSCompliantAttribute(false)] public void Add(string propertyName, ulong propertyValue) { } public void AddRange(System.Collections.Generic.IEnumerable> jsonProperties) { } + public override System.Text.Json.JsonNode Clone() { throw null; } public bool ContainsProperty(string propertyName) { throw null; } public System.Collections.Generic.IEnumerator> GetEnumerator() { throw null; } public System.Text.Json.JsonArray GetJsonArrayProperty(string propertyName) { throw null; } @@ -477,6 +493,7 @@ public JsonString(System.Guid value) { } public JsonString(System.ReadOnlySpan value) { } public JsonString(string value) { } public string Value { get { throw null; } set { } } + public override System.Text.Json.JsonNode Clone() { throw null; } public override bool Equals(object obj) { throw null; } public bool Equals(System.Text.Json.JsonString other) { throw null; } public override int GetHashCode() { throw null; } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs index 708e012ce170..6748d96f1be1 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs @@ -653,5 +653,11 @@ public JsonNode this[int idx] /// /// An enumerator structure for the . public IEnumerator GetEnumerator() => _list.GetEnumerator(); + + /// + /// Creates a new JSON array that is a copy of the current instance. + /// + /// A new JSON array that is a copy of this instance. + public override JsonNode Clone() => new JsonArray(_list); } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonBoolean.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonBoolean.cs index 985ed4843def..84020a0d37a8 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonBoolean.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonBoolean.cs @@ -94,5 +94,11 @@ public sealed class JsonBoolean : JsonNode, IEquatable /// otherwise. /// public static bool operator !=(JsonBoolean left, JsonBoolean right) => !(left == right); + + /// + /// Creates a new JSON boolean that is a copy of the current instance. + /// + /// A new JSON boolean that is a copy of this instance. + public override JsonNode Clone() => new JsonBoolean(Value); } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs index ec16a6642c76..a9de0191c60a 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs @@ -2,6 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +// for now disabling error caused by not adding documentation to methods +#pragma warning disable CS1591 + +using System.Buffers; +using System.IO; + namespace System.Text.Json { /// @@ -10,5 +16,59 @@ namespace System.Text.Json public abstract class JsonNode { private protected JsonNode() { } + + public JsonElement AsJsonElement() { throw null; } + + public static JsonNode GetNode(JsonElement jsonElement) { throw null; } + public static bool TryGetNode(JsonElement jsonElement, out JsonNode jsonNode) { throw null; } + + public static JsonNode Parse(string json) { throw null; } + public static JsonNode Parse(ReadOnlySequence utf8Json) { throw null; } + public static JsonNode Parse(Stream utf8Json) { throw null; } + public static JsonNode Parse(ReadOnlyMemory utf8Json) { throw null; } + public static JsonNode Parse(ReadOnlyMemory json) { throw null; } + + // IClonable on all primary types? + public static JsonNode DeepCopy(JsonNode jsonNode) => jsonNode.Clone(); + + public abstract JsonNode Clone(); + + public static JsonNode DeepCopy(JsonElement jsonElement) + { + switch (jsonElement.ValueKind) + { + case JsonValueKind.Object: + JsonObject jsonObject = new JsonObject(); + foreach (JsonProperty property in jsonElement.EnumerateObject()) + { + jsonObject.Add(property.Name, DeepCopy(property.Value)); + } + return jsonObject; + case JsonValueKind.Array: + JsonArray jsonArray = new JsonArray(); + foreach (JsonElement element in jsonElement.EnumerateArray()) + { + jsonArray.Add(DeepCopy(element)); + } + return jsonArray; + case JsonValueKind.Number: + return new JsonNumber(jsonElement.GetRawText()); + case JsonValueKind.String: + return new JsonString(jsonElement.GetString()); + case JsonValueKind.True: + return new JsonBoolean(true); + case JsonValueKind.False: + return new JsonBoolean(false); + case JsonValueKind.Null: + return null; + default: + throw new ArgumentException(); + } + + } + + public static JsonNode DeepCopy(JsonDocument jsonDocument) => DeepCopy(jsonDocument.RootElement); } } + +#pragma warning restore CS1591 diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNumber.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNumber.cs index a4c639d6da35..6820d6293a5e 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonNumber.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonNumber.cs @@ -684,5 +684,11 @@ public void SetDouble(double value) /// otherwise. /// public static bool operator !=(JsonNumber left, JsonNumber right) => !(left == right); + + /// + /// Creates a new JSON number that is a copy of the current instance. + /// + /// A new JSON number that is a copy of this instance. + public override JsonNode Clone() => new JsonNumber(_value); } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs index 5737999265e9..af4e7e11c6b7 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs @@ -556,5 +556,21 @@ public bool TryGetArrayProperty(string propertyName, out JsonArray jsonArray) /// /// An enumerator structure for the . IEnumerator IEnumerable.GetEnumerator() => new JsonObjectEnumerator(this); + + /// + /// Creates a new JSON object that is a copy of the current instance. + /// + /// A new JSON object that is a copy of this instance. + public override JsonNode Clone() + { + var jsonObject = new JsonObject(_duplicatePropertyNameHandling); + + foreach (KeyValuePair property in _dictionary) + { + jsonObject.Add(property.Key, property.Value.Clone()); + } + + return jsonObject; + } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs index bcf459c7e30b..3d2a4f4c4897 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs @@ -131,5 +131,11 @@ public string Value /// otherwise. /// public static bool operator !=(JsonString left, JsonString right) => !(left == right); + + /// + /// Creates a new JSON string that is a copy of the current instance. + /// + /// A new JSON string that is a copy of this instance. + public override JsonNode Clone() => new JsonString(Value); } } diff --git a/src/System.Text.Json/tests/JsonArrayTests.cs b/src/System.Text.Json/tests/JsonArrayTests.cs index af3a8aab42e3..c580fe4a4438 100644 --- a/src/System.Text.Json/tests/JsonArrayTests.cs +++ b/src/System.Text.Json/tests/JsonArrayTests.cs @@ -8,7 +8,7 @@ namespace System.Text.Json.Tests { - public static partial class JsonArrayTests + public static class JsonArrayTests { [Fact] public static void TestAdd() diff --git a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs new file mode 100644 index 000000000000..9a4a47067e3a --- /dev/null +++ b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Xunit; + +namespace System.Text.Json.Tests +{ + public static class JsonNodeTransformationsTests + { + [Fact] + public static void TestJsonDocumentToJsonNode() + { + var jsonString = @" + { + ""text"": ""property value"", + ""boolean true"": true, + ""boolean false"": false, + ""null"": null, + ""int"": 17, + ""double"": 3.14, + ""scientific"": 3e100, + ""array"" : [1,2,3], + ""inner object"" : + { + ""inner property"" : ""value"" + } + }"; + + JsonDocument jsonDocument = JsonDocument.Parse(jsonString); + JsonNode node = JsonNode.DeepCopy(jsonDocument); + + var jsonObject = (JsonObject)node; + Assert.Equal(9, jsonObject.PropertyNames.Count); + Assert.Equal(9, jsonObject.PropertyValues.Count); + Assert.Equal("property value", (JsonString)jsonObject["text"]); + Assert.True(((JsonBoolean)jsonObject["boolean true"]).Value); + Assert.False(((JsonBoolean)jsonObject["boolean false"]).Value); + Assert.Null(jsonObject["null"]); + Assert.Equal(17, ((JsonNumber)jsonObject["int"]).GetInt32()); + Assert.Equal(3.14, ((JsonNumber)jsonObject["double"]).GetDouble()); + Assert.Equal("3e100", ((JsonNumber)jsonObject["scientific"]).ToString()); + + var innerArray = (JsonArray)jsonObject["array"]; + Assert.Equal(3, innerArray.Count); + Assert.Equal(1, ((JsonNumber)innerArray[0]).GetInt32()); + Assert.Equal(2, ((JsonNumber)innerArray[1]).GetInt32()); + Assert.Equal(3, ((JsonNumber)innerArray[2]).GetInt32()); + + var innerObject = (JsonObject)jsonObject["inner object"]; + Assert.Equal(1, innerObject.PropertyNames.Count); + Assert.Equal(1, innerObject.PropertyValues.Count); + Assert.Equal("value", (JsonString)innerObject["inner property"]); + } + + [Fact] + public static void TestCloneJsonArray() + { + var jsonArray = new JsonArray { "value1", "value2" }; + var jsonArrayCopy = JsonNode.DeepCopy(jsonArray) as JsonArray; + Assert.Equal(2, jsonArrayCopy.Count); + jsonArray.Add("value3"); + Assert.Equal(2, jsonArrayCopy.Count); + } + + [Fact] + public static void TestCloneJsonNode() + { + var jsonObject = new JsonObject + { + { "text", "property value" }, + { "boolean", true }, + { "number", 15 }, + { "array", new JsonString[] { "value1", "value2"} } + }; + + var jsonObjectCopy = (JsonObject)JsonNode.DeepCopy(jsonObject); + + jsonObject["text"] = (JsonString)"something different"; + Assert.Equal("property value", (JsonString)jsonObjectCopy["text"]); + + ((JsonBoolean)jsonObject["boolean"]).Value = false; + Assert.True(((JsonBoolean)jsonObjectCopy["boolean"]).Value); + + Assert.Equal(2, jsonObjectCopy.GetJsonArrayProperty("array").Count); + jsonObject.GetJsonArrayProperty("array").Add("value3"); + Assert.Equal(2, jsonObjectCopy.GetJsonArrayProperty("array").Count); + + jsonObject.Add("new one", 123); + Assert.Equal(4, jsonObjectCopy.PropertyNames.Count); + } + } +} diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index 776b5e559850..30626bd2e0e4 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -7,7 +7,7 @@ namespace System.Text.Json.Tests { - public static partial class JsonObjectTests + public static class JsonObjectTests { [Fact] public static void TestDefaultConstructor() diff --git a/src/System.Text.Json/tests/System.Text.Json.Tests.csproj b/src/System.Text.Json/tests/System.Text.Json.Tests.csproj index d2417839eb88..f20b381d2329 100644 --- a/src/System.Text.Json/tests/System.Text.Json.Tests.csproj +++ b/src/System.Text.Json/tests/System.Text.Json.Tests.csproj @@ -22,6 +22,7 @@ + From e8efb681c981b7619d68dbba64460a58898a033b Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Mon, 19 Aug 2019 12:53:36 -0700 Subject: [PATCH 05/29] Simple parse added --- src/System.Text.Json/ref/System.Text.Json.cs | 14 +++++ .../src/System.Text.Json.csproj | 1 + .../src/System/Text/Json/Node/JsonArray.cs | 5 ++ .../src/System/Text/Json/Node/JsonBoolean.cs | 5 ++ .../src/System/Text/Json/Node/JsonNode.cs | 48 ++++++++++++-- .../src/System/Text/Json/Node/JsonNodeKind.cs | 37 +++++++++++ .../src/System/Text/Json/Node/JsonNumber.cs | 5 ++ .../src/System/Text/Json/Node/JsonObject.cs | 5 ++ .../src/System/Text/Json/Node/JsonString.cs | 5 ++ .../tests/JsonNodeTransformationsTests.cs | 62 +++++++++++++++---- 10 files changed, 170 insertions(+), 17 deletions(-) create mode 100644 src/System.Text.Json/src/System/Text/Json/Node/JsonNodeKind.cs diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index f2257c3610c7..6cecbb9c5474 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -37,6 +37,7 @@ public JsonArray(System.Collections.Generic.IEnumerable values) { } public int Count { get { throw null; } } public bool IsReadOnly { get { throw null; } } public System.Text.Json.JsonNode this[int idx] { get { throw null; } set { } } + public override System.Text.Json.JsonNodeKind NodeKind { get { throw null; } } public void Add(bool value) { } public void Add(byte value) { } public void Add(decimal value) { } @@ -106,6 +107,7 @@ public sealed partial class JsonBoolean : System.Text.Json.JsonNode, System.IEqu { public JsonBoolean() { } public JsonBoolean(bool value) { } + public override System.Text.Json.JsonNodeKind NodeKind { get { throw null; } } public bool Value { get { throw null; } set { } } public override System.Text.Json.JsonNode Clone() { throw null; } public override bool Equals(object obj) { throw null; } @@ -268,6 +270,7 @@ protected JsonNamingPolicy() { } public abstract partial class JsonNode { internal JsonNode() { } + public abstract System.Text.Json.JsonNodeKind NodeKind { get; } public System.Text.Json.JsonElement AsJsonElement() { throw null; } public abstract System.Text.Json.JsonNode Clone(); public static System.Text.Json.JsonNode DeepCopy(System.Text.Json.JsonDocument jsonDocument) { throw null; } @@ -281,6 +284,14 @@ internal JsonNode() { } public static System.Text.Json.JsonNode Parse(string json) { throw null; } public static bool TryGetNode(System.Text.Json.JsonElement jsonElement, out System.Text.Json.JsonNode jsonNode) { throw null; } } + public enum JsonNodeKind : byte + { + Object = (byte)0, + Array = (byte)1, + String = (byte)2, + Number = (byte)3, + Boolean = (byte)4, + } public sealed partial class JsonNumber : System.Text.Json.JsonNode, System.IEquatable { public JsonNumber() { } @@ -300,6 +311,7 @@ public JsonNumber(ushort value) { } public JsonNumber(uint value) { } [System.CLSCompliantAttribute(false)] public JsonNumber(ulong value) { } + public override System.Text.Json.JsonNodeKind NodeKind { get { throw null; } } public override System.Text.Json.JsonNode Clone() { throw null; } public override bool Equals(object obj) { throw null; } public bool Equals(System.Text.Json.JsonNumber other) { throw null; } @@ -374,6 +386,7 @@ public sealed partial class JsonObject : System.Text.Json.JsonNode, System.Colle public JsonObject(System.Collections.Generic.IEnumerable> jsonProperties, System.Text.Json.DuplicatePropertyNameHandling duplicatePropertyNameHandling = System.Text.Json.DuplicatePropertyNameHandling.Replace) { } public JsonObject(System.Text.Json.DuplicatePropertyNameHandling duplicatePropertyNameHandling = System.Text.Json.DuplicatePropertyNameHandling.Replace) { } public System.Text.Json.JsonNode this[string propertyName] { get { throw null; } set { } } + public override System.Text.Json.JsonNodeKind NodeKind { get { throw null; } } public System.Collections.Generic.ICollection PropertyNames { get { throw null; } } public System.Collections.Generic.ICollection PropertyValues { get { throw null; } } public void Add(System.Collections.Generic.KeyValuePair jsonProperty) { } @@ -492,6 +505,7 @@ public JsonString(System.DateTimeOffset value) { } public JsonString(System.Guid value) { } public JsonString(System.ReadOnlySpan value) { } public JsonString(string value) { } + public override System.Text.Json.JsonNodeKind NodeKind { get { throw null; } } public string Value { get { throw null; } set { } } public override System.Text.Json.JsonNode Clone() { throw null; } public override bool Equals(object obj) { throw null; } diff --git a/src/System.Text.Json/src/System.Text.Json.csproj b/src/System.Text.Json/src/System.Text.Json.csproj index c39adf93873e..8684c7fb911c 100644 --- a/src/System.Text.Json/src/System.Text.Json.csproj +++ b/src/System.Text.Json/src/System.Text.Json.csproj @@ -24,6 +24,7 @@ + diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs index 6748d96f1be1..fd97fadff13a 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs @@ -659,5 +659,10 @@ public JsonNode this[int idx] /// /// A new JSON array that is a copy of this instance. public override JsonNode Clone() => new JsonArray(_list); + + /// + /// Returns + /// + public override JsonNodeKind NodeKind { get => JsonNodeKind.Array;} } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonBoolean.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonBoolean.cs index 84020a0d37a8..51b7eef2f21b 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonBoolean.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonBoolean.cs @@ -100,5 +100,10 @@ public sealed class JsonBoolean : JsonNode, IEquatable /// /// A new JSON boolean that is a copy of this instance. public override JsonNode Clone() => new JsonBoolean(Value); + + /// + /// Returns + /// + public override JsonNodeKind NodeKind { get => JsonNodeKind.Boolean; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs index a9de0191c60a..3f2c71a73b6f 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs @@ -19,16 +19,52 @@ private protected JsonNode() { } public JsonElement AsJsonElement() { throw null; } + public abstract JsonNodeKind NodeKind { get; } + public static JsonNode GetNode(JsonElement jsonElement) { throw null; } + public static bool TryGetNode(JsonElement jsonElement, out JsonNode jsonNode) { throw null; } - public static JsonNode Parse(string json) { throw null; } - public static JsonNode Parse(ReadOnlySequence utf8Json) { throw null; } - public static JsonNode Parse(Stream utf8Json) { throw null; } - public static JsonNode Parse(ReadOnlyMemory utf8Json) { throw null; } - public static JsonNode Parse(ReadOnlyMemory json) { throw null; } + public static JsonNode Parse(string json) + { + using (JsonDocument document = JsonDocument.Parse(json)) + { + return DeepCopy(document); + } + } + + public static JsonNode Parse(ReadOnlySequence utf8Json) + { + using (JsonDocument document = JsonDocument.Parse(utf8Json)) + { + return DeepCopy(document); + } + } + + public static JsonNode Parse(Stream utf8Json) + { + using (JsonDocument document = JsonDocument.Parse(utf8Json)) + { + return DeepCopy(document); + } + } + + public static JsonNode Parse(ReadOnlyMemory utf8Json) + { + using (JsonDocument document = JsonDocument.Parse(utf8Json)) + { + return DeepCopy(document); + } + } + + public static JsonNode Parse(ReadOnlyMemory json) + { + using (JsonDocument document = JsonDocument.Parse(json)) + { + return DeepCopy(document); + } + } - // IClonable on all primary types? public static JsonNode DeepCopy(JsonNode jsonNode) => jsonNode.Clone(); public abstract JsonNode Clone(); diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNodeKind.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNodeKind.cs new file mode 100644 index 000000000000..d19b6a1353e7 --- /dev/null +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonNodeKind.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Text.Json +{ + /// + /// Specifies the JSON node value. + /// + public enum JsonNodeKind : byte + { + /// + /// Indicates that a value is a JSON object. + /// + Object, + + /// + /// Indicates that a value is a JSON array. + /// + Array, + + /// + /// Indicates that a value is a JSON string. + /// + String, + + /// + /// Indicates that a value is a JSON number. + /// + Number, + + /// + /// Indicates that a value is a JSON boolean. + /// + Boolean + } +} diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNumber.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNumber.cs index 6820d6293a5e..6fc2f149a37a 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonNumber.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonNumber.cs @@ -690,5 +690,10 @@ public void SetDouble(double value) /// /// A new JSON number that is a copy of this instance. public override JsonNode Clone() => new JsonNumber(_value); + + /// + /// Returns + /// + public override JsonNodeKind NodeKind { get => JsonNodeKind.Number; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs index af4e7e11c6b7..1b845c3b5f78 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs @@ -572,5 +572,10 @@ public override JsonNode Clone() return jsonObject; } + + /// + /// Returns + /// + public override JsonNodeKind NodeKind { get => JsonNodeKind.Object; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs index 3d2a4f4c4897..469ac1a93188 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs @@ -137,5 +137,10 @@ public string Value /// /// A new JSON string that is a copy of this instance. public override JsonNode Clone() => new JsonString(Value); + + /// + /// Returns + /// + public override JsonNodeKind NodeKind { get => JsonNodeKind.String; } } } diff --git a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs index 9a4a47067e3a..bc4fc6f50e33 100644 --- a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs +++ b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs @@ -2,17 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; +using System.Buffers; +using System.IO; using Xunit; namespace System.Text.Json.Tests { public static class JsonNodeTransformationsTests { - [Fact] - public static void TestJsonDocumentToJsonNode() - { - var jsonString = @" + private static string jsonSampleString = @" { ""text"": ""property value"", ""boolean true"": true, @@ -28,9 +26,8 @@ public static void TestJsonDocumentToJsonNode() } }"; - JsonDocument jsonDocument = JsonDocument.Parse(jsonString); - JsonNode node = JsonNode.DeepCopy(jsonDocument); - + private static void CheckNodeFromSampleString(JsonNode node) + { var jsonObject = (JsonObject)node; Assert.Equal(9, jsonObject.PropertyNames.Count); Assert.Equal(9, jsonObject.PropertyValues.Count); @@ -53,7 +50,50 @@ public static void TestJsonDocumentToJsonNode() Assert.Equal(1, innerObject.PropertyValues.Count); Assert.Equal("value", (JsonString)innerObject["inner property"]); } - + + [Fact] + public static void TestJsonDocumentToJsonNode() + { + JsonDocument jsonDocument = JsonDocument.Parse(jsonSampleString); + JsonNode node = JsonNode.DeepCopy(jsonDocument); + CheckNodeFromSampleString(node); + } + + [Fact] + public static void TestParseStringToJsonNode() + { + JsonNode node = JsonNode.Parse(jsonSampleString); + CheckNodeFromSampleString(node); + } + + [Fact] + public static void TestParseBytesToJsonNode() + { + JsonNode node = JsonNode.Parse(new ReadOnlySequence(Encoding.UTF8.GetBytes(jsonSampleString).AsMemory())); + CheckNodeFromSampleString(node); + } + + [Fact] + public static void TestParseByteMemoryToJsonNode() + { + JsonNode node = JsonNode.Parse(Encoding.UTF8.GetBytes(jsonSampleString).AsMemory()); + CheckNodeFromSampleString(node); + } + + [Fact] + public static void TestParseCharMemoryToJsonNode() + { + JsonNode node = JsonNode.Parse(jsonSampleString.AsMemory()); + CheckNodeFromSampleString(node); + } + + [Fact] + public static void TestParseStreamToJsonNode() + { + JsonNode node = JsonNode.Parse(new MemoryStream(Encoding.UTF8.GetBytes(jsonSampleString))); + CheckNodeFromSampleString(node); + } + [Fact] public static void TestCloneJsonArray() { @@ -76,7 +116,7 @@ public static void TestCloneJsonNode() }; var jsonObjectCopy = (JsonObject)JsonNode.DeepCopy(jsonObject); - + jsonObject["text"] = (JsonString)"something different"; Assert.Equal("property value", (JsonString)jsonObjectCopy["text"]); @@ -88,7 +128,7 @@ public static void TestCloneJsonNode() Assert.Equal(2, jsonObjectCopy.GetJsonArrayProperty("array").Count); jsonObject.Add("new one", 123); - Assert.Equal(4, jsonObjectCopy.PropertyNames.Count); + Assert.Equal(4, jsonObjectCopy.PropertyNames.Count); } } } From a6ca8b7d5c197f679c00c4d11dea16bcfa94215a Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Mon, 19 Aug 2019 13:31:39 -0700 Subject: [PATCH 06/29] Work on JsonElement changing --- .../src/System.Text.Json.csproj | 1 - .../System/Text/Json/Document/JsonElement.cs | 52 +++++++++++++++++-- .../src/System/Text/Json/Node/JsonArray.cs | 4 +- .../src/System/Text/Json/Node/JsonBoolean.cs | 4 +- .../src/System/Text/Json/Node/JsonNode.cs | 18 +++++-- .../src/System/Text/Json/Node/JsonNodeKind.cs | 37 ------------- .../src/System/Text/Json/Node/JsonNumber.cs | 4 +- .../src/System/Text/Json/Node/JsonObject.cs | 4 +- .../src/System/Text/Json/Node/JsonString.cs | 4 +- 9 files changed, 72 insertions(+), 56 deletions(-) delete mode 100644 src/System.Text.Json/src/System/Text/Json/Node/JsonNodeKind.cs diff --git a/src/System.Text.Json/src/System.Text.Json.csproj b/src/System.Text.Json/src/System.Text.Json.csproj index 8684c7fb911c..c39adf93873e 100644 --- a/src/System.Text.Json/src/System.Text.Json.csproj +++ b/src/System.Text.Json/src/System.Text.Json.csproj @@ -24,7 +24,6 @@ - diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index 9fa492d5c23d..c84891d6a19f 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -13,7 +13,7 @@ namespace System.Text.Json [DebuggerDisplay("{DebuggerDisplay,nq}")] public readonly partial struct JsonElement { - private readonly JsonDocument _parent; + internal readonly object _parent; private readonly int _idx; internal JsonElement(JsonDocument parent, int idx) @@ -27,8 +27,26 @@ internal JsonElement(JsonDocument parent, int idx) _idx = idx; } - private JsonTokenType TokenType => _parent?.GetJsonTokenType(_idx) ?? JsonTokenType.None; + internal JsonElement(JsonNode parent) + { + _parent = parent; + _idx = -1; + } + + /// + /// Indicates if JSON element instance is immutable. + /// + public bool IsImmutable => _idx != -1; + + private JsonTokenType TokenType + { + get + { + JsonDocument document = (JsonDocument)_parent; + return document?.GetJsonTokenType(_idx) ?? JsonTokenType.None; + } + } /// /// The that the value is. /// @@ -56,7 +74,19 @@ public JsonElement this[int index] { CheckValidInstance(); - return _parent.GetArrayIndexElement(_idx, index); + if (_parent is JsonDocument document) + { + return document.GetArrayIndexElement(_idx, index); + } + + JsonNode node = (JsonNode)_parent; + + if (node is JsonArray jsonArray) + { + return jsonArray[index].AsJsonElement(); + } + + throw new InvalidOperationException(); } } @@ -74,7 +104,19 @@ public int GetArrayLength() { CheckValidInstance(); - return _parent.GetArrayLength(_idx); + if (_parent is JsonDocument document) + { + return document.GetArrayLength(_idx); + } + + JsonNode node = (JsonNode)_parent; + + if (node is JsonArray jsonArray) + { + return jsonArray.Count; + } + + throw new InvalidOperationException(); } /// @@ -1436,6 +1478,8 @@ private void CheckValidInstance() { throw new InvalidOperationException(); } + + Debug.Assert(_parent is JsonDocument || _parent is JsonNode, "JsonElement can be backed only by JsonDocument or JsonNode"); } [DebuggerBrowsable(DebuggerBrowsableState.Never)] diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs index fd97fadff13a..a35b56b7edea 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs @@ -661,8 +661,8 @@ public JsonNode this[int idx] public override JsonNode Clone() => new JsonArray(_list); /// - /// Returns + /// Returns /// - public override JsonNodeKind NodeKind { get => JsonNodeKind.Array;} + public override JsonValueKind ValueKind { get => JsonValueKind.Array;} } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonBoolean.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonBoolean.cs index 51b7eef2f21b..7a358dd34c9b 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonBoolean.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonBoolean.cs @@ -102,8 +102,8 @@ public sealed class JsonBoolean : JsonNode, IEquatable public override JsonNode Clone() => new JsonBoolean(Value); /// - /// Returns + /// Returns or , accordingly to the represented value. /// - public override JsonNodeKind NodeKind { get => JsonNodeKind.Boolean; } + public override JsonValueKind ValueKind { get => Value ? JsonValueKind.True : JsonValueKind.False; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs index 3f2c71a73b6f..a3ad197b90be 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs @@ -17,13 +17,23 @@ public abstract class JsonNode { private protected JsonNode() { } - public JsonElement AsJsonElement() { throw null; } + public JsonElement AsJsonElement() => new JsonElement(this); - public abstract JsonNodeKind NodeKind { get; } + public abstract JsonValueKind ValueKind { get; } - public static JsonNode GetNode(JsonElement jsonElement) { throw null; } + public static JsonNode GetNode(JsonElement jsonElement) => (JsonNode)jsonElement._parent; - public static bool TryGetNode(JsonElement jsonElement, out JsonNode jsonNode) { throw null; } + public static bool TryGetNode(JsonElement jsonElement, out JsonNode jsonNode) + { + if (!jsonElement.IsImmutable) + { + jsonNode = (JsonNode)jsonElement._parent; + return true; + } + + jsonNode = null; + return false; + } public static JsonNode Parse(string json) { diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNodeKind.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNodeKind.cs deleted file mode 100644 index d19b6a1353e7..000000000000 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonNodeKind.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Text.Json -{ - /// - /// Specifies the JSON node value. - /// - public enum JsonNodeKind : byte - { - /// - /// Indicates that a value is a JSON object. - /// - Object, - - /// - /// Indicates that a value is a JSON array. - /// - Array, - - /// - /// Indicates that a value is a JSON string. - /// - String, - - /// - /// Indicates that a value is a JSON number. - /// - Number, - - /// - /// Indicates that a value is a JSON boolean. - /// - Boolean - } -} diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNumber.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNumber.cs index 6fc2f149a37a..041131cccb2b 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonNumber.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonNumber.cs @@ -692,8 +692,8 @@ public void SetDouble(double value) public override JsonNode Clone() => new JsonNumber(_value); /// - /// Returns + /// Returns /// - public override JsonNodeKind NodeKind { get => JsonNodeKind.Number; } + public override JsonValueKind ValueKind { get => JsonValueKind.Number; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs index 1b845c3b5f78..1daf37ed9f27 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs @@ -574,8 +574,8 @@ public override JsonNode Clone() } /// - /// Returns + /// Returns /// - public override JsonNodeKind NodeKind { get => JsonNodeKind.Object; } + public override JsonValueKind ValueKind { get => JsonValueKind.Object; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs index 469ac1a93188..812bf398efa7 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs @@ -139,8 +139,8 @@ public string Value public override JsonNode Clone() => new JsonString(Value); /// - /// Returns + /// Returns /// - public override JsonNodeKind NodeKind { get => JsonNodeKind.String; } + public override JsonValueKind ValueKind { get => JsonValueKind.String; } } } From 3fabfe07d4ee5a1513c83fd435a0c7f4f7348126 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Mon, 19 Aug 2019 14:54:21 -0700 Subject: [PATCH 07/29] Work on JsonElement changing --- .../System/Text/Json/Document/JsonElement.cs | 63 +++++++++++++++++-- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index c84891d6a19f..90f2d80c8d5a 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -79,7 +79,7 @@ public JsonElement this[int index] return document.GetArrayIndexElement(_idx, index); } - JsonNode node = (JsonNode)_parent; + var node = (JsonNode)_parent; if (node is JsonArray jsonArray) { @@ -109,7 +109,7 @@ public int GetArrayLength() return document.GetArrayLength(_idx); } - JsonNode node = (JsonNode)_parent; + var node = (JsonNode)_parent; if (node is JsonArray jsonArray) { @@ -306,7 +306,26 @@ public bool TryGetProperty(ReadOnlySpan propertyName, out JsonElement valu { CheckValidInstance(); - return _parent.TryGetNamedPropertyValue(_idx, propertyName, out value); + if (_parent is JsonDocument document) + { + return document.TryGetNamedPropertyValue(_idx, propertyName, out value); + } + + var node = (JsonNode)_parent; + + if (node is JsonObject jsonObject) + { + if (jsonObject.TryGetPropertyValue(propertyName.ToString(), out JsonNode nodeValue)) + { + value = nodeValue.AsJsonElement(); + return true; + } + + value = default; + return false; + } + + throw new InvalidOperationException(); } /// @@ -342,7 +361,26 @@ public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out JsonElement { CheckValidInstance(); - return _parent.TryGetNamedPropertyValue(_idx, utf8PropertyName, out value); + if (_parent is JsonDocument document) + { + return document.TryGetNamedPropertyValue(_idx, utf8PropertyName, out value); + } + + var node = (JsonNode)_parent; + + if (node is JsonObject jsonObject) + { + if (jsonObject.TryGetPropertyValue(utf8PropertyName.ToString(), out JsonNode nodeValue)) + { + value = nodeValue.AsJsonElement(); + return true; + } + + value = default; + return false; + } + + throw new InvalidOperationException(); } /// @@ -390,7 +428,19 @@ public string GetString() { CheckValidInstance(); - return _parent.GetString(_idx, JsonTokenType.String); + if (_parent is JsonDocument document) + { + return document.GetString(_idx, JsonTokenType.String); + } + + var node = (JsonNode)_parent; + + if (node is JsonString jsonString) + { + return jsonString.Value; + } + + throw new InvalidOperationException(); } /// @@ -414,7 +464,8 @@ public bool TryGetBytesFromBase64(out byte[] value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + JsonDocument document = (JsonDocument)_parent; + return document.TryGetValue(_idx, out value); } /// From 161404d5553012b678eb14ae4b0853c8bc762d1f Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 20 Aug 2019 09:16:17 -0700 Subject: [PATCH 08/29] JsonElement and JsonString adjusted to transformation API --- src/System.Text.Json/ref/System.Text.Json.cs | 27 +- .../Document/JsonElement.ArrayEnumerator.cs | 8 +- .../Document/JsonElement.ObjectEnumerator.cs | 8 +- .../System/Text/Json/Document/JsonElement.cs | 238 +++++++++++++++--- .../src/System/Text/Json/Node/JsonString.cs | 86 ++++++- 5 files changed, 303 insertions(+), 64 deletions(-) diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index 6cecbb9c5474..71267069c69b 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -37,7 +37,7 @@ public JsonArray(System.Collections.Generic.IEnumerable values) { } public int Count { get { throw null; } } public bool IsReadOnly { get { throw null; } } public System.Text.Json.JsonNode this[int idx] { get { throw null; } set { } } - public override System.Text.Json.JsonNodeKind NodeKind { get { throw null; } } + public override System.Text.Json.JsonValueKind ValueKind { get { throw null; } } public void Add(bool value) { } public void Add(byte value) { } public void Add(decimal value) { } @@ -107,8 +107,8 @@ public sealed partial class JsonBoolean : System.Text.Json.JsonNode, System.IEqu { public JsonBoolean() { } public JsonBoolean(bool value) { } - public override System.Text.Json.JsonNodeKind NodeKind { get { throw null; } } public bool Value { get { throw null; } set { } } + public override System.Text.Json.JsonValueKind ValueKind { get { throw null; } } public override System.Text.Json.JsonNode Clone() { throw null; } public override bool Equals(object obj) { throw null; } public bool Equals(System.Text.Json.JsonBoolean other) { throw null; } @@ -150,6 +150,7 @@ public readonly partial struct JsonElement { private readonly object _dummy; private readonly int _dummyPrimitive; + public bool IsImmutable { get { throw null; } } public System.Text.Json.JsonElement this[int index] { get { throw null; } } public System.Text.Json.JsonValueKind ValueKind { get { throw null; } } public System.Text.Json.JsonElement Clone() { throw null; } @@ -270,7 +271,7 @@ protected JsonNamingPolicy() { } public abstract partial class JsonNode { internal JsonNode() { } - public abstract System.Text.Json.JsonNodeKind NodeKind { get; } + public abstract System.Text.Json.JsonValueKind ValueKind { get; } public System.Text.Json.JsonElement AsJsonElement() { throw null; } public abstract System.Text.Json.JsonNode Clone(); public static System.Text.Json.JsonNode DeepCopy(System.Text.Json.JsonDocument jsonDocument) { throw null; } @@ -284,14 +285,6 @@ internal JsonNode() { } public static System.Text.Json.JsonNode Parse(string json) { throw null; } public static bool TryGetNode(System.Text.Json.JsonElement jsonElement, out System.Text.Json.JsonNode jsonNode) { throw null; } } - public enum JsonNodeKind : byte - { - Object = (byte)0, - Array = (byte)1, - String = (byte)2, - Number = (byte)3, - Boolean = (byte)4, - } public sealed partial class JsonNumber : System.Text.Json.JsonNode, System.IEquatable { public JsonNumber() { } @@ -311,7 +304,7 @@ public JsonNumber(ushort value) { } public JsonNumber(uint value) { } [System.CLSCompliantAttribute(false)] public JsonNumber(ulong value) { } - public override System.Text.Json.JsonNodeKind NodeKind { get { throw null; } } + public override System.Text.Json.JsonValueKind ValueKind { get { throw null; } } public override System.Text.Json.JsonNode Clone() { throw null; } public override bool Equals(object obj) { throw null; } public bool Equals(System.Text.Json.JsonNumber other) { throw null; } @@ -386,9 +379,9 @@ public sealed partial class JsonObject : System.Text.Json.JsonNode, System.Colle public JsonObject(System.Collections.Generic.IEnumerable> jsonProperties, System.Text.Json.DuplicatePropertyNameHandling duplicatePropertyNameHandling = System.Text.Json.DuplicatePropertyNameHandling.Replace) { } public JsonObject(System.Text.Json.DuplicatePropertyNameHandling duplicatePropertyNameHandling = System.Text.Json.DuplicatePropertyNameHandling.Replace) { } public System.Text.Json.JsonNode this[string propertyName] { get { throw null; } set { } } - public override System.Text.Json.JsonNodeKind NodeKind { get { throw null; } } public System.Collections.Generic.ICollection PropertyNames { get { throw null; } } public System.Collections.Generic.ICollection PropertyValues { get { throw null; } } + public override System.Text.Json.JsonValueKind ValueKind { get { throw null; } } public void Add(System.Collections.Generic.KeyValuePair jsonProperty) { } public void Add(string propertyName, bool propertyValue) { } public void Add(string propertyName, byte propertyValue) { } @@ -505,16 +498,22 @@ public JsonString(System.DateTimeOffset value) { } public JsonString(System.Guid value) { } public JsonString(System.ReadOnlySpan value) { } public JsonString(string value) { } - public override System.Text.Json.JsonNodeKind NodeKind { get { throw null; } } public string Value { get { throw null; } set { } } + public override System.Text.Json.JsonValueKind ValueKind { get { throw null; } } public override System.Text.Json.JsonNode Clone() { throw null; } public override bool Equals(object obj) { throw null; } public bool Equals(System.Text.Json.JsonString other) { throw null; } + public System.DateTime GetDateTime() { throw null; } + public System.DateTimeOffset GetDateTimeOffset() { throw null; } + public System.Guid GetGuid() { throw null; } public override int GetHashCode() { throw null; } public static bool operator ==(System.Text.Json.JsonString left, System.Text.Json.JsonString right) { throw null; } public static implicit operator System.Text.Json.JsonString (string value) { throw null; } public static bool operator !=(System.Text.Json.JsonString left, System.Text.Json.JsonString right) { throw null; } public override string ToString() { throw null; } + public bool TryGetDateTime(out System.DateTime value) { throw null; } + public bool TryGetDateTimeOffset(out System.DateTimeOffset value) { throw null; } + public bool TryGetGuid(out System.Guid value) { throw null; } } public enum JsonTokenType : byte { diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs index 2362bddc1d2e..37fdfede6d56 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs @@ -26,12 +26,13 @@ internal ArrayEnumerator(JsonElement target) _target = target; _curIdx = -1; - _endIdx = _target._parent.GetEndIndex(_target._idx, includeEndElement: false); + JsonDocument document = (JsonDocument)_target._parent; + _endIdx = document.GetEndIndex(_target._idx, includeEndElement: false); } /// public JsonElement Current => - _curIdx < 0 ? default : new JsonElement(_target._parent, _curIdx); + _curIdx < 0 ? default : new JsonElement((JsonDocument)_target._parent, _curIdx); /// /// Returns an enumerator that iterates through a collection. @@ -82,7 +83,8 @@ public bool MoveNext() } else { - _curIdx = _target._parent.GetEndIndex(_curIdx, includeEndElement: true); + JsonDocument document = (JsonDocument)_target._parent; + _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true); } return _curIdx < _endIdx; diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs index 2b2b40a99b3e..288c4e2a4cb4 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs @@ -26,14 +26,15 @@ internal ObjectEnumerator(JsonElement target) _target = target; _curIdx = -1; - _endIdx = _target._parent.GetEndIndex(_target._idx, includeEndElement: false); + JsonDocument document = (JsonDocument)_target._parent; + _endIdx = document.GetEndIndex(_target._idx, includeEndElement: false); } /// public JsonProperty Current => _curIdx < 0 ? default : - new JsonProperty(new JsonElement(_target._parent, _curIdx)); + new JsonProperty(new JsonElement((JsonDocument)_target._parent, _curIdx)); /// /// Returns an enumerator that iterates the properties of an object. @@ -90,7 +91,8 @@ public bool MoveNext() } else { - _curIdx = _target._parent.GetEndIndex(_curIdx, includeEndElement: true); + JsonDocument document = (JsonDocument)_target._parent; + _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true); } // _curIdx is now pointing at a property name, move one more to get the value diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index 90f2d80c8d5a..3c6cae90406a 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -79,9 +79,7 @@ public JsonElement this[int index] return document.GetArrayIndexElement(_idx, index); } - var node = (JsonNode)_parent; - - if (node is JsonArray jsonArray) + if (_parent is JsonArray jsonArray) { return jsonArray[index].AsJsonElement(); } @@ -109,9 +107,7 @@ public int GetArrayLength() return document.GetArrayLength(_idx); } - var node = (JsonNode)_parent; - - if (node is JsonArray jsonArray) + if (_parent is JsonArray jsonArray) { return jsonArray.Count; } @@ -270,7 +266,9 @@ public JsonElement GetProperty(ReadOnlySpan utf8PropertyName) public bool TryGetProperty(string propertyName, out JsonElement value) { if (propertyName == null) + { throw new ArgumentNullException(nameof(propertyName)); + } return TryGetProperty(propertyName.AsSpan(), out value); } @@ -311,9 +309,7 @@ public bool TryGetProperty(ReadOnlySpan propertyName, out JsonElement valu return document.TryGetNamedPropertyValue(_idx, propertyName, out value); } - var node = (JsonNode)_parent; - - if (node is JsonObject jsonObject) + if (_parent is JsonObject jsonObject) { if (jsonObject.TryGetPropertyValue(propertyName.ToString(), out JsonNode nodeValue)) { @@ -366,9 +362,7 @@ public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out JsonElement return document.TryGetNamedPropertyValue(_idx, utf8PropertyName, out value); } - var node = (JsonNode)_parent; - - if (node is JsonObject jsonObject) + if (_parent is JsonObject jsonObject) { if (jsonObject.TryGetPropertyValue(utf8PropertyName.ToString(), out JsonNode nodeValue)) { @@ -433,9 +427,7 @@ public string GetString() return document.GetString(_idx, JsonTokenType.String); } - var node = (JsonNode)_parent; - - if (node is JsonString jsonString) + if (_parent is JsonString jsonString) { return jsonString.Value; } @@ -517,7 +509,17 @@ public bool TryGetSByte(out sbyte value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonNumber jsonNumber) + { + return jsonNumber.TryGetSByte(out value); + } + + throw new InvalidOperationException(); } /// @@ -565,7 +567,17 @@ public bool TryGetByte(out byte value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonNumber jsonNumber) + { + return jsonNumber.TryGetByte(out value); + } + + throw new InvalidOperationException(); } /// @@ -615,7 +627,17 @@ public bool TryGetInt16(out short value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonNumber jsonNumber) + { + return jsonNumber.TryGetInt16(out value); + } + + throw new InvalidOperationException(); } /// @@ -663,7 +685,17 @@ public bool TryGetUInt16(out ushort value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonNumber jsonNumber) + { + return jsonNumber.TryGetUInt16(out value); + } + + throw new InvalidOperationException(); } /// @@ -714,7 +746,17 @@ public bool TryGetInt32(out int value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonNumber jsonNumber) + { + return jsonNumber.TryGetInt32(out value); + } + + throw new InvalidOperationException(); } /// @@ -762,7 +804,17 @@ public bool TryGetUInt32(out uint value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonNumber jsonNumber) + { + return jsonNumber.TryGetUInt32(out value); + } + + throw new InvalidOperationException(); } /// @@ -813,7 +865,17 @@ public bool TryGetInt64(out long value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonNumber jsonNumber) + { + return jsonNumber.TryGetInt64(out value); + } + + throw new InvalidOperationException(); } /// @@ -864,7 +926,17 @@ public bool TryGetUInt64(out ulong value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonNumber jsonNumber) + { + return jsonNumber.TryGetUInt64(out value); + } + + throw new InvalidOperationException(); } /// @@ -924,7 +996,17 @@ public bool TryGetDouble(out double value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonNumber jsonNumber) + { + return jsonNumber.TryGetDouble(out value); + } + + throw new InvalidOperationException(); } /// @@ -991,7 +1073,17 @@ public bool TryGetSingle(out float value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonNumber jsonNumber) + { + return jsonNumber.TryGetSingle(out value); + } + + throw new InvalidOperationException(); } /// @@ -1050,7 +1142,17 @@ public bool TryGetDecimal(out decimal value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonNumber jsonNumber) + { + return jsonNumber.TryGetDecimal(out value); + } + + throw new InvalidOperationException(); } /// @@ -1101,7 +1203,17 @@ public bool TryGetDateTime(out DateTime value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonString jsonString) + { + return jsonString.TryGetDateTime(out value); + } + + throw new InvalidOperationException(); } /// @@ -1152,7 +1264,17 @@ public bool TryGetDateTimeOffset(out DateTimeOffset value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonString jsonString) + { + return jsonString.TryGetDateTimeOffset(out value); + } + + throw new InvalidOperationException(); } /// @@ -1203,7 +1325,17 @@ public bool TryGetGuid(out Guid value) { CheckValidInstance(); - return _parent.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + if (_parent is JsonString jsonString) + { + return jsonString.TryGetGuid(out value); + } + + throw new InvalidOperationException(); } /// @@ -1237,7 +1369,8 @@ internal string GetPropertyName() { CheckValidInstance(); - return _parent.GetNameOfPropertyValue(_idx); + var document = (JsonDocument)_parent; + return document.GetNameOfPropertyValue(_idx); } /// @@ -1253,14 +1386,21 @@ public string GetRawText() { CheckValidInstance(); - return _parent.GetRawValueAsString(_idx); + if (_parent is JsonDocument document) + { + return document.GetRawValueAsString(_idx); + } + + var jsonNode = (JsonNode)_parent; + return jsonNode.ToString(); } internal string GetPropertyRawText() { CheckValidInstance(); - return _parent.GetPropertyRawValueAsString(_idx); + var document = (JsonDocument)_parent; + return document.GetPropertyRawValueAsString(_idx); } /// @@ -1351,14 +1491,16 @@ internal bool TextEqualsHelper(ReadOnlySpan utf8Text, bool isPropertyName) { CheckValidInstance(); - return _parent.TextEquals(_idx, utf8Text, isPropertyName); + var document = (JsonDocument)_parent; + return document.TextEquals(_idx, utf8Text, isPropertyName); } internal bool TextEqualsHelper(ReadOnlySpan text, bool isPropertyName) { CheckValidInstance(); - return _parent.TextEquals(_idx, text, isPropertyName); + var document = (JsonDocument)_parent; + return document.TextEquals(_idx, text, isPropertyName); } /// @@ -1383,7 +1525,8 @@ public void WriteTo(Utf8JsonWriter writer) CheckValidInstance(); - _parent.WriteElementTo(_idx, writer); + var document = (JsonDocument)_parent; + document.WriteElementTo(_idx, writer); } /// @@ -1485,7 +1628,13 @@ public override string ToString() { // null parent should have hit the None case Debug.Assert(_parent != null); - return _parent.GetRawValueAsString(_idx); + if (_parent is JsonDocument document) + { + return document.GetRawValueAsString(_idx); + } + + var jsonNode = (JsonNode)_parent; + return jsonNode.ToString(); } case JsonTokenType.String: return GetString(); @@ -1515,12 +1664,18 @@ public JsonElement Clone() { CheckValidInstance(); - if (!_parent.IsDisposable) + if (_parent is JsonDocument docuemnt) { - return this; + if (!docuemnt.IsDisposable) + { + return this; + } + + return docuemnt.CloneElement(_idx); } - return _parent.CloneElement(_idx); + var jsonNode = (JsonNode)_parent; + return jsonNode.Clone().AsJsonElement(); } private void CheckValidInstance() @@ -1530,7 +1685,10 @@ private void CheckValidInstance() throw new InvalidOperationException(); } - Debug.Assert(_parent is JsonDocument || _parent is JsonNode, "JsonElement can be backed only by JsonDocument or JsonNode"); + if (!(_parent is JsonDocument) && !(_parent is JsonNode)) + { + throw new NotSupportedException("JsonElement can be backed only by JsonDocument or JsonNode"); + } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs index 812bf398efa7..c87e3a205dc0 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Globalization; + namespace System.Text.Json { /// @@ -38,16 +40,28 @@ public sealed class JsonString : JsonNode, IEquatable public JsonString(Guid value) => Value = value.ToString(); /// - /// Initializes a new instance of the with a string representation of the structure. + /// Initializes a new instance of the with an ISO 8601 representation of the structure. /// /// The value to represent as a JSON string. - public JsonString(DateTime value) => Value = value.ToString(); + /// + /// Text value of this instance is not in an ISO 8601 defined DateTimeOffset format. + /// + /// + /// The date and time is outside the range of dates supported by the calendar used by the invariant culture. + /// + public JsonString(DateTime value) => Value = value.ToString("s", CultureInfo.InvariantCulture); /// - /// Initializes a new instance of the with a string representation of the structure. + /// Initializes a new instance of the with an ISO 8601 representation of the structure. /// /// The value to represent as a JSON string. - public JsonString(DateTimeOffset value) => Value = value.ToString(); + /// + /// Text value of this instance is not in an ISO 8601 defined DateTimeOffset format. + /// + /// + /// The date and time is outside the range of dates supported by the calendar used by the invariant culture. + /// + public JsonString(DateTimeOffset value) => Value = value.ToString("s", CultureInfo.InvariantCulture); /// /// Gets or sets the text value represented by the instance. @@ -67,6 +81,70 @@ public string Value /// The value represented by this instance. public override string ToString() => _value; + /// + /// Converts the ISO 8601 text value of this instance to its equivalent. + /// + /// A equivalent to the text stored by this instance. + /// + /// Text value of this instance is not in an ISO 8601 defined DateTime format. + /// + public DateTime GetDateTime() => DateTime.ParseExact(_value, "s", CultureInfo.InvariantCulture); + + /// + /// Converts the ISO 8601 text value of this instance to equivalent. + /// + /// A equivalent to the text stored by this instance. + /// + /// Text value of this instance is not in an ISO 8601 defined DateTimeOffset format. + /// + public DateTimeOffset GetDateTimeOffset() => DateTimeOffset.ParseExact(_value, "s", CultureInfo.InvariantCulture); + + /// + /// Converts the text value of this instance to its equivalent. + /// + /// A equivalent to the text stored by this instance. + /// + /// Text value of this instance is not in a GUID recognized format. + /// + public Guid GetGuid() => Guid.Parse(_value); + + /// + /// Converts the ISO 8601 text value of this instance to its ISO 8601 equivalent. + /// A return value indicates whether the conversion succeeded. + /// + /// + /// When this method returns, contains the see cref="DateTime"/> value equivalent of the text contained in this instance, + /// if the conversion succeeded, or zero if the conversion failed. + /// + /// + /// if instance was converted successfully; + /// otherwise, + /// + public bool TryGetDateTime(out DateTime value) => DateTime.TryParseExact(_value, "s", CultureInfo.InvariantCulture, DateTimeStyles.None, out value); + + /// + /// Converts the ISO 8601 text value of this instance to its equivalent. + /// A return value indicates whether the conversion succeeded. + /// + /// + /// When this method returns, contains the value equivalent of the text contained in this instance, + /// if the conversion succeeded, or zero if the conversion failed. + /// + /// + /// if instance was converted successfully; + /// otherwise, + /// + public bool TryGetDateTimeOffset(out DateTimeOffset value) => DateTimeOffset.TryParseExact(_value, "s", CultureInfo.InvariantCulture, DateTimeStyles.None, out value); + + /// + /// Converts the text value of this instance to its equivalent. + /// + /// A equivalent to the text stored by this instance. + /// + /// Text value of this instance is not in a GUID recognized format. + /// + public bool TryGetGuid(out Guid value) => Guid.TryParse(_value, out value); + /// /// Converts a to a . /// From 2e387d1699dd2d36f81230ba445460c55ae8cd3d Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 20 Aug 2019 11:13:07 -0700 Subject: [PATCH 09/29] work on enumerators --- .../Document/JsonElement.ObjectEnumerator.cs | 2 + .../System/Text/Json/Document/JsonElement.cs | 21 +++++---- .../src/System/Text/Json/Node/JsonNode.cs | 4 +- .../tests/JsonNodeTransformationsTests.cs | 46 +++++++++++++++++++ src/System.Text.Json/tests/JsonObjectTests.cs | 26 +++++++---- src/System.Text.Json/tests/JsonStringTests.cs | 27 +++++++---- 6 files changed, 99 insertions(+), 27 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs index 288c4e2a4cb4..f1e0583352a9 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs @@ -19,6 +19,7 @@ public struct ObjectEnumerator : IEnumerable, IEnumerator /// A representing the value of the requested property. /// - /// + /// /// /// This value's is not . /// @@ -173,7 +173,7 @@ public JsonElement GetProperty(string propertyName) /// /// A representing the value of the requested property. /// - /// + /// /// /// This value's is not . /// @@ -222,7 +222,7 @@ public JsonElement GetProperty(ReadOnlySpan propertyName) /// /// The parent has been disposed. /// - /// + /// public JsonElement GetProperty(ReadOnlySpan utf8PropertyName) { if (TryGetProperty(utf8PropertyName, out JsonElement property)) @@ -262,7 +262,7 @@ public JsonElement GetProperty(ReadOnlySpan utf8PropertyName) /// /// The parent has been disposed. /// - /// + /// public bool TryGetProperty(string propertyName, out JsonElement value) { if (propertyName == null) @@ -293,7 +293,7 @@ public bool TryGetProperty(string propertyName, out JsonElement value) /// /// if the property was found, otherwise. /// - /// + /// /// /// This value's is not . /// @@ -346,7 +346,7 @@ public bool TryGetProperty(ReadOnlySpan propertyName, out JsonElement valu /// /// if the property was found, otherwise. /// - /// + /// /// /// This value's is not . /// @@ -1541,7 +1541,7 @@ public void WriteTo(Utf8JsonWriter writer) /// /// The parent has been disposed. /// - public ArrayEnumerator EnumerateArray() + public ArrayEnumerator EnumerateJsonDocumentArray() { CheckValidInstance(); @@ -1567,9 +1567,12 @@ public ArrayEnumerator EnumerateArray() /// /// The parent has been disposed. /// - public ObjectEnumerator EnumerateObject() + public ObjectEnumerator EnumerateJsonObject() { - CheckValidInstance(); + if (!(_parent is JsonDocument)) + { + throw new InvalidOperationException(); + } JsonTokenType tokenType = TokenType; diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs index a3ad197b90be..f9552b12a12c 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs @@ -85,14 +85,14 @@ public static JsonNode DeepCopy(JsonElement jsonElement) { case JsonValueKind.Object: JsonObject jsonObject = new JsonObject(); - foreach (JsonProperty property in jsonElement.EnumerateObject()) + foreach (JsonProperty property in jsonElement.EnumerateJsonObject()) { jsonObject.Add(property.Name, DeepCopy(property.Value)); } return jsonObject; case JsonValueKind.Array: JsonArray jsonArray = new JsonArray(); - foreach (JsonElement element in jsonElement.EnumerateArray()) + foreach (JsonElement element in jsonElement.EnumerateJsonDocumentArray()) { jsonArray.Add(DeepCopy(element)); } diff --git a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs index bc4fc6f50e33..71544bdfae14 100644 --- a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs +++ b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs @@ -130,5 +130,51 @@ public static void TestCloneJsonNode() jsonObject.Add("new one", 123); Assert.Equal(4, jsonObjectCopy.PropertyNames.Count); } + + [Fact] + public static void TestAsJsonElement() + { + var jsonObject = new JsonObject + { + { "text", "property value" }, + { "boolean", true }, + { "number", 15 }, + { "array", new JsonString[] { "value1", "value2"} } + }; + + JsonElement jsonElement = jsonObject.AsJsonElement(); + Assert.False(jsonElement.IsImmutable); + + JsonElement.ObjectEnumerator enumerator = jsonElement.EnumerateObject(); + + Assert.Equal("text", enumerator.Current.Name); + Assert.Equal(JsonValueKind.String, enumerator.Current.Value.ValueKind); + Assert.Equal("property value", enumerator.Current.Value.GetString()); + enumerator.MoveNext(); + + Assert.Equal("boolean", enumerator.Current.Name); + Assert.Equal(JsonValueKind.String, enumerator.Current.Value.ValueKind); + Assert.True( enumerator.Current.Value.GetBoolean()); + enumerator.MoveNext(); + + Assert.Equal("number", enumerator.Current.Name); + Assert.Equal(15, enumerator.Current.Value.GetInt32()); + Assert.Equal(JsonValueKind.Number, enumerator.Current.Value.ValueKind); + enumerator.MoveNext(); + + Assert.Equal("array", enumerator.Current.Name); + Assert.Equal(2, enumerator.Current.Value.GetArrayLength()); + Assert.Equal(JsonValueKind.Array, enumerator.Current.Value.ValueKind); + JsonElement.ArrayEnumerator innerEnumerator = enumerator.Current.Value.EnumerateArray(); + + Assert.Equal(JsonValueKind.String, innerEnumerator.Current.ValueKind); + Assert.Equal("value1", innerEnumerator.Current.GetString()); + innerEnumerator.MoveNext(); + + Assert.Equal(JsonValueKind.String, innerEnumerator.Current.ValueKind); + Assert.Equal("value2", innerEnumerator.Current.GetString()); + innerEnumerator.Dispose(); + enumerator.Dispose(); + } } } diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index 30626bd2e0e4..f158afc95abb 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Globalization; using Xunit; namespace System.Text.Json.Tests @@ -131,20 +132,29 @@ public static void TestGuid() Assert.Equal(guidString, (JsonString)jsonObject["guid"]); } - [Fact] - public static void TestDateTime() + public static IEnumerable DateTimeData => + new List + { + new object[] { new DateTime(DateTime.MinValue.Ticks, DateTimeKind.Utc) }, + new object[] { new DateTime(2019, 1, 1) }, + new object[] { new DateTime(2019, 1, 1, new GregorianCalendar()) }, + new object[] { new DateTime(2019, 1, 1, new ChineseLunisolarCalendar()) } + }; + + [Theory] + [MemberData(nameof(DateTimeData))] + public static void TestDateTime(DateTime dateTime) { - DateTime dateTime = new DateTime(DateTime.MinValue.Ticks); var jsonObject = new JsonObject { { "dateTime", dateTime } }; - Assert.Equal(dateTime.ToString(), (JsonString)jsonObject["dateTime"]); + Assert.Equal(dateTime.ToString("s", CultureInfo.InvariantCulture), (JsonString)jsonObject["dateTime"]); } - [Fact] - public static void TestDateTimeOffset() + [Theory] + [MemberData(nameof(DateTimeData))] + public static void TestDateTimeOffset(DateTimeOffset dateTimeOffset) { - DateTimeOffset dateTimeOffset = new DateTime(DateTime.MinValue.Ticks); var jsonObject = new JsonObject { { "dateTimeOffset", dateTimeOffset } }; - Assert.Equal(dateTimeOffset.ToString(), (JsonString)jsonObject["dateTimeOffset"]); + Assert.Equal(dateTimeOffset.ToString("s", CultureInfo.InvariantCulture), (JsonString)jsonObject["dateTimeOffset"]); } [Fact] diff --git a/src/System.Text.Json/tests/JsonStringTests.cs b/src/System.Text.Json/tests/JsonStringTests.cs index 1dde1e6717c7..7644fd9d6075 100644 --- a/src/System.Text.Json/tests/JsonStringTests.cs +++ b/src/System.Text.Json/tests/JsonStringTests.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; +using System.Globalization; using Xunit; namespace System.Text.Json.Tests @@ -79,20 +81,29 @@ public static void TestGuid() Assert.Equal(guidString, jsonString); } - [Fact] - public static void TestDateTime() + public static IEnumerable DateTimeData => + new List + { + new object[] { new DateTime(DateTime.MinValue.Ticks, DateTimeKind.Utc) }, + new object[] { new DateTime(2019, 1, 1) }, + new object[] { new DateTime(2019, 1, 1, new GregorianCalendar()) }, + new object[] { new DateTime(2019, 1, 1, new ChineseLunisolarCalendar()) } + }; + + [Theory] + [MemberData(nameof(DateTimeData))] + public static void TestDateTime(DateTime dateTime) { - DateTime dateTime = new DateTime(DateTime.MinValue.Ticks); var jsonString = new JsonString(dateTime); - Assert.Equal(dateTime.ToString(), jsonString); + Assert.Equal(dateTime.ToString("s", CultureInfo.InvariantCulture), jsonString); } - [Fact] - public static void TestDateTimeOffset() + [Theory] + [MemberData(nameof(DateTimeData))] + public static void TestDateTimeOffset(DateTimeOffset dateTimeOffset) { - DateTimeOffset dateTimeOffset = new DateTime(DateTime.MinValue.Ticks); var jsonString = new JsonString(dateTimeOffset); - Assert.Equal(dateTimeOffset.ToString(), jsonString); + Assert.Equal(dateTimeOffset.ToString("s", CultureInfo.InvariantCulture), jsonString); } [Fact] From e52c48cef15aa60d37c12d6a420f98f2862e9bf8 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 20 Aug 2019 13:08:04 -0700 Subject: [PATCH 10/29] work on enumerators --- .../Document/JsonElement.ObjectEnumerator.cs | 89 +++++++++++++------ .../System/Text/Json/Document/JsonElement.cs | 87 +++++++++++++----- .../System/Text/Json/Document/JsonProperty.cs | 6 +- .../src/System/Text/Json/Node/JsonNode.cs | 4 +- .../Text/Json/Node/JsonObjectEnumerator.cs | 5 +- .../tests/JsonNodeTransformationsTests.cs | 16 ++-- 6 files changed, 145 insertions(+), 62 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs index f1e0583352a9..2756899cd87a 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs @@ -19,24 +19,48 @@ public struct ObjectEnumerator : IEnumerable, IEnumerator - public JsonProperty Current => - _curIdx < 0 ? - default : - new JsonProperty(new JsonElement((JsonDocument)_target._parent, _curIdx)); + public JsonProperty Current + { + get + { + if (_target._parent is JsonDocument document) + { + if (_curIdx < 0) + { + return default; + } + return new JsonProperty(new JsonElement(document, _curIdx)); + } + + KeyValuePair propertyPair = _jsonObjectEnumerator.Value.Current; + return new JsonProperty(propertyPair.Value.AsJsonElement(), propertyPair.Key); + } + } /// /// Returns an enumerator that iterates the properties of an object. @@ -68,12 +92,20 @@ public ObjectEnumerator GetEnumerator() public void Dispose() { _curIdx = _endIdx; + if (_jsonObjectEnumerator.HasValue) + { + _jsonObjectEnumerator.Value.Dispose(); + } } /// public void Reset() { _curIdx = -1; + if (_jsonObjectEnumerator.HasValue) + { + _jsonObjectEnumerator.Value.Reset(); + } } /// @@ -82,25 +114,30 @@ public void Reset() /// public bool MoveNext() { - if (_curIdx >= _endIdx) - { - return false; - } - - if (_curIdx < 0) + if (_target._parent is JsonDocument document) { - _curIdx = _target._idx + JsonDocument.DbRow.Size; + if (_curIdx >= _endIdx) + { + return false; + } + + if (_curIdx < 0) + { + _curIdx = _target._idx + JsonDocument.DbRow.Size; + } + else + { + _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true); + } + + // _curIdx is now pointing at a property name, move one more to get the value + _curIdx += JsonDocument.DbRow.Size; + + return _curIdx < _endIdx; } - else - { - JsonDocument document = (JsonDocument)_target._parent; - _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true); - } - - // _curIdx is now pointing at a property name, move one more to get the value - _curIdx += JsonDocument.DbRow.Size; - return _curIdx < _endIdx; + Debug.Assert(_jsonObjectEnumerator.HasValue); + return _jsonObjectEnumerator.Value.MoveNext(); } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index b6b39b61c7df..701814527ab3 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -53,7 +53,19 @@ private JsonTokenType TokenType /// /// The parent has been disposed. /// - public JsonValueKind ValueKind => TokenType.ToValueKind(); + public JsonValueKind ValueKind + { + get + { + if (_parent is JsonDocument) + { + return TokenType.ToValueKind(); + } + + var jsonNode = (JsonNode)_parent; + return jsonNode.ValueKind; + } + } /// /// Get the value at a specified index when the current value is a @@ -129,7 +141,7 @@ public int GetArrayLength() /// /// A representing the value of the requested property. /// - /// + /// /// /// This value's is not . /// @@ -173,7 +185,7 @@ public JsonElement GetProperty(string propertyName) /// /// A representing the value of the requested property. /// - /// + /// /// /// This value's is not . /// @@ -222,7 +234,7 @@ public JsonElement GetProperty(ReadOnlySpan propertyName) /// /// The parent has been disposed. /// - /// + /// public JsonElement GetProperty(ReadOnlySpan utf8PropertyName) { if (TryGetProperty(utf8PropertyName, out JsonElement property)) @@ -262,7 +274,7 @@ public JsonElement GetProperty(ReadOnlySpan utf8PropertyName) /// /// The parent has been disposed. /// - /// + /// public bool TryGetProperty(string propertyName, out JsonElement value) { if (propertyName == null) @@ -293,7 +305,7 @@ public bool TryGetProperty(string propertyName, out JsonElement value) /// /// if the property was found, otherwise. /// - /// + /// /// /// This value's is not . /// @@ -346,7 +358,7 @@ public bool TryGetProperty(ReadOnlySpan propertyName, out JsonElement valu /// /// if the property was found, otherwise. /// - /// + /// /// /// This value's is not . /// @@ -396,12 +408,22 @@ public bool GetBoolean() // CheckValidInstance is redundant. Asking for the type will // return None, which then throws the same exception in the return statement. - JsonTokenType type = TokenType; + if (_parent is JsonDocument document) + { + JsonTokenType type = TokenType; + + return + type == JsonTokenType.True ? true : + type == JsonTokenType.False ? false : + throw ThrowHelper.GetJsonElementWrongTypeException(nameof(Boolean), type); + } - return - type == JsonTokenType.True ? true : - type == JsonTokenType.False ? false : - throw ThrowHelper.GetJsonElementWrongTypeException(nameof(Boolean), type); + if (_parent is JsonBoolean jsonBoolean) + { + return jsonBoolean.Value; + } + + throw new InvalidOperationException(); } /// @@ -1541,15 +1563,25 @@ public void WriteTo(Utf8JsonWriter writer) /// /// The parent has been disposed. /// - public ArrayEnumerator EnumerateJsonDocumentArray() + public ArrayEnumerator EnumerateArray() { CheckValidInstance(); - JsonTokenType tokenType = TokenType; + if (_parent is JsonDocument) + { + JsonTokenType tokenType = TokenType; - if (tokenType != JsonTokenType.StartArray) + if (tokenType != JsonTokenType.StartArray) + { + throw ThrowHelper.GetJsonElementWrongTypeException(JsonTokenType.StartArray, tokenType); + } + } + else if (_parent is JsonNode node) { - throw ThrowHelper.GetJsonElementWrongTypeException(JsonTokenType.StartArray, tokenType); + if (node.ValueKind != JsonValueKind.Array) + { + throw new InvalidOperationException(); + } } return new ArrayEnumerator(this); @@ -1567,18 +1599,25 @@ public ArrayEnumerator EnumerateJsonDocumentArray() /// /// The parent has been disposed. /// - public ObjectEnumerator EnumerateJsonObject() + public ObjectEnumerator EnumerateObject() { - if (!(_parent is JsonDocument)) - { - throw new InvalidOperationException(); - } + CheckValidInstance(); - JsonTokenType tokenType = TokenType; + if (_parent is JsonDocument) + { + JsonTokenType tokenType = TokenType; - if (tokenType != JsonTokenType.StartObject) + if (tokenType != JsonTokenType.StartObject) + { + throw ThrowHelper.GetJsonElementWrongTypeException(JsonTokenType.StartObject, tokenType); + } + } + else if (_parent is JsonNode node) { - throw ThrowHelper.GetJsonElementWrongTypeException(JsonTokenType.StartObject, tokenType); + if (node.ValueKind != JsonValueKind.Object) + { + throw new InvalidOperationException(); + } } return new ObjectEnumerator(this); diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonProperty.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonProperty.cs index 79d8fd810809..ac6265df3444 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonProperty.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonProperty.cs @@ -17,16 +17,18 @@ public readonly struct JsonProperty /// The value of this property. /// public JsonElement Value { get; } + private string _name { get; } - internal JsonProperty(JsonElement value) + internal JsonProperty(JsonElement value, string name = null) { Value = value; + _name = name; } /// /// The name of this property. /// - public string Name => Value.GetPropertyName(); + public string Name => _name ?? Value.GetPropertyName(); /// /// Compares to the name of this property. diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs index f9552b12a12c..a3ad197b90be 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs @@ -85,14 +85,14 @@ public static JsonNode DeepCopy(JsonElement jsonElement) { case JsonValueKind.Object: JsonObject jsonObject = new JsonObject(); - foreach (JsonProperty property in jsonElement.EnumerateJsonObject()) + foreach (JsonProperty property in jsonElement.EnumerateObject()) { jsonObject.Add(property.Name, DeepCopy(property.Value)); } return jsonObject; case JsonValueKind.Array: JsonArray jsonArray = new JsonArray(); - foreach (JsonElement element in jsonElement.EnumerateJsonDocumentArray()) + foreach (JsonElement element in jsonElement.EnumerateArray()) { jsonArray.Add(DeepCopy(element)); } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs index 2723e21ccc3d..7db2e9abc1e4 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs @@ -14,7 +14,10 @@ public struct JsonObjectEnumerator : IEnumerator> /// Initializes a new instance of the class supporting an interation over provided JSON object. /// /// JSON object to iterate over. - public JsonObjectEnumerator(JsonObject jsonObject) => _enumerator = jsonObject._dictionary.GetEnumerator(); + public JsonObjectEnumerator(JsonObject jsonObject) //=> _enumerator = jsonObject._dictionary.GetEnumerator(); + { + _enumerator = jsonObject._dictionary.GetEnumerator(); + } /// /// Gets the property in the JSON object at the current position of the enumerator. diff --git a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs index 71544bdfae14..38aabf6d8535 100644 --- a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs +++ b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs @@ -146,31 +146,33 @@ public static void TestAsJsonElement() Assert.False(jsonElement.IsImmutable); JsonElement.ObjectEnumerator enumerator = jsonElement.EnumerateObject(); - + + enumerator.MoveNext(); Assert.Equal("text", enumerator.Current.Name); Assert.Equal(JsonValueKind.String, enumerator.Current.Value.ValueKind); Assert.Equal("property value", enumerator.Current.Value.GetString()); + enumerator.MoveNext(); - Assert.Equal("boolean", enumerator.Current.Name); - Assert.Equal(JsonValueKind.String, enumerator.Current.Value.ValueKind); - Assert.True( enumerator.Current.Value.GetBoolean()); + Assert.Equal(JsonValueKind.True, enumerator.Current.Value.ValueKind); + Assert.True(enumerator.Current.Value.GetBoolean()); + enumerator.MoveNext(); - Assert.Equal("number", enumerator.Current.Name); Assert.Equal(15, enumerator.Current.Value.GetInt32()); Assert.Equal(JsonValueKind.Number, enumerator.Current.Value.ValueKind); + enumerator.MoveNext(); - Assert.Equal("array", enumerator.Current.Name); Assert.Equal(2, enumerator.Current.Value.GetArrayLength()); Assert.Equal(JsonValueKind.Array, enumerator.Current.Value.ValueKind); JsonElement.ArrayEnumerator innerEnumerator = enumerator.Current.Value.EnumerateArray(); + innerEnumerator.MoveNext(); Assert.Equal(JsonValueKind.String, innerEnumerator.Current.ValueKind); Assert.Equal("value1", innerEnumerator.Current.GetString()); + innerEnumerator.MoveNext(); - Assert.Equal(JsonValueKind.String, innerEnumerator.Current.ValueKind); Assert.Equal("value2", innerEnumerator.Current.GetString()); innerEnumerator.Dispose(); From 7795b876ee6f89585b4a63b7ac54fb4798018eb1 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 20 Aug 2019 13:21:43 -0700 Subject: [PATCH 11/29] enumerators fixed --- src/System.Text.Json/ref/System.Text.Json.cs | 10 +++ .../src/System.Text.Json.csproj | 1 + .../Document/JsonElement.ArrayEnumerator.cs | 79 ++++++++++++++----- .../Document/JsonElement.ObjectEnumerator.cs | 1 + .../src/System/Text/Json/Node/JsonArray.cs | 2 +- .../Text/Json/Node/JsonArrayEnumerator.cs | 45 +++++++++++ .../Text/Json/Node/JsonObjectEnumerator.cs | 5 +- 7 files changed, 119 insertions(+), 24 deletions(-) create mode 100644 src/System.Text.Json/src/System/Text/Json/Node/JsonArrayEnumerator.cs diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index 71267069c69b..eb37a0a1060a 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -103,6 +103,16 @@ public void RemoveAt(int index) { } void System.Collections.Generic.ICollection.CopyTo(System.Text.Json.JsonNode[] array, int arrayIndex) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } + public partial struct JsonArrayEnumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable + { + private object _dummy; + public JsonArrayEnumerator(System.Text.Json.JsonArray jsonArray) { throw null; } + public System.Text.Json.JsonNode Current { get { throw null; } } + object System.Collections.IEnumerator.Current { get { throw null; } } + public void Dispose() { } + public bool MoveNext() { throw null; } + public void Reset() { } + } public sealed partial class JsonBoolean : System.Text.Json.JsonNode, System.IEquatable { public JsonBoolean() { } diff --git a/src/System.Text.Json/src/System.Text.Json.csproj b/src/System.Text.Json/src/System.Text.Json.csproj index c39adf93873e..620ec54d7f3d 100644 --- a/src/System.Text.Json/src/System.Text.Json.csproj +++ b/src/System.Text.Json/src/System.Text.Json.csproj @@ -24,6 +24,7 @@ + diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs index 37fdfede6d56..e9a9aab71008 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs @@ -19,20 +19,48 @@ public struct ArrayEnumerator : IEnumerable, IEnumerator - public JsonElement Current => - _curIdx < 0 ? default : new JsonElement((JsonDocument)_target._parent, _curIdx); + public JsonElement Current + { + get + { + if (_target._parent is JsonDocument document) + { + if (_curIdx < 0) + { + return default; + } + return new JsonElement(document, _curIdx); + } + + Debug.Assert(_jsonArrayEnumerator.HasValue); + return _jsonArrayEnumerator.Value.Current.AsJsonElement(); + } + } /// /// Returns an enumerator that iterates through a collection. @@ -58,12 +86,20 @@ public ArrayEnumerator GetEnumerator() public void Dispose() { _curIdx = _endIdx; + if (_jsonArrayEnumerator.HasValue) + { + _jsonArrayEnumerator.Value.Dispose(); + } } /// public void Reset() { _curIdx = -1; + if (_jsonArrayEnumerator.HasValue) + { + _jsonArrayEnumerator.Value.Reset(); + } } /// @@ -72,22 +108,27 @@ public void Reset() /// public bool MoveNext() { - if (_curIdx >= _endIdx) + if (_target._parent is JsonDocument document) { - return false; - } + if (_curIdx >= _endIdx) + { + return false; + } - if (_curIdx < 0) - { - _curIdx = _target._idx + JsonDocument.DbRow.Size; - } - else - { - JsonDocument document = (JsonDocument)_target._parent; - _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true); + if (_curIdx < 0) + { + _curIdx = _target._idx + JsonDocument.DbRow.Size; + } + else + { + _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true); + } + + return _curIdx < _endIdx; } - return _curIdx < _endIdx; + Debug.Assert(_jsonArrayEnumerator.HasValue); + return _jsonArrayEnumerator.Value.MoveNext(); } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs index 2756899cd87a..8f8a87d15bf3 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs @@ -57,6 +57,7 @@ public JsonProperty Current return new JsonProperty(new JsonElement(document, _curIdx)); } + Debug.Assert(_jsonObjectEnumerator.HasValue); KeyValuePair propertyPair = _jsonObjectEnumerator.Value.Current; return new JsonProperty(propertyPair.Value.AsJsonElement(), propertyPair.Key); } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs index a35b56b7edea..cebb5fe61a9e 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs @@ -12,7 +12,7 @@ namespace System.Text.Json /// public partial class JsonArray : JsonNode, IList, IReadOnlyList { - private readonly List _list; + internal readonly List _list; /// /// Initializes a new instance of the class representing the empty array. diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArrayEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArrayEnumerator.cs new file mode 100644 index 000000000000..9ac1157f0d27 --- /dev/null +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArrayEnumerator.cs @@ -0,0 +1,45 @@ +using System.Collections; +using System.Collections.Generic; + +namespace System.Text.Json +{ + /// + /// Supports an iteration over a JSON array. + /// + public struct JsonArrayEnumerator : IEnumerator + { + private readonly IEnumerator _enumerator; + + /// + /// Initializes a new instance of the class supporting an interation over provided JSON array. + /// + /// JSON array to iterate over. + public JsonArrayEnumerator(JsonArray jsonArray) => _enumerator = jsonArray._list.GetEnumerator(); + + /// + /// Gets the property in the JSON array at the current position of the enumerator. + /// + public JsonNode Current => _enumerator.Current; + + /// + /// Gets the property in the JSON array at the current position of the enumerator. + /// + object IEnumerator.Current => _enumerator.Current; + + /// + /// Releases all resources used by the . + /// + public void Dispose() => _enumerator.Dispose(); + + /// + /// Advances the enumerator to the next property of the JSON array. + /// + /// + public bool MoveNext() => _enumerator.MoveNext(); + + /// + /// Sets the enumerator to its initial position, which is before the first element in the JSON array. + /// + public void Reset() => _enumerator.Reset(); + } +} diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs index 7db2e9abc1e4..2723e21ccc3d 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs @@ -14,10 +14,7 @@ public struct JsonObjectEnumerator : IEnumerator> /// Initializes a new instance of the class supporting an interation over provided JSON object. /// /// JSON object to iterate over. - public JsonObjectEnumerator(JsonObject jsonObject) //=> _enumerator = jsonObject._dictionary.GetEnumerator(); - { - _enumerator = jsonObject._dictionary.GetEnumerator(); - } + public JsonObjectEnumerator(JsonObject jsonObject) => _enumerator = jsonObject._dictionary.GetEnumerator(); /// /// Gets the property in the JSON object at the current position of the enumerator. From 835392e10e65bf7dc50d54b823cd28f4d71e20c0 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 20 Aug 2019 14:29:41 -0700 Subject: [PATCH 12/29] nulls fixes --- .../Document/JsonElement.ArrayEnumerator.cs | 7 +++++-- .../Document/JsonElement.ObjectEnumerator.cs | 18 +++++++++++++++--- .../System/Text/Json/Document/JsonElement.cs | 9 +++++++-- .../tests/JsonNodeTransformationsTests.cs | 7 ++++++- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs index e9a9aab71008..a1aa9902559f 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs @@ -48,8 +48,9 @@ public JsonElement Current { get { - if (_target._parent is JsonDocument document) + if (_target._parent == null || _target._parent is JsonDocument) { + var document = _target._parent as JsonDocument; if (_curIdx < 0) { return default; @@ -108,8 +109,10 @@ public void Reset() /// public bool MoveNext() { - if (_target._parent is JsonDocument document) + if (_target._parent == null || _target._parent is JsonDocument) { + var document = _target._parent as JsonDocument; + if (_curIdx >= _endIdx) { return false; diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs index 8f8a87d15bf3..84d845cd732c 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs @@ -48,8 +48,10 @@ public JsonProperty Current { get { - if (_target._parent is JsonDocument document) + if (_target._parent == null || _target._parent is JsonDocument) { + var document = _target._parent as JsonDocument; + if (_curIdx < 0) { return default; @@ -59,7 +61,15 @@ public JsonProperty Current Debug.Assert(_jsonObjectEnumerator.HasValue); KeyValuePair propertyPair = _jsonObjectEnumerator.Value.Current; - return new JsonProperty(propertyPair.Value.AsJsonElement(), propertyPair.Key); + + if (propertyPair.Value == null) + { + return new JsonProperty(new JsonElement(null), propertyPair.Key); + } + else + { + return new JsonProperty(propertyPair.Value.AsJsonElement(), propertyPair.Key); + } } } @@ -115,8 +125,10 @@ public void Reset() /// public bool MoveNext() { - if (_target._parent is JsonDocument document) + if (_target._parent == null || _target._parent is JsonDocument) { + var document = _target._parent as JsonDocument; + if (_curIdx >= _endIdx) { return false; diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index 701814527ab3..08b984284d87 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -38,7 +38,6 @@ internal JsonElement(JsonNode parent) /// public bool IsImmutable => _idx != -1; - private JsonTokenType TokenType { get @@ -57,12 +56,18 @@ public JsonValueKind ValueKind { get { - if (_parent is JsonDocument) + if (IsImmutable) { return TokenType.ToValueKind(); } var jsonNode = (JsonNode)_parent; + + if (jsonNode == null) + { + return JsonValueKind.Null; + + } return jsonNode.ValueKind; } } diff --git a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs index 38aabf6d8535..bd6af6708c2c 100644 --- a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs +++ b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs @@ -139,6 +139,7 @@ public static void TestAsJsonElement() { "text", "property value" }, { "boolean", true }, { "number", 15 }, + { "null node", (JsonNode) null }, { "array", new JsonString[] { "value1", "value2"} } }; @@ -161,7 +162,11 @@ public static void TestAsJsonElement() Assert.Equal("number", enumerator.Current.Name); Assert.Equal(15, enumerator.Current.Value.GetInt32()); Assert.Equal(JsonValueKind.Number, enumerator.Current.Value.ValueKind); - + + enumerator.MoveNext(); + Assert.Equal("null node", enumerator.Current.Name); + Assert.Equal(JsonValueKind.Null, enumerator.Current.Value.ValueKind); + enumerator.MoveNext(); Assert.Equal("array", enumerator.Current.Name); Assert.Equal(2, enumerator.Current.Value.GetArrayLength()); From a0f774c9f7078d59e246602b2cd4d0ce41e91ea1 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 20 Aug 2019 14:49:33 -0700 Subject: [PATCH 13/29] csproj fixes --- src/System.Text.Json/src/System.Text.Json.csproj | 4 ++-- src/System.Text.Json/tests/System.Text.Json.Tests.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Text.Json/src/System.Text.Json.csproj b/src/System.Text.Json/src/System.Text.Json.csproj index 620ec54d7f3d..3e2278e2decc 100644 --- a/src/System.Text.Json/src/System.Text.Json.csproj +++ b/src/System.Text.Json/src/System.Text.Json.csproj @@ -23,8 +23,6 @@ - - @@ -213,6 +211,8 @@ + + diff --git a/src/System.Text.Json/tests/System.Text.Json.Tests.csproj b/src/System.Text.Json/tests/System.Text.Json.Tests.csproj index f20b381d2329..fc1cf929172b 100644 --- a/src/System.Text.Json/tests/System.Text.Json.Tests.csproj +++ b/src/System.Text.Json/tests/System.Text.Json.Tests.csproj @@ -22,7 +22,6 @@ - @@ -117,9 +116,10 @@ + + - From c2b0b5a153dfdc50203d5d047a444c6e0bbdb696 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 20 Aug 2019 15:10:12 -0700 Subject: [PATCH 14/29] open questions added to specification --- src/System.Text.Json/docs/writable_json_dom_spec.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/System.Text.Json/docs/writable_json_dom_spec.md b/src/System.Text.Json/docs/writable_json_dom_spec.md index 8a3288a3456c..8017ee9805f6 100644 --- a/src/System.Text.Json/docs/writable_json_dom_spec.md +++ b/src/System.Text.Json/docs/writable_json_dom_spec.md @@ -262,7 +262,14 @@ Mailbox.SendAllEmployeesData(employees.AsJsonElement()); * Should `ToString` on `JsonBoolean` and `JsonString` return the .NET or JSON representation? * Do we want to keep implicit cast operators (even though for `JsonNumber` it would mean throwing in some cases, which is against FDG)? * Do we want overloads that take a `StringComparison` enum for methods retrieving properties? It would make API easier to use where case is not important. -* Where do we want to enable user to set handling properties manner? +* Where do we want to enable user to set handling properties manner? Do we want to support it as `JsonElement` does not? (and currently `JsonNode.Parse` implementation uses `JsonDocument.Parse`) +* Do we want to support transforming `JsonObject` into JSON string? Should `ToString` behave this way or do we want an additional method - e.g. `GetJsonString` (it might be confusing as we have a type `JsonString`) or `GetJsonRepresenation`? +* `AsJsonElement` function on `JsonNode` currently does not work for `null` node. Do we want to support null case somehow? +* Do we want separate `JsonElement` implementations for `JsonNode` and `JsonDocument` or can the implementation be mixed as it is now (with a lot of ifs in each method)? +* Do we want separate `JsonElement.ArrayEnumerator` and `JsonElement.ObjectEnumerator` implementations for `JsonNode` and `JsonDocument`? Do we want separate methods in `JsonElement` returning strongly typed different implementations? +* Is it OK that `JsonElement.GetRawText` does not return the raw text for `JsonNode` parent? +* Should we support `JsonElement.WriteTo` for `JsonNode` parent? +* Private property `JsonElement.TokenType` is currently throwing an `InvalidCast` exception for `JsonNode` parent in debugger. Is it OK? ## Useful links From 6f15b4c3320b502c185f69dc22d61b8cb4c15b60 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 20 Aug 2019 15:22:42 -0700 Subject: [PATCH 15/29] open questions added to specification --- src/System.Text.Json/docs/writable_json_dom_spec.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/System.Text.Json/docs/writable_json_dom_spec.md b/src/System.Text.Json/docs/writable_json_dom_spec.md index 8017ee9805f6..4d7a76a39a79 100644 --- a/src/System.Text.Json/docs/writable_json_dom_spec.md +++ b/src/System.Text.Json/docs/writable_json_dom_spec.md @@ -270,6 +270,10 @@ Mailbox.SendAllEmployeesData(employees.AsJsonElement()); * Is it OK that `JsonElement.GetRawText` does not return the raw text for `JsonNode` parent? * Should we support `JsonElement.WriteTo` for `JsonNode` parent? * Private property `JsonElement.TokenType` is currently throwing an `InvalidCast` exception for `JsonNode` parent in debugger. Is it OK? +* Do we want both `Clone` and `DeepCopy` methods for `JsonNode`? +* Are we OK with needing to cast `JsonArray` to `JsonNode` in order to add it to `JsonObject`? (there's an ambiguous call between `JsonArray` and `IEnumerable` right now) +* Do we want `IsImmutable` property for `JsonElement`? +* Is `DateTime` for `JsonString` handled correctly? ## Useful links From c63d553c2048f4c2ed14adfa7f25f2dbd56699da Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Wed, 21 Aug 2019 12:00:09 -0700 Subject: [PATCH 16/29] minor fixes, code coverage improved --- src/System.Text.Json/ref/System.Text.Json.cs | 4 +- .../src/System/Text/Json/Node/JsonArray.cs | 13 +- .../src/System/Text/Json/Node/JsonObject.cs | 6 +- src/System.Text.Json/tests/JsonArrayTests.cs | 286 +++++++++++++----- .../tests/JsonNodeTransformationsTests.cs | 40 ++- src/System.Text.Json/tests/JsonObjectTests.cs | 29 ++ 6 files changed, 295 insertions(+), 83 deletions(-) diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index 300668bde38a..c250beabf0aa 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -420,12 +420,12 @@ public void AddRange(System.Collections.Generic.IEnumerable> GetEnumerator() { throw null; } - public System.Text.Json.JsonArray GetJsonArrayProperty(string propertyName) { throw null; } + public System.Text.Json.JsonArray GetJsonArrayPropertyValue(string propertyName) { throw null; } public System.Text.Json.JsonObject GetJsonObjectPropertyValue(string propertyName) { throw null; } public System.Text.Json.JsonNode GetPropertyValue(string propertyName) { throw null; } public bool Remove(string propertyName) { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - public bool TryGetArrayProperty(string propertyName, out System.Text.Json.JsonArray jsonArray) { throw null; } + public bool TryGetJsonArrayPropertyValue(string propertyName, out System.Text.Json.JsonArray jsonArray) { throw null; } public bool TryGetJsonObjectPropertyValue(string propertyName, out System.Text.Json.JsonObject jsonObject) { throw null; } public bool TryGetPropertyValue(string propertyName, out System.Text.Json.JsonNode jsonNode) { throw null; } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs index cebb5fe61a9e..d7052580f0fa 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs @@ -203,7 +203,14 @@ public JsonArray(IEnumerable values) : this() /// public JsonNode this[int idx] { - get => 0 <= idx && idx < _list.Count ? _list[idx] : throw new ArgumentOutOfRangeException(); + get + { + if (idx < 0 || idx >= _list.Count) + { + throw new ArgumentOutOfRangeException(); + } + return _list[idx]; + } set { if (idx < 0 || idx >= _list.Count) @@ -646,13 +653,13 @@ public JsonNode this[int idx] /// Returns an enumerator that iterates through the JSON array values. /// /// An enumerator structure for the . - IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// /// Returns an enumerator that iterates through the JSON array values. /// /// An enumerator structure for the . - public IEnumerator GetEnumerator() => _list.GetEnumerator(); + public IEnumerator GetEnumerator() => new JsonArrayEnumerator(this); /// /// Creates a new JSON array that is a copy of the current instance. diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs index 1daf37ed9f27..22d580203a8c 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs @@ -507,7 +507,7 @@ public bool TryGetJsonObjectPropertyValue(string propertyName, out JsonObject js /// /// Property with specified name is not a JSON array. /// - public JsonArray GetJsonArrayProperty(string propertyName) + public JsonArray GetJsonArrayPropertyValue(string propertyName) { if (GetPropertyValue(propertyName) is JsonArray jsonArray) { @@ -526,7 +526,7 @@ public JsonArray GetJsonArrayProperty(string propertyName) /// if JSON array property with specified name was found; /// otherwise, /// - public bool TryGetArrayProperty(string propertyName, out JsonArray jsonArray) + public bool TryGetJsonArrayPropertyValue(string propertyName, out JsonArray jsonArray) { if (TryGetPropertyValue(propertyName, out JsonNode jsonNode)) { @@ -555,7 +555,7 @@ public bool TryGetArrayProperty(string propertyName, out JsonArray jsonArray) /// Returns an enumerator that iterates through the JSON object properties. /// /// An enumerator structure for the . - IEnumerator IEnumerable.GetEnumerator() => new JsonObjectEnumerator(this); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// /// Creates a new JSON object that is a copy of the current instance. diff --git a/src/System.Text.Json/tests/JsonArrayTests.cs b/src/System.Text.Json/tests/JsonArrayTests.cs index c580fe4a4438..4fcc2e2b981f 100644 --- a/src/System.Text.Json/tests/JsonArrayTests.cs +++ b/src/System.Text.Json/tests/JsonArrayTests.cs @@ -10,101 +10,175 @@ namespace System.Text.Json.Tests { public static class JsonArrayTests { - [Fact] - public static void TestAdd() - { - var jsonArray = new JsonArray(); - int idx = 0; - - jsonArray.Add("value"); - Assert.Equal("value", (JsonString)jsonArray[idx++]); - Assert.True(jsonArray.Contains("value")); - - jsonArray.Add(true); - Assert.True(((JsonBoolean)jsonArray[idx++]).Value); - Assert.True(jsonArray.Contains(true)); - - jsonArray.Add(byte.MaxValue); - Assert.Equal(byte.MaxValue, ((JsonNumber)jsonArray[idx++]).GetByte()); - Assert.True(jsonArray.Contains(byte.MaxValue)); - jsonArray.Add(short.MaxValue); - Assert.Equal(short.MaxValue, ((JsonNumber)jsonArray[idx++]).GetInt16()); - Assert.True(jsonArray.Contains(short.MaxValue)); + private static void TestArray(T value1, T value2, Func getter, Func nodeCtor) + { + var value1Casted = value1 as dynamic; + var value2Casted = value2 as dynamic; - jsonArray.Add(int.MaxValue); - Assert.Equal(int.MaxValue, ((JsonNumber)jsonArray[idx++]).GetInt32()); - Assert.True(jsonArray.Contains(int.MaxValue)); + var list = new List() { value1 }; + JsonArray jsonArray = new JsonArray(list as dynamic); + Assert.Equal(1, jsonArray.Count); - jsonArray.Add(long.MaxValue); - Assert.Equal(long.MaxValue, ((JsonNumber)jsonArray[idx++]).GetInt64()); - Assert.True(jsonArray.Contains(long.MaxValue)); + Assert.True(jsonArray.Contains(value1Casted)); + Assert.Equal(value1, getter(jsonArray)); - jsonArray.Add(3.14f); - Assert.Equal(3.14f, ((JsonNumber)jsonArray[idx++]).GetSingle()); + jsonArray.Insert(0, value2 as dynamic); + Assert.Equal(2, jsonArray.Count); + Assert.True(jsonArray.Contains(value2Casted)); + Assert.Equal(value2, getter(jsonArray)); - jsonArray.Add(3.14); - Assert.Equal(3.14, ((JsonNumber)jsonArray[idx++]).GetDouble()); + JsonNode value1Node = nodeCtor(value1); + Assert.Equal(1, jsonArray.IndexOf(value1Node)); + Assert.Equal(1, jsonArray.LastIndexOf(value1Node)); - jsonArray.Add(sbyte.MaxValue); - Assert.Equal(sbyte.MaxValue, ((JsonNumber)jsonArray[idx++]).GetSByte()); + jsonArray.RemoveAt(0); + Assert.False(jsonArray.Contains(value2Casted)); - jsonArray.Add(ushort.MaxValue); - Assert.Equal(ushort.MaxValue, ((JsonNumber)jsonArray[idx++]).GetUInt16()); + jsonArray.Remove(value1Node); + Assert.False(jsonArray.Contains(value1Casted)); - jsonArray.Add(uint.MaxValue); - Assert.Equal(uint.MaxValue, ((JsonNumber)jsonArray[idx++]).GetUInt32()); + Assert.Equal(0, jsonArray.Count); - jsonArray.Add(ulong.MaxValue); - Assert.Equal(ulong.MaxValue, ((JsonNumber)jsonArray[idx++]).GetUInt64()); + jsonArray.Add(value2Casted); + Assert.Equal(1, jsonArray.Count); + Assert.True(jsonArray.Contains(value2Casted)); + Assert.Equal(value2, getter(jsonArray)); - jsonArray.Add(decimal.One); - Assert.Equal(decimal.One, ((JsonNumber)jsonArray[idx++]).GetDecimal()); + jsonArray[0] = value1Node; + Assert.Equal(1, jsonArray.Count); + Assert.True(jsonArray.Contains(value1Casted)); + Assert.Equal(value1, getter(jsonArray)); } [Fact] - public static void TestInsert() + public static void TestStringArray() { - var jsonArray = new JsonArray(); - - jsonArray.Insert(0, "value"); - Assert.Equal("value", (JsonString)jsonArray[0]); + TestArray( + "value1", "value2", + jsonArray => ((JsonString)jsonArray[0]).Value, + v => new JsonString(v) + ); + } - jsonArray.Insert(0, true); - Assert.True(((JsonBoolean)jsonArray[0]).Value); + [Fact] + public static void TestBooleanArray() + { + TestArray( + true, false, + jsonArray => ((JsonBoolean)jsonArray[0]).Value, + v => new JsonBoolean(v) + ); + } - jsonArray.Insert(0, byte.MaxValue); - Assert.Equal(byte.MaxValue, ((JsonNumber)jsonArray[0]).GetByte()); + [Fact] + public static void TestByteArray() + { + TestArray( + byte.MaxValue, byte.MaxValue - 1, + jsonArray => ((JsonNumber)jsonArray[0]).GetByte(), + v => new JsonNumber(v) + ); + } - jsonArray.Insert(0, short.MaxValue); - Assert.Equal(short.MaxValue, ((JsonNumber)jsonArray[0]).GetInt16()); + [Fact] + public static void TestInt16Array() + { + TestArray( + short.MaxValue, short.MaxValue - 1, + jsonArray => ((JsonNumber)jsonArray[0]).GetInt16(), + v => new JsonNumber(v) + ); + } - jsonArray.Insert(0, int.MaxValue); - Assert.Equal(int.MaxValue, ((JsonNumber)jsonArray[0]).GetInt32()); + [Fact] + public static void TestInt32rray() + { + TestArray( + int.MaxValue, int.MaxValue - 1, + jsonArray => ((JsonNumber)jsonArray[0]).GetInt32(), + v => new JsonNumber(v) + ); + } - jsonArray.Insert(0, long.MaxValue); - Assert.Equal(long.MaxValue, ((JsonNumber)jsonArray[0]).GetInt64()); + [Fact] + public static void TestInt64rray() + { + TestArray( + long.MaxValue, long.MaxValue - 1, + jsonArray => ((JsonNumber)jsonArray[0]).GetInt64(), + v => new JsonNumber(v) + ); + } - jsonArray.Insert(0, 3.14f); - Assert.Equal(3.14f, ((JsonNumber)jsonArray[0]).GetSingle()); + [Fact] + public static void TestSingleArray() + { + TestArray( + 3.14f, 1.41f, + jsonArray => ((JsonNumber)jsonArray[0]).GetSingle(), + v => new JsonNumber(v) + ); + } - jsonArray.Insert(0, 3.14); - Assert.Equal(3.14, ((JsonNumber)jsonArray[0]).GetDouble()); + [Fact] + public static void TestDoubleArray() + { + TestArray( + 3.14, 1.41, + jsonArray => ((JsonNumber)jsonArray[0]).GetDouble(), + v => new JsonNumber(v) + ); + } - jsonArray.Insert(0, sbyte.MaxValue); - Assert.Equal(sbyte.MaxValue, ((JsonNumber)jsonArray[0]).GetSByte()); + [Fact] + public static void TestSByteArray() + { + TestArray( + sbyte.MaxValue, sbyte.MaxValue - 1, + jsonArray => ((JsonNumber)jsonArray[0]).GetSByte(), + v => new JsonNumber(v) + ); + } - jsonArray.Insert(0, ushort.MaxValue); - Assert.Equal(ushort.MaxValue, ((JsonNumber)jsonArray[0]).GetUInt16()); + [Fact] + public static void TestUInt16Array() + { + TestArray( + ushort.MaxValue, ushort.MaxValue - 1, + jsonArray => ((JsonNumber)jsonArray[0]).GetUInt16(), + v => new JsonNumber(v) + ); + } - jsonArray.Insert(0, uint.MaxValue); - Assert.Equal(uint.MaxValue, ((JsonNumber)jsonArray[0]).GetUInt32()); + [Fact] + public static void TestUInt32Array() + { + TestArray( + uint.MaxValue, uint.MaxValue - 1, + jsonArray => ((JsonNumber)jsonArray[0]).GetUInt32(), + v => new JsonNumber(v) + ); + } - jsonArray.Insert(0, ulong.MaxValue); - Assert.Equal(ulong.MaxValue, ((JsonNumber)jsonArray[0]).GetUInt64()); + [Fact] + public static void TestUInt64Array() + { + TestArray( + ulong.MaxValue, ulong.MaxValue - 1, + jsonArray => ((JsonNumber)jsonArray[0]).GetUInt64(), + v => new JsonNumber(v) + ); + } - jsonArray.Insert(0, decimal.One); - Assert.Equal(decimal.One, ((JsonNumber)jsonArray[0]).GetDecimal()); + [Fact] + public static void TestDecimalArray() + { + TestArray( + decimal.One, decimal.Zero, + jsonArray => ((JsonNumber)jsonArray[0]).GetDecimal(), + v => new JsonNumber(v) + ); } [Fact] @@ -332,13 +406,81 @@ public static void TestAccesingNestedJsonArrayGetPropertyMethod() { "tests", new JsonString [] { "code coverage" } }, }; - issues.GetJsonArrayProperty("bugs").Add("bug 12356"); - ((JsonString)issues.GetJsonArrayProperty("features")[0]).Value = "feature 1569"; - ((JsonString)issues.GetJsonArrayProperty("features")[1]).Value = "feature 56134"; + issues.GetJsonArrayPropertyValue("bugs").Add("bug 12356"); + ((JsonString)issues.GetJsonArrayPropertyValue("features")[0]).Value = "feature 1569"; + ((JsonString)issues.GetJsonArrayPropertyValue("features")[1]).Value = "feature 56134"; Assert.True(((JsonArray)issues["bugs"]).Contains("bug 12356")); Assert.Equal("feature 1569", (JsonString)((JsonArray)issues["features"])[0]); Assert.Equal("feature 56134", (JsonString)((JsonArray)issues["features"])[1]); } + + [Fact] + public static void TestAccesingNestedJsonArrayTryGetPropertyMethod() + { + var issues = new JsonObject() + { + { "features", new JsonString [] { "new functionality 1", "new functionality 2" } }, + }; + + Assert.True(issues.TryGetJsonArrayPropertyValue("features", out JsonArray featuresArray)); + Assert.Equal("new functionality 1", (JsonString)featuresArray[0]); + Assert.Equal("new functionality 2", (JsonString)featuresArray[1]); + } + + [Fact] + public static void TestOutOfRangeException() + { + Assert.Throws(() => new JsonArray()[-1]); + Assert.Throws(() => new JsonArray()[-1] = new JsonString()); + Assert.Throws(() => new JsonArray()[0]); + Assert.Throws(() => new JsonArray()[0] = new JsonString()); + Assert.Throws(() => new JsonArray()[1]); + Assert.Throws(() => new JsonArray()[1] = new JsonString()); + } + + [Fact] + public static void TestIsReadOnly() + { + Assert.False(new JsonArray().IsReadOnly); + } + + [Fact] + public static void TestClean() + { + var jsonArray = new JsonArray { 1, 2, 3 }; + + Assert.Equal(3, jsonArray.Count); + Assert.True(jsonArray.Contains(1)); + Assert.True(jsonArray.Contains(2)); + Assert.True(jsonArray.Contains(3)); + + jsonArray.Clear(); + + Assert.Equal(0, jsonArray.Count); + Assert.False(jsonArray.Contains(1)); + Assert.False(jsonArray.Contains(2)); + Assert.False(jsonArray.Contains(3)); + } + + [Fact] + public static void TestRemoveAll() + { + var jsonArray = new JsonArray { 1, 2, 3 }; + + Assert.Equal(3, jsonArray.Count); + Assert.True(jsonArray.Contains(1)); + Assert.True(jsonArray.Contains(2)); + Assert.True(jsonArray.Contains(3)); + + jsonArray.RemoveAll(v => ((JsonNumber)v).GetInt32() <= 2); + + Assert.Equal(1, jsonArray.Count); + Assert.False(jsonArray.Contains(1)); + Assert.False(jsonArray.Contains(2)); + Assert.True(jsonArray.Contains(3)); + } + + } } diff --git a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs index bd6af6708c2c..c51f5a500cf6 100644 --- a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs +++ b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs @@ -123,9 +123,9 @@ public static void TestCloneJsonNode() ((JsonBoolean)jsonObject["boolean"]).Value = false; Assert.True(((JsonBoolean)jsonObjectCopy["boolean"]).Value); - Assert.Equal(2, jsonObjectCopy.GetJsonArrayProperty("array").Count); - jsonObject.GetJsonArrayProperty("array").Add("value3"); - Assert.Equal(2, jsonObjectCopy.GetJsonArrayProperty("array").Count); + Assert.Equal(2, jsonObjectCopy.GetJsonArrayPropertyValue("array").Count); + jsonObject.GetJsonArrayPropertyValue("array").Add("value3"); + Assert.Equal(2, jsonObjectCopy.GetJsonArrayPropertyValue("array").Count); jsonObject.Add("new one", 123); Assert.Equal(4, jsonObjectCopy.PropertyNames.Count); @@ -180,8 +180,42 @@ public static void TestAsJsonElement() innerEnumerator.MoveNext(); Assert.Equal(JsonValueKind.String, innerEnumerator.Current.ValueKind); Assert.Equal("value2", innerEnumerator.Current.GetString()); + innerEnumerator.Dispose(); enumerator.Dispose(); + + // Modifying JsonObject will change JsonElement: + + jsonObject["text"] = new JsonNumber("123"); + Assert.Equal(123, jsonElement.GetProperty("text").GetInt32()); + } + + [Fact] + public static void TestGetNode() + { + var jsonObject = new JsonObject + { + { "text", "property value" }, + { "boolean", true }, + { "number", 15 }, + { "null node", (JsonNode) null }, + { "array", new JsonString[] { "value1", "value2"} } + }; + + JsonElement jsonElement = jsonObject.AsJsonElement(); + JsonObject jsonObjectFromElement = (JsonObject)JsonNode.GetNode(jsonElement); + + Assert.Equal("property value", (JsonString)jsonObjectFromElement["text"]); + + // Modifying JsonObject will change JsonObjectFromElement: + + jsonObject["text"] = new JsonString("something different"); + Assert.Equal("something different", (JsonString)jsonObjectFromElement["text"]); + + // Modifying JsonObjectFromElement will change JsonObject: + + ((JsonBoolean)jsonObjectFromElement["boolean"]).Value = false; + Assert.False(((JsonBoolean)jsonObject["boolean"]).Value); } } } diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index f158afc95abb..736267aa81fc 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -543,6 +543,35 @@ public static void TestTryGetObjectPropertyFails() Assert.Null(property); } + [Fact] + public static void TestGetJsonArrayPropertyThrows() + { + Assert.Throws(() => + { + var jsonObject = new JsonObject() + { + { "name", "value" } + }; + + jsonObject.GetJsonArrayPropertyValue("name"); + }); + } + + [Fact] + public static void TestTryGetArrayPropertyFails() + { + var jsonObject = new JsonObject() + { + { "name", "value" } + }; + + Assert.False(jsonObject.TryGetJsonArrayPropertyValue("name", out JsonArray property)); + Assert.Null(property); + + Assert.False(jsonObject.TryGetJsonArrayPropertyValue("other", out property)); + Assert.Null(property); + } + [Fact] public static void TestArgumentNullValidation() { From e1ac1305f672a0fc9efcadb656041702f547bb7a Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Thu, 22 Aug 2019 11:35:14 -0700 Subject: [PATCH 17/29] work on review comments --- .../Document/JsonElement.ArrayEnumerator.cs | 61 ++++---- .../Document/JsonElement.ObjectEnumerator.cs | 71 +++++----- .../System/Text/Json/Document/JsonElement.cs | 130 +++++++++++------- .../src/System/Text/Json/Node/JsonArray.cs | 7 +- .../src/System/Text/Json/Node/JsonNode.cs | 36 +---- .../src/System/Text/Json/ThrowHelper.cs | 22 ++- 6 files changed, 174 insertions(+), 153 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs index a1aa9902559f..1bc77bd2814a 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs @@ -23,19 +23,18 @@ public struct ArrayEnumerator : IEnumerable, IEnumerator public bool MoveNext() { - if (_target._parent == null || _target._parent is JsonDocument) + if (_jsonArrayEnumerator.HasValue) { - var document = _target._parent as JsonDocument; + return _jsonArrayEnumerator.Value.MoveNext(); + } - if (_curIdx >= _endIdx) - { - return false; - } + var document = _target._parent as JsonDocument; - if (_curIdx < 0) - { - _curIdx = _target._idx + JsonDocument.DbRow.Size; - } - else - { - _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true); - } + if (_curIdx >= _endIdx) + { + return false; + } - return _curIdx < _endIdx; + if (_curIdx < 0) + { + _curIdx = _target._idx + JsonDocument.DbRow.Size; + } + else + { + _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true); } - Debug.Assert(_jsonArrayEnumerator.HasValue); - return _jsonArrayEnumerator.Value.MoveNext(); + return _curIdx < _endIdx; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs index 84d845cd732c..bbf624bbb471 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs @@ -23,19 +23,18 @@ public struct ObjectEnumerator : IEnumerable, IEnumerator propertyPair = _jsonObjectEnumerator.Value.Current; - if (_curIdx < 0) + if (propertyPair.Value == null) + { + return new JsonProperty(new JsonElement(null), propertyPair.Key); + } + else { - return default; + return new JsonProperty(propertyPair.Value.AsJsonElement(), propertyPair.Key); } - return new JsonProperty(new JsonElement(document, _curIdx)); } - Debug.Assert(_jsonObjectEnumerator.HasValue); - KeyValuePair propertyPair = _jsonObjectEnumerator.Value.Current; + var document = (JsonDocument)_target._parent; - if (propertyPair.Value == null) - { - return new JsonProperty(new JsonElement(null), propertyPair.Key); - } - else + if (_curIdx < 0) { - return new JsonProperty(propertyPair.Value.AsJsonElement(), propertyPair.Key); + return default; } + return new JsonProperty(new JsonElement(document, _curIdx)); } } @@ -125,32 +123,31 @@ public void Reset() /// public bool MoveNext() { - if (_target._parent == null || _target._parent is JsonDocument) + if (_jsonObjectEnumerator.HasValue) { - var document = _target._parent as JsonDocument; - - if (_curIdx >= _endIdx) - { - return false; - } + return _jsonObjectEnumerator.Value.MoveNext(); + } - if (_curIdx < 0) - { - _curIdx = _target._idx + JsonDocument.DbRow.Size; - } - else - { - _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true); - } + var document = _target._parent as JsonDocument; - // _curIdx is now pointing at a property name, move one more to get the value - _curIdx += JsonDocument.DbRow.Size; + if (_curIdx >= _endIdx) + { + return false; + } - return _curIdx < _endIdx; + if (_curIdx < 0) + { + _curIdx = _target._idx + JsonDocument.DbRow.Size; + } + else + { + _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true); } - Debug.Assert(_jsonObjectEnumerator.HasValue); - return _jsonObjectEnumerator.Value.MoveNext(); + // _curIdx is now pointing at a property name, move one more to get the value + _curIdx += JsonDocument.DbRow.Size; + + return _curIdx < _endIdx; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index 08b984284d87..55c4ba8920bc 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -34,10 +34,11 @@ internal JsonElement(JsonNode parent) } /// - /// Indicates if JSON element instance is immutable. + /// Indicates whether or not this instance is immutable. /// public bool IsImmutable => _idx != -1; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private JsonTokenType TokenType { get @@ -96,12 +97,14 @@ public JsonElement this[int index] return document.GetArrayIndexElement(_idx, index); } - if (_parent is JsonArray jsonArray) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonArray jsonArray) { return jsonArray[index].AsJsonElement(); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Array, jsonNode.ValueKind); } } @@ -124,12 +127,14 @@ public int GetArrayLength() return document.GetArrayLength(_idx); } - if (_parent is JsonArray jsonArray) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonArray jsonArray) { return jsonArray.Count; } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Array, jsonNode.ValueKind); } /// @@ -283,9 +288,7 @@ public JsonElement GetProperty(ReadOnlySpan utf8PropertyName) public bool TryGetProperty(string propertyName, out JsonElement value) { if (propertyName == null) - { throw new ArgumentNullException(nameof(propertyName)); - } return TryGetProperty(propertyName.AsSpan(), out value); } @@ -326,7 +329,9 @@ public bool TryGetProperty(ReadOnlySpan propertyName, out JsonElement valu return document.TryGetNamedPropertyValue(_idx, propertyName, out value); } - if (_parent is JsonObject jsonObject) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonObject jsonObject) { if (jsonObject.TryGetPropertyValue(propertyName.ToString(), out JsonNode nodeValue)) { @@ -338,7 +343,7 @@ public bool TryGetProperty(ReadOnlySpan propertyName, out JsonElement valu return false; } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Object, jsonNode.ValueKind); } /// @@ -379,9 +384,11 @@ public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out JsonElement return document.TryGetNamedPropertyValue(_idx, utf8PropertyName, out value); } - if (_parent is JsonObject jsonObject) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonObject jsonObject) { - if (jsonObject.TryGetPropertyValue(utf8PropertyName.ToString(), out JsonNode nodeValue)) + if (jsonObject.TryGetPropertyValue(Encoding.UTF8.GetString(utf8PropertyName), out JsonNode nodeValue)) { value = nodeValue.AsJsonElement(); return true; @@ -391,7 +398,7 @@ public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out JsonElement return false; } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Object, jsonNode.ValueKind); } /// @@ -423,12 +430,14 @@ public bool GetBoolean() throw ThrowHelper.GetJsonElementWrongTypeException(nameof(Boolean), type); } + var jsonNode = (JsonNode)_parent; + if (_parent is JsonBoolean jsonBoolean) { return jsonBoolean.Value; } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(nameof(Boolean), jsonNode.ValueKind); } /// @@ -454,12 +463,14 @@ public string GetString() return document.GetString(_idx, JsonTokenType.String); } - if (_parent is JsonString jsonString) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonString jsonString) { return jsonString.Value; } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.String, jsonNode.ValueKind); } /// @@ -541,12 +552,14 @@ public bool TryGetSByte(out sbyte value) return document.TryGetValue(_idx, out value); } - if (_parent is JsonNumber jsonNumber) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonNumber jsonNumber) { return jsonNumber.TryGetSByte(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind); } /// @@ -599,12 +612,14 @@ public bool TryGetByte(out byte value) return document.TryGetValue(_idx, out value); } - if (_parent is JsonNumber jsonNumber) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonNumber jsonNumber) { return jsonNumber.TryGetByte(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind); } /// @@ -659,12 +674,14 @@ public bool TryGetInt16(out short value) return document.TryGetValue(_idx, out value); } + var jsonNode = (JsonNode)_parent; + if (_parent is JsonNumber jsonNumber) { return jsonNumber.TryGetInt16(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind); } /// @@ -717,12 +734,14 @@ public bool TryGetUInt16(out ushort value) return document.TryGetValue(_idx, out value); } + var jsonNode = (JsonNode)_parent; + if (_parent is JsonNumber jsonNumber) { return jsonNumber.TryGetUInt16(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind); } /// @@ -778,12 +797,14 @@ public bool TryGetInt32(out int value) return document.TryGetValue(_idx, out value); } - if (_parent is JsonNumber jsonNumber) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonNumber jsonNumber) { return jsonNumber.TryGetInt32(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind); } /// @@ -836,12 +857,14 @@ public bool TryGetUInt32(out uint value) return document.TryGetValue(_idx, out value); } - if (_parent is JsonNumber jsonNumber) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonNumber jsonNumber) { return jsonNumber.TryGetUInt32(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind); } /// @@ -897,12 +920,14 @@ public bool TryGetInt64(out long value) return document.TryGetValue(_idx, out value); } - if (_parent is JsonNumber jsonNumber) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonNumber jsonNumber) { return jsonNumber.TryGetInt64(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind); } /// @@ -958,12 +983,14 @@ public bool TryGetUInt64(out ulong value) return document.TryGetValue(_idx, out value); } - if (_parent is JsonNumber jsonNumber) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonNumber jsonNumber) { return jsonNumber.TryGetUInt64(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind); } /// @@ -1028,12 +1055,14 @@ public bool TryGetDouble(out double value) return document.TryGetValue(_idx, out value); } + var jsonNode = (JsonNode)_parent; + if (_parent is JsonNumber jsonNumber) { return jsonNumber.TryGetDouble(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind); } /// @@ -1105,12 +1134,14 @@ public bool TryGetSingle(out float value) return document.TryGetValue(_idx, out value); } - if (_parent is JsonNumber jsonNumber) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonNumber jsonNumber) { return jsonNumber.TryGetSingle(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind); } /// @@ -1174,12 +1205,14 @@ public bool TryGetDecimal(out decimal value) return document.TryGetValue(_idx, out value); } - if (_parent is JsonNumber jsonNumber) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonNumber jsonNumber) { return jsonNumber.TryGetDecimal(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Number, jsonNode.ValueKind); } /// @@ -1235,12 +1268,14 @@ public bool TryGetDateTime(out DateTime value) return document.TryGetValue(_idx, out value); } - if (_parent is JsonString jsonString) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonString jsonString) { return jsonString.TryGetDateTime(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.String, jsonNode.ValueKind); } /// @@ -1296,12 +1331,14 @@ public bool TryGetDateTimeOffset(out DateTimeOffset value) return document.TryGetValue(_idx, out value); } - if (_parent is JsonString jsonString) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonString jsonString) { return jsonString.TryGetDateTimeOffset(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.String, jsonNode.ValueKind); } /// @@ -1357,12 +1394,14 @@ public bool TryGetGuid(out Guid value) return document.TryGetValue(_idx, out value); } - if (_parent is JsonString jsonString) + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonString jsonString) { return jsonString.TryGetGuid(out value); } - throw new InvalidOperationException(); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.String, jsonNode.ValueKind); } /// @@ -1711,14 +1750,14 @@ public JsonElement Clone() { CheckValidInstance(); - if (_parent is JsonDocument docuemnt) + if (_parent is JsonDocument document) { - if (!docuemnt.IsDisposable) + if (!document.IsDisposable) { return this; } - return docuemnt.CloneElement(_idx); + return document.CloneElement(_idx); } var jsonNode = (JsonNode)_parent; @@ -1732,10 +1771,7 @@ private void CheckValidInstance() throw new InvalidOperationException(); } - if (!(_parent is JsonDocument) && !(_parent is JsonNode)) - { - throw new NotSupportedException("JsonElement can be backed only by JsonDocument or JsonNode"); - } + Debug.Assert(_parent is JsonDocument || _parent is JsonNode); } [DebuggerBrowsable(DebuggerBrowsableState.Never)] diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs index d7052580f0fa..0259751ecaf7 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs @@ -10,7 +10,7 @@ namespace System.Text.Json /// /// Represents a JSON array. /// - public partial class JsonArray : JsonNode, IList, IReadOnlyList + public sealed class JsonArray : JsonNode, IList, IReadOnlyList { internal readonly List _list; @@ -36,6 +36,11 @@ public JsonArray(IEnumerable values) : this() { foreach (string value in values) { + if (value == null) + { + _list.Add(null); + } + _list.Add(new JsonString(value)); } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs index a3ad197b90be..0747e19372a1 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs @@ -39,39 +39,7 @@ public static JsonNode Parse(string json) { using (JsonDocument document = JsonDocument.Parse(json)) { - return DeepCopy(document); - } - } - - public static JsonNode Parse(ReadOnlySequence utf8Json) - { - using (JsonDocument document = JsonDocument.Parse(utf8Json)) - { - return DeepCopy(document); - } - } - - public static JsonNode Parse(Stream utf8Json) - { - using (JsonDocument document = JsonDocument.Parse(utf8Json)) - { - return DeepCopy(document); - } - } - - public static JsonNode Parse(ReadOnlyMemory utf8Json) - { - using (JsonDocument document = JsonDocument.Parse(utf8Json)) - { - return DeepCopy(document); - } - } - - public static JsonNode Parse(ReadOnlyMemory json) - { - using (JsonDocument document = JsonDocument.Parse(json)) - { - return DeepCopy(document); + return DeepCopy(document.RootElement); } } @@ -112,8 +80,6 @@ public static JsonNode DeepCopy(JsonElement jsonElement) } } - - public static JsonNode DeepCopy(JsonDocument jsonDocument) => DeepCopy(jsonDocument.RootElement); } } diff --git a/src/System.Text.Json/src/System/Text/Json/ThrowHelper.cs b/src/System.Text.Json/src/System/Text/Json/ThrowHelper.cs index cbab677acbb5..00a9f47093a5 100644 --- a/src/System.Text.Json/src/System/Text/Json/ThrowHelper.cs +++ b/src/System.Text.Json/src/System/Text/Json/ThrowHelper.cs @@ -231,17 +231,33 @@ internal static InvalidOperationException GetJsonElementWrongTypeException( JsonTokenType expectedType, JsonTokenType actualType) { - return GetInvalidOperationException( - SR.Format(SR.JsonElementHasWrongType, expectedType.ToValueKind(), actualType.ToValueKind())); + return GetJsonElementWrongTypeException(expectedType.ToValueKind(), actualType.ToValueKind()); } [MethodImpl(MethodImplOptions.NoInlining)] internal static InvalidOperationException GetJsonElementWrongTypeException( string expectedTypeName, JsonTokenType actualType) + { + return GetJsonElementWrongTypeException(expectedTypeName, actualType.ToValueKind()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static InvalidOperationException GetJsonElementWrongTypeException( + JsonValueKind expectedType, + JsonValueKind actualType) + { + return GetInvalidOperationException( + SR.Format(SR.JsonElementHasWrongType, expectedType, actualType)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static InvalidOperationException GetJsonElementWrongTypeException( + string expectedTypeName, + JsonValueKind actualType) { return GetInvalidOperationException( - SR.Format(SR.JsonElementHasWrongType, expectedTypeName, actualType.ToValueKind())); + SR.Format(SR.JsonElementHasWrongType, expectedTypeName, actualType)); } public static void ThrowJsonReaderException(ref Utf8JsonReader json, ExceptionResource resource, byte nextByte = default, ReadOnlySpan bytes = default) From 3b11f1e893c2d3692324b55d3d13dbe140f85807 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Thu, 22 Aug 2019 14:23:19 -0700 Subject: [PATCH 18/29] work on review comments --- .../src/System/Text/Json/Node/JsonArray.cs | 311 +++++------------- .../src/System/Text/Json/Node/JsonObject.cs | 30 +- 2 files changed, 105 insertions(+), 236 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs index 0259751ecaf7..0bd87bd7184e 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs @@ -208,72 +208,66 @@ public JsonArray(IEnumerable values) : this() /// public JsonNode this[int idx] { - get - { - if (idx < 0 || idx >= _list.Count) - { - throw new ArgumentOutOfRangeException(); - } - return _list[idx]; - } - set - { - if (idx < 0 || idx >= _list.Count) - { - throw new ArgumentOutOfRangeException(); - } - _list[idx] = value; - } + get => _list[idx]; + set => _list[idx] = value; } /// - /// Adds the specified value to the JSON array. + /// Adds the specified value as the last item in this collection. /// /// The value to add. - /// Null value is allowed and represents a null JSON node. + /// Null value is allowed and represents the JSON null value. public void Add(JsonNode value) => _list.Add(value); /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. - /// - /// Provided value is null. - /// - public void Add(string value) => Add(new JsonString(value)); + /// Null value is allowed and represents the JSON null value. + public void Add(string value) + { + if (value == null) + { + Add((JsonNode)null); + } + else + { + Add(new JsonString(value)); + } + } /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. public void Add(bool value) => Add(new JsonBoolean(value)); /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. public void Add(byte value) => Add(new JsonNumber(value)); /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. public void Add(short value) => Add(new JsonNumber(value)); /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. public void Add(int value) => Add(new JsonNumber(value)); /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. public void Add(long value) => Add(new JsonNumber(value)); /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. /// @@ -282,7 +276,7 @@ public JsonNode this[int idx] public void Add(float value) => Add(new JsonNumber(value)); /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. /// @@ -291,35 +285,35 @@ public JsonNode this[int idx] public void Add(double value) => Add(new JsonNumber(value)); /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. [CLSCompliant(false)] public void Add(sbyte value) => Add(new JsonNumber(value)); /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. [CLSCompliant(false)] public void Add(ushort value) => Add(new JsonNumber(value)); /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. [CLSCompliant(false)] public void Add(uint value) => Add(new JsonNumber(value)); /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. [CLSCompliant(false)] public void Add(ulong value) => Add(new JsonNumber(value)); /// - /// Adds the specified value as a to the JSON array. + /// Adds the specified value as a as the last item in this collection. /// /// The value to add. public void Add(decimal value) => Add(new JsonNumber(value)); @@ -327,57 +321,57 @@ public JsonNode this[int idx] /// /// Inserts the specified item at the specified index of the JSON array. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. - /// Null item is allowed and represents a null JSON node. + /// The parameter may be , which represents the JSON null value. public void Insert(int index, JsonNode item) => _list.Insert(index, item); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. public void Insert(int index, string item) => Insert(index, new JsonString(item)); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. public void Insert(int index, bool item) => Insert(index, new JsonBoolean(item)); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. public void Insert(int index, byte item) => Insert(index, new JsonNumber(item)); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. public void Insert(int index, short item) => Insert(index, new JsonNumber(item)); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. public void Insert(int index, int item) => Insert(index, new JsonNumber(item)); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. public void Insert(int index, long item) => Insert(index, new JsonNumber(item)); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. /// /// Provided value is not in a legal JSON number format. @@ -385,9 +379,9 @@ public JsonNode this[int idx] public void Insert(int index, float item) => Insert(index, new JsonNumber(item)); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. /// /// Provided value is not in a legal JSON number format. @@ -395,219 +389,76 @@ public JsonNode this[int idx] public void Insert(int index, double item) => Insert(index, new JsonNumber(item)); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. [CLSCompliant(false)] public void Insert(int index, sbyte item) => Insert(index, new JsonNumber(item)); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. [CLSCompliant(false)] public void Insert(int index, ushort item) => Insert(index, new JsonNumber(item)); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. [CLSCompliant(false)] public void Insert(int index, uint item) => Insert(index, new JsonNumber(item)); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. [CLSCompliant(false)] public void Insert(int index, ulong item) => Insert(index, new JsonNumber(item)); /// - /// Inserts the specified item as a at the specified index of the JSON array. + /// Inserts the specified item as a at the specified index of the collection. /// - /// The zero-based index at which item should be inserted. + /// The zero-based index at which should be inserted. /// The item to add. public void Insert(int index, decimal item) => Insert(index, new JsonNumber(item)); /// - /// Determines whether a specified element is in a JSON array. + /// Determines whether a specified element is in a collection. /// /// Value to check. /// - /// if the value is successfully found in a JSON array, + /// if the value is successfully found in a collection, /// otherwise. /// public bool Contains(JsonNode value) => _list.Contains(value); /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - /// - /// Provided value is null. - /// - public bool Contains(string value) => Contains(new JsonString(value)); - - /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - public bool Contains(bool value) => Contains(new JsonBoolean(value)); - - /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - public bool Contains(byte value) => Contains(new JsonNumber(value)); - - /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - public bool Contains(short value) => Contains(new JsonNumber(value)); - - /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - public bool Contains(int value) => Contains(new JsonNumber(value)); - - /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - public bool Contains(long value) => Contains(new JsonNumber(value)); - - /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - /// - /// Provided value is not in a legal JSON number format. - /// - public bool Contains(float value) => Contains(new JsonNumber(value)); - - /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - /// - /// Provided value is not in a legal JSON number format. - /// - public bool Contains(double value) => Contains(new JsonNumber(value)); - - /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - [CLSCompliant(false)] - public bool Contains(sbyte value) => Contains(new JsonNumber(value)); - - /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - [CLSCompliant(false)] - public bool Contains(ushort value) => Contains(new JsonNumber(value)); - - /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - [CLSCompliant(false)] - public bool Contains(uint value) => Contains(new JsonNumber(value)); - - /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - [CLSCompliant(false)] - public bool Contains(ulong value) => Contains(new JsonNumber(value)); - - /// - /// Determines whether a representing provided value is in a JSON array. - /// - /// Value to check. - /// - /// if the value is successfully found in a JSON array, - /// otherwise. - /// - public bool Contains(decimal value) => Contains(new JsonNumber(value)); - - /// - /// Gets the number of elements contained in the JSON array. + /// Gets the number of elements contained in the collection. /// public int Count => _list.Count; /// - /// Gets a value indicating whether the JSON array is read-only. + /// Gets a value indicating whether the collection is read-only. /// public bool IsReadOnly => false; /// - /// Returns the zero-based index of the first occurrence of a specified item in the JSON array. + /// Returns the zero-based index of the first occurrence of a specified item in the collection. /// /// Item to find. - /// The zero-based starting index of the search. 0 (zero) is valid in an empty list. + /// The zero-based starting index of the search. 0 (zero) is valid in an empty collection. public int IndexOf(JsonNode item) => _list.IndexOf(item); /// - /// Returns the zero-based index of the last occurrence of a specified item in the JSON array. + /// Returns the zero-based index of the last occurrence of a specified item in the collection. /// /// Item to find. - /// The zero-based starting index of the search. 0 (zero) is valid in an empty list. + /// The zero-based starting index of the search. 0 (zero) is valid in an empty collection. public int LastIndexOf(JsonNode item) => _list.LastIndexOf(item); /// @@ -616,13 +467,13 @@ public JsonNode this[int idx] public void Clear() => _list.Clear(); /// - /// Removes the first occurrence of a specific object from the JSON array. + /// Removes the first occurrence of a specific object from the collection. /// /// - /// The object to remove from the JSON array. The value can be null and it represents null JSON node. + /// The object to remove from the collection. The value can be null and it represents null collection. /// /// - /// if the item is successfully found in a JSON array and removed, + /// if the item is successfully found in a collection and removed, /// otherwise. /// public bool Remove(JsonNode item) => _list.Remove(item); @@ -633,11 +484,11 @@ public JsonNode this[int idx] /// /// Thepredicate delegate that defines the conditions of the elements to remove. /// - /// The number of elements removed from the JSOJN array. + /// The number of elements removed from the collection. public int RemoveAll(Predicate match) => _list.RemoveAll(match); /// - /// Removes the item at the specified index of the JSON array. + /// Removes the item at the specified index of the collection. /// /// /// The zero-based index of the element to remove. @@ -645,31 +496,37 @@ public JsonNode this[int idx] public void RemoveAt(int index) => _list.RemoveAt(index); /// - /// Copies the JSON array or a portion of it to an array. + /// Copies the collection or a portion of it to an array. /// /// - /// The one-dimensional Array that is the destination of the elements copied from JSON array. - /// The Array must have zero-based indexing. + /// The one-dimensional array that is the destination of the elements copied from collection. + /// The array must have zero-based indexing. /// /// The zero-based index in array at which copying begins. void ICollection.CopyTo(JsonNode[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex); /// - /// Returns an enumerator that iterates through the JSON array values. + /// Returns an enumerator that iterates through the collection values. /// /// An enumerator structure for the . IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// - /// Returns an enumerator that iterates through the JSON array values. + /// Returns an enumerator that iterates through the collection values. + /// + /// An enumerator structure for the . + IEnumerator IEnumerable.GetEnumerator() => new JsonArrayEnumerator(this); + + /// + /// Returns an enumerator that iterates through the collection values. /// /// An enumerator structure for the . - public IEnumerator GetEnumerator() => new JsonArrayEnumerator(this); + public JsonArrayEnumerator GetEnumerator() => new JsonArrayEnumerator(this); /// - /// Creates a new JSON array that is a copy of the current instance. + /// Creates a new collection that is a copy of the current instance. /// - /// A new JSON array that is a copy of this instance. + /// A new collection that is a copy of this instance. public override JsonNode Clone() => new JsonArray(_list); /// diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs index 22d580203a8c..6a88a9bc1723 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs @@ -67,15 +67,6 @@ public JsonNode this[string propertyName] } } - /// - /// Returns an enumerator that iterates through the JSON object properties. - /// - /// An enumerator structure for the JSON object. - /// - /// Property name to set already exists if handling duplicates is set to . - /// - public IEnumerator> GetEnumerator() => new JsonObjectEnumerator(this); - /// /// Adds the specified property to the JSON object. /// @@ -555,8 +546,29 @@ public bool TryGetJsonArrayPropertyValue(string propertyName, out JsonArray json /// Returns an enumerator that iterates through the JSON object properties. /// /// An enumerator structure for the . + /// + /// Property name to set already exists if handling duplicates is set to . + /// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + /// + /// Returns an enumerator that iterates through the JSON object properties. + /// + /// An enumerator structure for the JSON object. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + IEnumerator> IEnumerable>.GetEnumerator() => new JsonObjectEnumerator(this); + + /// + /// Returns an enumerator that iterates through the JSON object properties. + /// + /// An enumerator structure for the JSON object. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + public JsonObjectEnumerator GetEnumerator() => new JsonObjectEnumerator(this); + /// /// Creates a new JSON object that is a copy of the current instance. /// From eea2a962a15e485815cc3a7e4ae1fcb16d706bfc Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Thu, 22 Aug 2019 14:50:11 -0700 Subject: [PATCH 19/29] work on review comments --- .../src/System/Text/Json/Node/JsonArray.cs | 12 +++++++++- .../Text/Json/Node/JsonArrayEnumerator.cs | 4 ++-- .../src/System/Text/Json/Node/JsonNode.cs | 11 +++++++--- .../src/System/Text/Json/Node/JsonObject.cs | 22 +++++++------------ .../Text/Json/Node/JsonObjectEnumerator.cs | 4 ++-- .../src/System/Text/Json/Node/JsonString.cs | 10 ++------- 6 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs index 0bd87bd7184e..837fdd8209d2 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs @@ -527,7 +527,17 @@ public void Add(string value) /// Creates a new collection that is a copy of the current instance. /// /// A new collection that is a copy of this instance. - public override JsonNode Clone() => new JsonArray(_list); + public override JsonNode Clone() + { + var jsonArray = new JsonArray(); + + foreach (JsonNode jsonNode in _list) + { + jsonArray.Add(jsonNode.Clone()); + } + + return jsonArray; + } /// /// Returns diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArrayEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArrayEnumerator.cs index 9ac1157f0d27..9bfb6a78e1b1 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonArrayEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArrayEnumerator.cs @@ -8,7 +8,7 @@ namespace System.Text.Json /// public struct JsonArrayEnumerator : IEnumerator { - private readonly IEnumerator _enumerator; + private readonly List.Enumerator _enumerator; /// /// Initializes a new instance of the class supporting an interation over provided JSON array. @@ -40,6 +40,6 @@ public struct JsonArrayEnumerator : IEnumerator /// /// Sets the enumerator to its initial position, which is before the first element in the JSON array. /// - public void Reset() => _enumerator.Reset(); + void IEnumerator.Reset() => ((IEnumerator)_enumerator).Reset(); } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs index 0747e19372a1..1433e724439c 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs @@ -6,6 +6,7 @@ #pragma warning disable CS1591 using System.Buffers; +using System.Diagnostics; using System.IO; namespace System.Text.Json @@ -43,12 +44,15 @@ public static JsonNode Parse(string json) } } - public static JsonNode DeepCopy(JsonNode jsonNode) => jsonNode.Clone(); - public abstract JsonNode Clone(); public static JsonNode DeepCopy(JsonElement jsonElement) { + if (!jsonElement.IsImmutable) + { + return GetNode(jsonElement).Clone(); + } + switch (jsonElement.ValueKind) { case JsonValueKind.Object: @@ -76,7 +80,8 @@ public static JsonNode DeepCopy(JsonElement jsonElement) case JsonValueKind.Null: return null; default: - throw new ArgumentException(); + Debug.Assert(jsonElement.ValueKind == JsonValueKind.Undefined, "No handler for JsonValueKind.{jsonElement.ValueKind}"); + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Undefined, jsonElement.ValueKind); } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs index 6a88a9bc1723..90e456b7d3ad 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs @@ -450,7 +450,7 @@ public JsonNode GetPropertyValue(string propertyName) /// /// Property with specified name is not found in JSON object. /// - /// + /// /// Property with specified name is not a JSON object. /// public JsonObject GetJsonObjectPropertyValue(string propertyName) @@ -460,7 +460,7 @@ public JsonObject GetJsonObjectPropertyValue(string propertyName) return jsonObject; } - throw new InvalidCastException(SR.Format(SR.PropertyTypeMismatch, propertyName)); + throw new ArgumentException(SR.Format(SR.PropertyTypeMismatch, propertyName)); } /// @@ -476,11 +476,8 @@ public bool TryGetJsonObjectPropertyValue(string propertyName, out JsonObject js { if (TryGetPropertyValue(propertyName, out JsonNode jsonNode)) { - if (jsonNode is JsonObject jsonNodeCasted) - { - jsonObject = jsonNodeCasted; - return true; - } + jsonObject = jsonNode as JsonObject; + return jsonObject != null; } jsonObject = null; @@ -495,7 +492,7 @@ public bool TryGetJsonObjectPropertyValue(string propertyName, out JsonObject js /// /// Property with specified name is not found in JSON array. /// - /// + /// /// Property with specified name is not a JSON array. /// public JsonArray GetJsonArrayPropertyValue(string propertyName) @@ -505,7 +502,7 @@ public JsonArray GetJsonArrayPropertyValue(string propertyName) return jsonArray; } - throw new InvalidCastException(SR.Format(SR.PropertyTypeMismatch, propertyName)); + throw new ArgumentException(SR.Format(SR.PropertyTypeMismatch, propertyName)); } /// @@ -521,11 +518,8 @@ public bool TryGetJsonArrayPropertyValue(string propertyName, out JsonArray json { if (TryGetPropertyValue(propertyName, out JsonNode jsonNode)) { - if (jsonNode is JsonArray jsonNodeCasted) - { - jsonArray = jsonNodeCasted; - return true; - } + jsonArray = jsonNode as JsonArray; + return jsonArray != null; } jsonArray = null; diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs index 2723e21ccc3d..64d87a941c0d 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs @@ -8,7 +8,7 @@ namespace System.Text.Json /// public struct JsonObjectEnumerator : IEnumerator> { - private readonly IEnumerator> _enumerator; + private readonly Dictionary.Enumerator _enumerator; /// /// Initializes a new instance of the class supporting an interation over provided JSON object. @@ -40,6 +40,6 @@ public struct JsonObjectEnumerator : IEnumerator> /// /// Sets the enumerator to its initial position, which is before the first element in the JSON object. /// - public void Reset() => _enumerator.Reset(); + void IEnumerator.Reset() => ((IEnumerator)_enumerator).Reset(); } } diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs index c87e3a205dc0..32ec727d48bd 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs @@ -37,15 +37,12 @@ public sealed class JsonString : JsonNode, IEquatable /// Initializes a new instance of the with a string representation of the structure. /// /// The value to represent as a JSON string. - public JsonString(Guid value) => Value = value.ToString(); + public JsonString(Guid value) => Value = value.ToString("D"); /// /// Initializes a new instance of the with an ISO 8601 representation of the structure. /// /// The value to represent as a JSON string. - /// - /// Text value of this instance is not in an ISO 8601 defined DateTimeOffset format. - /// /// /// The date and time is outside the range of dates supported by the calendar used by the invariant culture. /// @@ -55,9 +52,6 @@ public sealed class JsonString : JsonNode, IEquatable /// Initializes a new instance of the with an ISO 8601 representation of the structure. /// /// The value to represent as a JSON string. - /// - /// Text value of this instance is not in an ISO 8601 defined DateTimeOffset format. - /// /// /// The date and time is outside the range of dates supported by the calendar used by the invariant culture. /// @@ -106,7 +100,7 @@ public string Value /// /// Text value of this instance is not in a GUID recognized format. /// - public Guid GetGuid() => Guid.Parse(_value); + public Guid GetGuid() => Guid.ParseExact(_value, "D"); /// /// Converts the ISO 8601 text value of this instance to its ISO 8601 equivalent. From d63c48b721aa1f3dbcfd856eabfcb881149c80ea Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Thu, 22 Aug 2019 14:57:21 -0700 Subject: [PATCH 20/29] build fixed --- src/System.Text.Json/ref/System.Text.Json.cs | 35 ++++------------ .../Document/JsonElement.ArrayEnumerator.cs | 2 +- .../Document/JsonElement.ObjectEnumerator.cs | 2 +- src/System.Text.Json/tests/JsonArrayTests.cs | 21 ++++------ .../tests/JsonNodeTransformationsTests.cs | 40 +------------------ 5 files changed, 19 insertions(+), 81 deletions(-) diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index c250beabf0aa..262999f99056 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -13,7 +13,7 @@ public enum DuplicatePropertyNameHandling Ignore = 1, Error = 2, } - public partial class JsonArray : System.Text.Json.JsonNode, System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.IEnumerable + public sealed partial class JsonArray : System.Text.Json.JsonNode, System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.IEnumerable { public JsonArray() { } public JsonArray(System.Collections.Generic.IEnumerable values) { } @@ -58,25 +58,8 @@ public void Add(uint value) { } public void Add(ulong value) { } public void Clear() { } public override System.Text.Json.JsonNode Clone() { throw null; } - public bool Contains(bool value) { throw null; } - public bool Contains(byte value) { throw null; } - public bool Contains(decimal value) { throw null; } - public bool Contains(double value) { throw null; } - public bool Contains(short value) { throw null; } - public bool Contains(int value) { throw null; } - public bool Contains(long value) { throw null; } - [System.CLSCompliantAttribute(false)] - public bool Contains(sbyte value) { throw null; } - public bool Contains(float value) { throw null; } - public bool Contains(string value) { throw null; } public bool Contains(System.Text.Json.JsonNode value) { throw null; } - [System.CLSCompliantAttribute(false)] - public bool Contains(ushort value) { throw null; } - [System.CLSCompliantAttribute(false)] - public bool Contains(uint value) { throw null; } - [System.CLSCompliantAttribute(false)] - public bool Contains(ulong value) { throw null; } - public System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + public System.Text.Json.JsonArrayEnumerator GetEnumerator() { throw null; } public int IndexOf(System.Text.Json.JsonNode item) { throw null; } public void Insert(int index, bool item) { } public void Insert(int index, byte item) { } @@ -101,6 +84,7 @@ public void Insert(int index, ulong item) { } public int RemoveAll(System.Predicate match) { throw null; } public void RemoveAt(int index) { } void System.Collections.Generic.ICollection.CopyTo(System.Text.Json.JsonNode[] array, int arrayIndex) { } + System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } public partial struct JsonArrayEnumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable @@ -111,7 +95,7 @@ public partial struct JsonArrayEnumerator : System.Collections.Generic.IEnumerat object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } public bool MoveNext() { throw null; } - public void Reset() { } + void System.Collections.IEnumerator.Reset() { } } public sealed partial class JsonBoolean : System.Text.Json.JsonNode, System.IEquatable { @@ -284,14 +268,8 @@ internal JsonNode() { } public abstract System.Text.Json.JsonValueKind ValueKind { get; } public System.Text.Json.JsonElement AsJsonElement() { throw null; } public abstract System.Text.Json.JsonNode Clone(); - public static System.Text.Json.JsonNode DeepCopy(System.Text.Json.JsonDocument jsonDocument) { throw null; } public static System.Text.Json.JsonNode DeepCopy(System.Text.Json.JsonElement jsonElement) { throw null; } - public static System.Text.Json.JsonNode DeepCopy(System.Text.Json.JsonNode jsonNode) { throw null; } public static System.Text.Json.JsonNode GetNode(System.Text.Json.JsonElement jsonElement) { throw null; } - public static System.Text.Json.JsonNode Parse(System.Buffers.ReadOnlySequence utf8Json) { throw null; } - public static System.Text.Json.JsonNode Parse(System.IO.Stream utf8Json) { throw null; } - public static System.Text.Json.JsonNode Parse(System.ReadOnlyMemory utf8Json) { throw null; } - public static System.Text.Json.JsonNode Parse(System.ReadOnlyMemory json) { throw null; } public static System.Text.Json.JsonNode Parse(string json) { throw null; } public static bool TryGetNode(System.Text.Json.JsonElement jsonElement, out System.Text.Json.JsonNode jsonNode) { throw null; } } @@ -419,11 +397,12 @@ public void Add(string propertyName, ulong propertyValue) { } public void AddRange(System.Collections.Generic.IEnumerable> jsonProperties) { } public override System.Text.Json.JsonNode Clone() { throw null; } public bool ContainsProperty(string propertyName) { throw null; } - public System.Collections.Generic.IEnumerator> GetEnumerator() { throw null; } + public System.Text.Json.JsonObjectEnumerator GetEnumerator() { throw null; } public System.Text.Json.JsonArray GetJsonArrayPropertyValue(string propertyName) { throw null; } public System.Text.Json.JsonObject GetJsonObjectPropertyValue(string propertyName) { throw null; } public System.Text.Json.JsonNode GetPropertyValue(string propertyName) { throw null; } public bool Remove(string propertyName) { throw null; } + System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } public bool TryGetJsonArrayPropertyValue(string propertyName, out System.Text.Json.JsonArray jsonArray) { throw null; } public bool TryGetJsonObjectPropertyValue(string propertyName, out System.Text.Json.JsonObject jsonObject) { throw null; } @@ -437,7 +416,7 @@ public partial struct JsonObjectEnumerator : System.Collections.Generic.IEnumera object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } public bool MoveNext() { throw null; } - public void Reset() { } + void System.Collections.IEnumerator.Reset() { } } public readonly partial struct JsonProperty { diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs index 1bc77bd2814a..53cf75038dfc 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs @@ -101,7 +101,7 @@ public void Reset() if (_jsonArrayEnumerator.HasValue) { - _jsonArrayEnumerator.Value.Reset(); + ((IEnumerator)_jsonArrayEnumerator.Value).Reset(); } } diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs index bbf624bbb471..e458ae0e5fda 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs @@ -113,7 +113,7 @@ public void Reset() _curIdx = -1; if (_jsonObjectEnumerator.HasValue) { - _jsonObjectEnumerator.Value.Reset(); + ((IEnumerator)_jsonObjectEnumerator.Value).Reset(); } } diff --git a/src/System.Text.Json/tests/JsonArrayTests.cs b/src/System.Text.Json/tests/JsonArrayTests.cs index 4fcc2e2b981f..9badef96c72a 100644 --- a/src/System.Text.Json/tests/JsonArrayTests.cs +++ b/src/System.Text.Json/tests/JsonArrayTests.cs @@ -410,7 +410,7 @@ public static void TestAccesingNestedJsonArrayGetPropertyMethod() ((JsonString)issues.GetJsonArrayPropertyValue("features")[0]).Value = "feature 1569"; ((JsonString)issues.GetJsonArrayPropertyValue("features")[1]).Value = "feature 56134"; - Assert.True(((JsonArray)issues["bugs"]).Contains("bug 12356")); + Assert.Equal((JsonString)"bug 12356", ((JsonArray)issues["bugs"])[3]); Assert.Equal("feature 1569", (JsonString)((JsonArray)issues["features"])[0]); Assert.Equal("feature 56134", (JsonString)((JsonArray)issues["features"])[1]); } @@ -451,16 +451,13 @@ public static void TestClean() var jsonArray = new JsonArray { 1, 2, 3 }; Assert.Equal(3, jsonArray.Count); - Assert.True(jsonArray.Contains(1)); - Assert.True(jsonArray.Contains(2)); - Assert.True(jsonArray.Contains(3)); + Assert.Equal((JsonNumber)1, jsonArray[0]); + Assert.Equal((JsonNumber)2, jsonArray[1]); + Assert.Equal((JsonNumber)3, jsonArray[2]); jsonArray.Clear(); Assert.Equal(0, jsonArray.Count); - Assert.False(jsonArray.Contains(1)); - Assert.False(jsonArray.Contains(2)); - Assert.False(jsonArray.Contains(3)); } [Fact] @@ -469,16 +466,14 @@ public static void TestRemoveAll() var jsonArray = new JsonArray { 1, 2, 3 }; Assert.Equal(3, jsonArray.Count); - Assert.True(jsonArray.Contains(1)); - Assert.True(jsonArray.Contains(2)); - Assert.True(jsonArray.Contains(3)); + Assert.Equal((JsonNumber)1, jsonArray[0]); + Assert.Equal((JsonNumber)2, jsonArray[1]); + Assert.Equal((JsonNumber)3, jsonArray[2]); jsonArray.RemoveAll(v => ((JsonNumber)v).GetInt32() <= 2); Assert.Equal(1, jsonArray.Count); - Assert.False(jsonArray.Contains(1)); - Assert.False(jsonArray.Contains(2)); - Assert.True(jsonArray.Contains(3)); + Assert.Equal((JsonNumber)3, jsonArray[0]); } diff --git a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs index c51f5a500cf6..8db107466a1d 100644 --- a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs +++ b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs @@ -51,14 +51,6 @@ private static void CheckNodeFromSampleString(JsonNode node) Assert.Equal("value", (JsonString)innerObject["inner property"]); } - [Fact] - public static void TestJsonDocumentToJsonNode() - { - JsonDocument jsonDocument = JsonDocument.Parse(jsonSampleString); - JsonNode node = JsonNode.DeepCopy(jsonDocument); - CheckNodeFromSampleString(node); - } - [Fact] public static void TestParseStringToJsonNode() { @@ -66,39 +58,11 @@ public static void TestParseStringToJsonNode() CheckNodeFromSampleString(node); } - [Fact] - public static void TestParseBytesToJsonNode() - { - JsonNode node = JsonNode.Parse(new ReadOnlySequence(Encoding.UTF8.GetBytes(jsonSampleString).AsMemory())); - CheckNodeFromSampleString(node); - } - - [Fact] - public static void TestParseByteMemoryToJsonNode() - { - JsonNode node = JsonNode.Parse(Encoding.UTF8.GetBytes(jsonSampleString).AsMemory()); - CheckNodeFromSampleString(node); - } - - [Fact] - public static void TestParseCharMemoryToJsonNode() - { - JsonNode node = JsonNode.Parse(jsonSampleString.AsMemory()); - CheckNodeFromSampleString(node); - } - - [Fact] - public static void TestParseStreamToJsonNode() - { - JsonNode node = JsonNode.Parse(new MemoryStream(Encoding.UTF8.GetBytes(jsonSampleString))); - CheckNodeFromSampleString(node); - } - [Fact] public static void TestCloneJsonArray() { var jsonArray = new JsonArray { "value1", "value2" }; - var jsonArrayCopy = JsonNode.DeepCopy(jsonArray) as JsonArray; + var jsonArrayCopy = jsonArray.Clone() as JsonArray; Assert.Equal(2, jsonArrayCopy.Count); jsonArray.Add("value3"); Assert.Equal(2, jsonArrayCopy.Count); @@ -115,7 +79,7 @@ public static void TestCloneJsonNode() { "array", new JsonString[] { "value1", "value2"} } }; - var jsonObjectCopy = (JsonObject)JsonNode.DeepCopy(jsonObject); + var jsonObjectCopy = (JsonObject)jsonObject.Clone(); jsonObject["text"] = (JsonString)"something different"; Assert.Equal("property value", (JsonString)jsonObjectCopy["text"]); From 61317663916f7b2801e9ec3985adf81a86a980b1 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Thu, 22 Aug 2019 15:25:29 -0700 Subject: [PATCH 21/29] work on fixing tests --- .../src/System/Text/Json/Node/JsonArrayEnumerator.cs | 2 +- .../src/System/Text/Json/Node/JsonObjectEnumerator.cs | 6 +++--- .../tests/JsonNodeTransformationsTests.cs | 11 +++++++++++ src/System.Text.Json/tests/JsonObjectTests.cs | 4 ++-- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArrayEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArrayEnumerator.cs index 9bfb6a78e1b1..1dc16a232173 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonArrayEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArrayEnumerator.cs @@ -8,7 +8,7 @@ namespace System.Text.Json /// public struct JsonArrayEnumerator : IEnumerator { - private readonly List.Enumerator _enumerator; + private List.Enumerator _enumerator; /// /// Initializes a new instance of the class supporting an interation over provided JSON array. diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs index 64d87a941c0d..cc44ccb6f23c 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs @@ -8,7 +8,7 @@ namespace System.Text.Json /// public struct JsonObjectEnumerator : IEnumerator> { - private readonly Dictionary.Enumerator _enumerator; + private Dictionary.Enumerator _enumerator; /// /// Initializes a new instance of the class supporting an interation over provided JSON object. @@ -35,8 +35,8 @@ public struct JsonObjectEnumerator : IEnumerator> /// Advances the enumerator to the next property of the JSON object. /// /// - public bool MoveNext() => _enumerator.MoveNext(); - + public bool MoveNext() => enumerator.MoveNext(); + /// /// Sets the enumerator to its initial position, which is before the first element in the JSON object. /// diff --git a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs index 8db107466a1d..ce54bae7d68d 100644 --- a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs +++ b/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs @@ -68,6 +68,17 @@ public static void TestCloneJsonArray() Assert.Equal(2, jsonArrayCopy.Count); } + [Fact] + public static void TestDeepCloneJsonArray() + { + JsonArray inner = new JsonArray { 1, 2, 3 }; + JsonArray outer = new JsonArray { inner }; + JsonArray outerClone = (JsonArray)outer.Clone(); + ((JsonArray) outerClone[0]).Add(4); + + Assert.Equal(3, inner.Count); + } + [Fact] public static void TestCloneJsonNode() { diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index 736267aa81fc..2babf759aa1d 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -517,7 +517,7 @@ public static void TestTryGetProperty () [Fact] public static void TestGetJsonObjectPropertyThrows() { - Assert.Throws(() => + Assert.Throws(() => { var jsonObject = new JsonObject() { @@ -546,7 +546,7 @@ public static void TestTryGetObjectPropertyFails() [Fact] public static void TestGetJsonArrayPropertyThrows() { - Assert.Throws(() => + Assert.Throws(() => { var jsonObject = new JsonObject() { From ed785055a108f29f478d1dd985918cc12f31a5af Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Fri, 23 Aug 2019 08:55:14 -0700 Subject: [PATCH 22/29] object enumerator fixed --- .../Document/JsonElement.ObjectEnumerator.cs | 21 ++++++++++--------- .../Text/Json/Node/JsonObjectEnumerator.cs | 13 +++++++++--- src/System.Text.Json/tests/JsonArrayTests.cs | 16 +++++++------- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs index e458ae0e5fda..0adae2e7a4c1 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs @@ -19,7 +19,7 @@ public struct ObjectEnumerator : IEnumerable, IEnumerator propertyPair = _jsonObjectEnumerator.Value.Current; + KeyValuePair propertyPair = _jsonObjectEnumerator.Current; if (propertyPair.Value == null) { @@ -101,9 +101,9 @@ public ObjectEnumerator GetEnumerator() public void Dispose() { _curIdx = _endIdx; - if (_jsonObjectEnumerator.HasValue) + if (!_target.IsImmutable) { - _jsonObjectEnumerator.Value.Dispose(); + _jsonObjectEnumerator.Dispose(); } } @@ -111,9 +111,9 @@ public void Dispose() public void Reset() { _curIdx = -1; - if (_jsonObjectEnumerator.HasValue) + if (!_target.IsImmutable) { - ((IEnumerator)_jsonObjectEnumerator.Value).Reset(); + ((IEnumerator)_jsonObjectEnumerator).Reset(); } } @@ -123,9 +123,10 @@ public void Reset() /// public bool MoveNext() { - if (_jsonObjectEnumerator.HasValue) + if (!_target.IsImmutable) { - return _jsonObjectEnumerator.Value.MoveNext(); + bool result = _jsonObjectEnumerator.MoveNext(); + return result; } var document = _target._parent as JsonDocument; diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs index cc44ccb6f23c..301a3d254365 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs @@ -9,12 +9,16 @@ namespace System.Text.Json public struct JsonObjectEnumerator : IEnumerator> { private Dictionary.Enumerator _enumerator; + // private IEnumerator> _enumerator; /// /// Initializes a new instance of the class supporting an interation over provided JSON object. /// /// JSON object to iterate over. - public JsonObjectEnumerator(JsonObject jsonObject) => _enumerator = jsonObject._dictionary.GetEnumerator(); + public JsonObjectEnumerator(JsonObject jsonObject) + { + _enumerator = jsonObject._dictionary.GetEnumerator(); + } /// /// Gets the property in the JSON object at the current position of the enumerator. @@ -35,8 +39,11 @@ public struct JsonObjectEnumerator : IEnumerator> /// Advances the enumerator to the next property of the JSON object. /// /// - public bool MoveNext() => enumerator.MoveNext(); - + public bool MoveNext() + { + return _enumerator.MoveNext(); + } + /// /// Sets the enumerator to its initial position, which is before the first element in the JSON object. /// diff --git a/src/System.Text.Json/tests/JsonArrayTests.cs b/src/System.Text.Json/tests/JsonArrayTests.cs index 9badef96c72a..530b776159ee 100644 --- a/src/System.Text.Json/tests/JsonArrayTests.cs +++ b/src/System.Text.Json/tests/JsonArrayTests.cs @@ -16,38 +16,40 @@ private static void TestArray(T value1, T value2, Func getter, var value1Casted = value1 as dynamic; var value2Casted = value2 as dynamic; + JsonNode value1Node = nodeCtor(value1); + JsonNode value2Node = nodeCtor(value2); + var list = new List() { value1 }; JsonArray jsonArray = new JsonArray(list as dynamic); Assert.Equal(1, jsonArray.Count); - Assert.True(jsonArray.Contains(value1Casted)); + Assert.True(jsonArray.Contains(value1Node)); Assert.Equal(value1, getter(jsonArray)); jsonArray.Insert(0, value2 as dynamic); Assert.Equal(2, jsonArray.Count); - Assert.True(jsonArray.Contains(value2Casted)); + Assert.True(jsonArray.Contains(value2Node)); Assert.Equal(value2, getter(jsonArray)); - JsonNode value1Node = nodeCtor(value1); Assert.Equal(1, jsonArray.IndexOf(value1Node)); Assert.Equal(1, jsonArray.LastIndexOf(value1Node)); jsonArray.RemoveAt(0); - Assert.False(jsonArray.Contains(value2Casted)); + Assert.False(jsonArray.Contains(value2Node)); jsonArray.Remove(value1Node); - Assert.False(jsonArray.Contains(value1Casted)); + Assert.False(jsonArray.Contains(value1Node)); Assert.Equal(0, jsonArray.Count); jsonArray.Add(value2Casted); Assert.Equal(1, jsonArray.Count); - Assert.True(jsonArray.Contains(value2Casted)); + Assert.True(jsonArray.Contains(value2Node)); Assert.Equal(value2, getter(jsonArray)); jsonArray[0] = value1Node; Assert.Equal(1, jsonArray.Count); - Assert.True(jsonArray.Contains(value1Casted)); + Assert.True(jsonArray.Contains(value1Node)); Assert.Equal(value1, getter(jsonArray)); } From 7798af1694802c701ccce2cde15de3a7b61e7850 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Fri, 23 Aug 2019 09:12:34 -0700 Subject: [PATCH 23/29] array enumerator fixed --- .../Document/JsonElement.ArrayEnumerator.cs | 33 +++++------ .../src/System/Text/Json/Node/JsonArray.cs | 56 +++++++++++++++---- .../Text/Json/Node/JsonObjectEnumerator.cs | 10 +--- 3 files changed, 61 insertions(+), 38 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs index 53cf75038dfc..72dc57893eb5 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs @@ -18,8 +18,7 @@ public struct ArrayEnumerator : IEnumerable, IEnumerator public void Reset() { _curIdx = -1; - - if (_jsonArrayEnumerator.HasValue) - { - ((IEnumerator)_jsonArrayEnumerator.Value).Reset(); - } } /// @@ -111,9 +100,13 @@ public void Reset() /// public bool MoveNext() { - if (_jsonArrayEnumerator.HasValue) + if (_target._parent is JsonArray jsonArray) { - return _jsonArrayEnumerator.Value.MoveNext(); + Debug.Assert(jsonArray._version == _endIdx); + + _curIdx++; + + return _curIdx < jsonArray.Count; } var document = _target._parent as JsonDocument; diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs index 837fdd8209d2..13b7b7a6a01e 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonArray.cs @@ -13,17 +13,26 @@ namespace System.Text.Json public sealed class JsonArray : JsonNode, IList, IReadOnlyList { internal readonly List _list; + internal int _version; /// /// Initializes a new instance of the class representing the empty array. /// - public JsonArray() => _list = new List(); + public JsonArray() + { + _list = new List(); + _version = 0; + } /// /// Initializes a new instance of the class representing the specified collection. /// /// Collection to represent. - public JsonArray(IEnumerable values) => _list = new List(values); + public JsonArray(IEnumerable values) + { + _list = new List(values); + _version = 0; + } /// /// Initializes a new instance of the class representing the specified collection of s. @@ -209,7 +218,11 @@ public JsonArray(IEnumerable values) : this() public JsonNode this[int idx] { get => _list[idx]; - set => _list[idx] = value; + set + { + _list[idx] = value; + _version++; + } } /// @@ -217,7 +230,11 @@ public JsonNode this[int idx] /// /// The value to add. /// Null value is allowed and represents the JSON null value. - public void Add(JsonNode value) => _list.Add(value); + public void Add(JsonNode value) + { + _list.Add(value); + _version++; + } /// /// Adds the specified value as a as the last item in this collection. @@ -324,7 +341,11 @@ public void Add(string value) /// The zero-based index at which should be inserted. /// The item to add. /// The parameter may be , which represents the JSON null value. - public void Insert(int index, JsonNode item) => _list.Insert(index, item); + public void Insert(int index, JsonNode item) + { + _list.Insert(index, item); + _version++; + } /// /// Inserts the specified item as a at the specified index of the collection. @@ -464,7 +485,11 @@ public void Add(string value) /// /// Removes all elements from the JSON array. /// - public void Clear() => _list.Clear(); + public void Clear() + { + _list.Clear(); + _version++; + } /// /// Removes the first occurrence of a specific object from the collection. @@ -476,7 +501,11 @@ public void Add(string value) /// if the item is successfully found in a collection and removed, /// otherwise. /// - public bool Remove(JsonNode item) => _list.Remove(item); + public bool Remove(JsonNode item) + { + _version++; + return _list.Remove(item); + } /// /// Removes all the elements that match the conditions defined by the specified predicate. @@ -485,15 +514,22 @@ public void Add(string value) /// Thepredicate delegate that defines the conditions of the elements to remove. /// /// The number of elements removed from the collection. - public int RemoveAll(Predicate match) => _list.RemoveAll(match); - + public int RemoveAll(Predicate match) + { + _version++; + return _list.RemoveAll(match); + } /// /// Removes the item at the specified index of the collection. /// /// /// The zero-based index of the element to remove. /// - public void RemoveAt(int index) => _list.RemoveAt(index); + public void RemoveAt(int index) + { + _list.RemoveAt(index); + _version++; + } /// /// Copies the collection or a portion of it to an array. diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs index 301a3d254365..a1568c0d7dc2 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs @@ -15,10 +15,7 @@ public struct JsonObjectEnumerator : IEnumerator> /// Initializes a new instance of the class supporting an interation over provided JSON object. /// /// JSON object to iterate over. - public JsonObjectEnumerator(JsonObject jsonObject) - { - _enumerator = jsonObject._dictionary.GetEnumerator(); - } + public JsonObjectEnumerator(JsonObject jsonObject) => _enumerator = jsonObject._dictionary.GetEnumerator(); /// /// Gets the property in the JSON object at the current position of the enumerator. @@ -39,10 +36,7 @@ public JsonObjectEnumerator(JsonObject jsonObject) /// Advances the enumerator to the next property of the JSON object. /// /// - public bool MoveNext() - { - return _enumerator.MoveNext(); - } + public bool MoveNext() => _enumerator.MoveNext(); /// /// Sets the enumerator to its initial position, which is before the first element in the JSON object. From 6d4b86912a08a196ce5a67e08e5184a725cf9a47 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Fri, 23 Aug 2019 09:19:22 -0700 Subject: [PATCH 24/29] all test failures fixed --- .../src/System/Text/Json/Document/JsonElement.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index 55c4ba8920bc..de6dc44aedbf 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -417,8 +417,7 @@ public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out JsonElement /// public bool GetBoolean() { - // CheckValidInstance is redundant. Asking for the type will - // return None, which then throws the same exception in the return statement. + CheckValidInstance(); if (_parent is JsonDocument document) { From 2cb68c9df3a96339e77a959bd5b1471d49674b28 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Fri, 23 Aug 2019 10:19:49 -0700 Subject: [PATCH 25/29] minor tests modifications, documentation added to JsonNode --- .../src/System/Text/Json/Node/JsonNode.cs | 48 +++++- src/System.Text.Json/tests/JsonArrayTests.cs | 141 ++---------------- .../tests/JsonNodeCloneTests.cs | 59 ++++++++ .../tests/JsonNodeParseTests.cs | 55 +++++++ .../tests/JsonNodeTestData.cs | 4 +- ...Tests.cs => JsonNodeToJsonElementTests.cs} | 100 +------------ src/System.Text.Json/tests/JsonObjectTests.cs | 99 ++++++++++++ .../tests/System.Text.Json.Tests.csproj | 4 +- 8 files changed, 271 insertions(+), 239 deletions(-) create mode 100644 src/System.Text.Json/tests/JsonNodeCloneTests.cs create mode 100644 src/System.Text.Json/tests/JsonNodeParseTests.cs rename src/System.Text.Json/tests/{JsonNodeTransformationsTests.cs => JsonNodeToJsonElementTests.cs} (50%) diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs index 1433e724439c..c07584082e88 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonNode.cs @@ -2,12 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// for now disabling error caused by not adding documentation to methods -#pragma warning disable CS1591 - -using System.Buffers; using System.Diagnostics; -using System.IO; namespace System.Text.Json { @@ -18,12 +13,37 @@ public abstract class JsonNode { private protected JsonNode() { } + /// + /// Transforms this instance into representation. + /// Operations performed on this instance will modify the returned . + /// + /// Mutable with underneath. public JsonElement AsJsonElement() => new JsonElement(this); + /// + /// The that the node is. + /// public abstract JsonValueKind ValueKind { get; } + /// + /// Gets the represented by . + /// Operations performed on the returned will modify the . + /// + /// to get the from. + /// represented by . public static JsonNode GetNode(JsonElement jsonElement) => (JsonNode)jsonElement._parent; + /// + /// Gets the represented by the . + /// Operations performed on the returned will modify the . + /// A return value indicates whether the operation succeeded. + /// + /// to get the from. + /// represented by . + /// + /// if the operation succeded; + /// otherwise, + /// public static bool TryGetNode(JsonElement jsonElement, out JsonNode jsonNode) { if (!jsonElement.IsImmutable) @@ -36,6 +56,11 @@ public static bool TryGetNode(JsonElement jsonElement, out JsonNode jsonNode) return false; } + /// + /// Parses a string representiong JSON document into . + /// + /// JSON to parse. + /// representation of . public static JsonNode Parse(string json) { using (JsonDocument document = JsonDocument.Parse(json)) @@ -44,8 +69,18 @@ public static JsonNode Parse(string json) } } + /// + /// Performs a deep copy operation on this instance. + /// + /// which is a clone of this instance. public abstract JsonNode Clone(); + /// + /// Performs a deep copy operation on returning corresponding modifiable tree structure of JSON nodes. + /// Operations performed on returned does not modify . + /// + /// to copy. + /// representing . public static JsonNode DeepCopy(JsonElement jsonElement) { if (!jsonElement.IsImmutable) @@ -83,9 +118,6 @@ public static JsonNode DeepCopy(JsonElement jsonElement) Debug.Assert(jsonElement.ValueKind == JsonValueKind.Undefined, "No handler for JsonValueKind.{jsonElement.ValueKind}"); throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.Undefined, jsonElement.ValueKind); } - } } } - -#pragma warning restore CS1591 diff --git a/src/System.Text.Json/tests/JsonArrayTests.cs b/src/System.Text.Json/tests/JsonArrayTests.cs index 530b776159ee..89ff9b6b9a04 100644 --- a/src/System.Text.Json/tests/JsonArrayTests.cs +++ b/src/System.Text.Json/tests/JsonArrayTests.cs @@ -184,127 +184,17 @@ public static void TestDecimalArray() } [Fact] - public static void TestAddingJsonArray() - { - var preferences = new JsonObject() - { - { "colours", (JsonNode) new JsonArray{ "red", "green", "blue" } } - }; - - Assert.IsType(preferences["colours"]); - var colours = preferences["colours"] as JsonArray; - Assert.Equal(3, colours.Count); - - string[] expected = { "red", "green", "blue" }; - - for (int i = 0; i < colours.Count; i++) - { - Assert.IsType(colours[i]); - Assert.Equal(expected[i], colours[i] as JsonString); - } - } - - [Fact] - public static void TestCretingJsonArrayFromStringArray() + public static void TestCreatingJsonArrayFromStringArray() { string[] expected = { "sushi", "pasta", "cucumber soup" }; - var preferences = new JsonObject() - { - { "dishes", (JsonNode) new JsonArray(expected) } - }; - - Assert.IsType(preferences["dishes"]); - var dishesJson = preferences["dishes"] as JsonArray; - Assert.Equal(3, dishesJson.Count); - for (int i = 0; i < dishesJson.Count; i++) - { - Assert.IsType(dishesJson[i]); - Assert.Equal(expected[i], dishesJson[i] as JsonString); - } - } - - /// - /// Adding JsonArray to JsonObject by passing JsonNumber array - /// - [Fact] - public static void TestAddingJsonArrayFromJsonNumberArray() - { - var preferences = new JsonObject() - { - { "prime numbers", new JsonNumber[] { 19, 37 } } - }; - - Assert.IsType(preferences["prime numbers"]); - var primeNumbers = preferences["prime numbers"] as JsonArray; - Assert.Equal(2, primeNumbers.Count); + var dishesJsonArray = new JsonArray(expected); + Assert.Equal(3, dishesJsonArray.Count); - int[] expected = { 19, 37 }; - - for (int i = 0; i < primeNumbers.Count; i++) + for (int i = 0; i < dishesJsonArray.Count; i++) { - Assert.IsType(primeNumbers[i]); - Assert.Equal(expected[i], primeNumbers[i] as JsonNumber); - } - } - - [Fact] - public static void TestAddingJsonArrayFromIEnumerableOfStrings() - { - var sportsExperienceYears = new JsonObject() - { - { "skiing", 5 }, - { "cycling", 8 }, - { "hiking", 6 }, - { "chess", 2 }, - { "skating", 1 }, - }; - - // choose only sports with > 2 experience years - IEnumerable sports = sportsExperienceYears.Where(sport => ((JsonNumber)sport.Value).GetInt32() > 2).Select(sport => sport.Key); - - var preferences = new JsonObject() - { - { "sports", (JsonNode) new JsonArray(sports) } - }; - - Assert.IsType(preferences["sports"]); - var sportsJsonArray = preferences["sports"] as JsonArray; - Assert.Equal(3, sportsJsonArray.Count); - - for (int i = 0; i < sportsJsonArray.Count; i++) - { - Assert.IsType(sportsJsonArray[i]); - Assert.Equal(sports.ElementAt(i), sportsJsonArray[i] as JsonString); - } - } - - [Fact] - public static void TestAddingJsonArrayFromIEnumerableOfJsonNodes() - { - var strangeWords = new JsonArray() - { - "supercalifragilisticexpialidocious", - "gladiolus", - "albumen", - "smaragdine" - }; - - var preferences = new JsonObject() - { - { "strange words", strangeWords.Where(word => ((JsonString)word).Value.Length < 10) } - }; - - Assert.IsType(preferences["strange words"]); - var strangeWordsJsonArray = preferences["strange words"] as JsonArray; - Assert.Equal(2, strangeWordsJsonArray.Count); - - string[] expected = { "gladiolus", "albumen" }; - - for (int i = 0; i < strangeWordsJsonArray.Count; i++) - { - Assert.IsType(strangeWordsJsonArray[i]); - Assert.Equal(expected[i], strangeWordsJsonArray[i] as JsonString); + Assert.IsType(dishesJsonArray[i]); + Assert.Equal(expected[i], dishesJsonArray[i] as JsonString); } } @@ -341,10 +231,8 @@ public static void TestCreatingNestedJsonArray() }, }; - Assert.IsType(vertices[0]); - var innerJsonArray = vertices[0] as JsonArray; - Assert.IsType(innerJsonArray[0]); - innerJsonArray = innerJsonArray[0] as JsonArray; + var innerJsonArray = (JsonArray)vertices[0]; + innerJsonArray = (JsonArray)innerJsonArray[0]; Assert.IsType(innerJsonArray[0]); } @@ -356,8 +244,7 @@ public static void TestCreatingJsonArrayFromCollection() JsonString prevId = new JsonString(); foreach (JsonNode employeeId in employeesIds) { - Assert.IsType(employeeId); - var employeeIdString = employeeId as JsonString; + var employeeIdString = (JsonString)employeeId; Assert.NotEqual(prevId, employeeIdString); prevId = employeeIdString; } @@ -371,8 +258,7 @@ public static void TestCreatingJsonArrayFromCollectionOfString() JsonString prevId = new JsonString(); foreach (JsonNode employeeId in employeesIds) { - Assert.IsType(employeeId); - var employeeIdString = employeeId as JsonString; + var employeeIdString = (JsonString)employeeId; Assert.NotEqual(prevId, employeeIdString); prevId = employeeIdString; } @@ -391,8 +277,7 @@ public static void TestAddingToJsonArray() JsonString prevId = new JsonString(); foreach (JsonNode employeeId in employeesIds) { - Assert.IsType(employeeId); - var employeeIdString = employeeId as JsonString; + var employeeIdString = (JsonString)employeeId; Assert.NotEqual(prevId, employeeIdString); prevId = employeeIdString; } @@ -476,8 +361,6 @@ public static void TestRemoveAll() Assert.Equal(1, jsonArray.Count); Assert.Equal((JsonNumber)3, jsonArray[0]); - } - - + } } } diff --git a/src/System.Text.Json/tests/JsonNodeCloneTests.cs b/src/System.Text.Json/tests/JsonNodeCloneTests.cs new file mode 100644 index 000000000000..6ed89ed8da1f --- /dev/null +++ b/src/System.Text.Json/tests/JsonNodeCloneTests.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Text.Json.Tests +{ + public static class JsonNodeCloneTests + { + [Fact] + public static void TestCloneJsonArray() + { + var jsonArray = new JsonArray { "value1", "value2" }; + var jsonArrayCopy = jsonArray.Clone() as JsonArray; + Assert.Equal(2, jsonArrayCopy.Count); + jsonArray.Add("value3"); + Assert.Equal(2, jsonArrayCopy.Count); + } + + [Fact] + public static void TestDeepCloneJsonArray() + { + JsonArray inner = new JsonArray { 1, 2, 3 }; + JsonArray outer = new JsonArray { inner }; + JsonArray outerClone = (JsonArray)outer.Clone(); + ((JsonArray) outerClone[0]).Add(4); + + Assert.Equal(3, inner.Count); + } + + [Fact] + public static void TestCloneJsonNode() + { + var jsonObject = new JsonObject + { + { "text", "property value" }, + { "boolean", true }, + { "number", 15 }, + { "array", new JsonString[] { "value1", "value2"} } + }; + + var jsonObjectCopy = (JsonObject)jsonObject.Clone(); + + jsonObject["text"] = (JsonString)"something different"; + Assert.Equal("property value", (JsonString)jsonObjectCopy["text"]); + + ((JsonBoolean)jsonObject["boolean"]).Value = false; + Assert.True(((JsonBoolean)jsonObjectCopy["boolean"]).Value); + + Assert.Equal(2, jsonObjectCopy.GetJsonArrayPropertyValue("array").Count); + jsonObject.GetJsonArrayPropertyValue("array").Add("value3"); + Assert.Equal(2, jsonObjectCopy.GetJsonArrayPropertyValue("array").Count); + + jsonObject.Add("new one", 123); + Assert.Equal(4, jsonObjectCopy.PropertyNames.Count); + } + } +} diff --git a/src/System.Text.Json/tests/JsonNodeParseTests.cs b/src/System.Text.Json/tests/JsonNodeParseTests.cs new file mode 100644 index 000000000000..426772ee7bb9 --- /dev/null +++ b/src/System.Text.Json/tests/JsonNodeParseTests.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Text.Json.Tests +{ + public static class JsonNodeParseTests + { + private static string jsonSampleString = @" + { + ""text"": ""property value"", + ""boolean true"": true, + ""boolean false"": false, + ""null"": null, + ""int"": 17, + ""double"": 3.14, + ""scientific"": 3e100, + ""array"" : [1,2,3], + ""inner object"" : + { + ""inner property"" : ""value"" + } + }"; + + [Fact] + public static void TestParseStringToJsonNode() + { + JsonNode node = JsonNode.Parse(jsonSampleString); + + var jsonObject = (JsonObject)node; + Assert.Equal(9, jsonObject.PropertyNames.Count); + Assert.Equal(9, jsonObject.PropertyValues.Count); + Assert.Equal("property value", (JsonString)jsonObject["text"]); + Assert.True(((JsonBoolean)jsonObject["boolean true"]).Value); + Assert.False(((JsonBoolean)jsonObject["boolean false"]).Value); + Assert.Null(jsonObject["null"]); + Assert.Equal(17, ((JsonNumber)jsonObject["int"]).GetInt32()); + Assert.Equal(3.14, ((JsonNumber)jsonObject["double"]).GetDouble()); + Assert.Equal("3e100", ((JsonNumber)jsonObject["scientific"]).ToString()); + + var innerArray = (JsonArray)jsonObject["array"]; + Assert.Equal(3, innerArray.Count); + Assert.Equal(1, ((JsonNumber)innerArray[0]).GetInt32()); + Assert.Equal(2, ((JsonNumber)innerArray[1]).GetInt32()); + Assert.Equal(3, ((JsonNumber)innerArray[2]).GetInt32()); + + var innerObject = (JsonObject)jsonObject["inner object"]; + Assert.Equal(1, innerObject.PropertyNames.Count); + Assert.Equal(1, innerObject.PropertyValues.Count); + Assert.Equal("value", (JsonString)innerObject["inner property"]); + } + } +} diff --git a/src/System.Text.Json/tests/JsonNodeTestData.cs b/src/System.Text.Json/tests/JsonNodeTestData.cs index 8f5ce9a07bab..39d443c2f7c0 100644 --- a/src/System.Text.Json/tests/JsonNodeTestData.cs +++ b/src/System.Text.Json/tests/JsonNodeTestData.cs @@ -34,7 +34,7 @@ public static IEnumerable> GetTenBestEmployees() /// Returns following JsonObject: /// { /// { "name" : "John" } - /// { "phone numbers" : { "work" : "123-456-7890", "home": "123-456-7890" } } + /// { "phone numbers" : { "work" : "425-555-0123", "home": "425-555-0134" } } /// { /// "reporting employees" : /// { @@ -56,7 +56,7 @@ public static JsonObject GetManager() "phone numbers", new JsonObject() { - { "work", "123-456-7890" }, { "home", "123-456-7890" } + { "work", "425-555-0123" }, { "home", "425-555-0134" } } ); diff --git a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs b/src/System.Text.Json/tests/JsonNodeToJsonElementTests.cs similarity index 50% rename from src/System.Text.Json/tests/JsonNodeTransformationsTests.cs rename to src/System.Text.Json/tests/JsonNodeToJsonElementTests.cs index ce54bae7d68d..ef4e82346cf8 100644 --- a/src/System.Text.Json/tests/JsonNodeTransformationsTests.cs +++ b/src/System.Text.Json/tests/JsonNodeToJsonElementTests.cs @@ -2,110 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Buffers; -using System.IO; using Xunit; namespace System.Text.Json.Tests { - public static class JsonNodeTransformationsTests + public static class JsonNodeToJsonElementTests { - private static string jsonSampleString = @" - { - ""text"": ""property value"", - ""boolean true"": true, - ""boolean false"": false, - ""null"": null, - ""int"": 17, - ""double"": 3.14, - ""scientific"": 3e100, - ""array"" : [1,2,3], - ""inner object"" : - { - ""inner property"" : ""value"" - } - }"; - - private static void CheckNodeFromSampleString(JsonNode node) - { - var jsonObject = (JsonObject)node; - Assert.Equal(9, jsonObject.PropertyNames.Count); - Assert.Equal(9, jsonObject.PropertyValues.Count); - Assert.Equal("property value", (JsonString)jsonObject["text"]); - Assert.True(((JsonBoolean)jsonObject["boolean true"]).Value); - Assert.False(((JsonBoolean)jsonObject["boolean false"]).Value); - Assert.Null(jsonObject["null"]); - Assert.Equal(17, ((JsonNumber)jsonObject["int"]).GetInt32()); - Assert.Equal(3.14, ((JsonNumber)jsonObject["double"]).GetDouble()); - Assert.Equal("3e100", ((JsonNumber)jsonObject["scientific"]).ToString()); - - var innerArray = (JsonArray)jsonObject["array"]; - Assert.Equal(3, innerArray.Count); - Assert.Equal(1, ((JsonNumber)innerArray[0]).GetInt32()); - Assert.Equal(2, ((JsonNumber)innerArray[1]).GetInt32()); - Assert.Equal(3, ((JsonNumber)innerArray[2]).GetInt32()); - - var innerObject = (JsonObject)jsonObject["inner object"]; - Assert.Equal(1, innerObject.PropertyNames.Count); - Assert.Equal(1, innerObject.PropertyValues.Count); - Assert.Equal("value", (JsonString)innerObject["inner property"]); - } - - [Fact] - public static void TestParseStringToJsonNode() - { - JsonNode node = JsonNode.Parse(jsonSampleString); - CheckNodeFromSampleString(node); - } - - [Fact] - public static void TestCloneJsonArray() - { - var jsonArray = new JsonArray { "value1", "value2" }; - var jsonArrayCopy = jsonArray.Clone() as JsonArray; - Assert.Equal(2, jsonArrayCopy.Count); - jsonArray.Add("value3"); - Assert.Equal(2, jsonArrayCopy.Count); - } - - [Fact] - public static void TestDeepCloneJsonArray() - { - JsonArray inner = new JsonArray { 1, 2, 3 }; - JsonArray outer = new JsonArray { inner }; - JsonArray outerClone = (JsonArray)outer.Clone(); - ((JsonArray) outerClone[0]).Add(4); - - Assert.Equal(3, inner.Count); - } - - [Fact] - public static void TestCloneJsonNode() - { - var jsonObject = new JsonObject - { - { "text", "property value" }, - { "boolean", true }, - { "number", 15 }, - { "array", new JsonString[] { "value1", "value2"} } - }; - - var jsonObjectCopy = (JsonObject)jsonObject.Clone(); - - jsonObject["text"] = (JsonString)"something different"; - Assert.Equal("property value", (JsonString)jsonObjectCopy["text"]); - - ((JsonBoolean)jsonObject["boolean"]).Value = false; - Assert.True(((JsonBoolean)jsonObjectCopy["boolean"]).Value); - - Assert.Equal(2, jsonObjectCopy.GetJsonArrayPropertyValue("array").Count); - jsonObject.GetJsonArrayPropertyValue("array").Add("value3"); - Assert.Equal(2, jsonObjectCopy.GetJsonArrayPropertyValue("array").Count); - - jsonObject.Add("new one", 123); - Assert.Equal(4, jsonObjectCopy.PropertyNames.Count); - } - [Fact] public static void TestAsJsonElement() { diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index 2babf759aa1d..392eaf568b6d 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using Xunit; namespace System.Text.Json.Tests @@ -304,6 +305,104 @@ public static void TestAddingKeyValuePairsCollectionAfterInitialization() CheckEmployeesAreDifferent(employees); } + [Fact] + public static void TestAddingJsonArrayFromJsonNumberArray() + { + var preferences = new JsonObject() + { + { "prime numbers", new JsonNumber[] { 19, 37 } } + }; + + var primeNumbers = (JsonArray)preferences["prime numbers"]; + Assert.Equal(2, primeNumbers.Count); + + int[] expected = { 19, 37 }; + + for (int i = 0; i < primeNumbers.Count; i++) + { + Assert.IsType(primeNumbers[i]); + Assert.Equal(expected[i], primeNumbers[i] as JsonNumber); + } + } + + [Fact] + public static void TestAddingJsonArray() + { + var preferences = new JsonObject() + { + { "colours", (JsonNode) new JsonArray{ "red", "green", "blue" } } + }; + + var colours = (JsonArray)preferences["colours"]; + Assert.Equal(3, colours.Count); + + string[] expected = { "red", "green", "blue" }; + + for (int i = 0; i < colours.Count; i++) + { + Assert.IsType(colours[i]); + Assert.Equal(expected[i], colours[i] as JsonString); + } + } + + [Fact] + public static void TestAddingJsonArrayFromIEnumerableOfStrings() + { + var sportsExperienceYears = new JsonObject() + { + { "skiing", 5 }, + { "cycling", 8 }, + { "hiking", 6 }, + { "chess", 2 }, + { "skating", 1 }, + }; + + // choose only sports with > 2 experience years + IEnumerable sports = sportsExperienceYears.Where(sport => ((JsonNumber)sport.Value).GetInt32() > 2).Select(sport => sport.Key); + + var preferences = new JsonObject() + { + { "sports", (JsonNode) new JsonArray(sports) } + }; + + var sportsJsonArray = (JsonArray)preferences["sports"]; + Assert.Equal(3, sportsJsonArray.Count); + + for (int i = 0; i < sportsJsonArray.Count; i++) + { + Assert.IsType(sportsJsonArray[i]); + Assert.Equal(sports.ElementAt(i), sportsJsonArray[i] as JsonString); + } + } + + [Fact] + public static void TestAddingJsonArrayFromIEnumerableOfJsonNodes() + { + var strangeWords = new JsonArray() + { + "supercalifragilisticexpialidocious", + "gladiolus", + "albumen", + "smaragdine" + }; + + var preferences = new JsonObject() + { + { "strange words", strangeWords.Where(word => ((JsonString)word).Value.Length < 10) } + }; + + var strangeWordsJsonArray = (JsonArray)preferences["strange words"]; + Assert.Equal(2, strangeWordsJsonArray.Count); + + string[] expected = { "gladiolus", "albumen" }; + + for (int i = 0; i < strangeWordsJsonArray.Count; i++) + { + Assert.IsType(strangeWordsJsonArray[i]); + Assert.Equal(expected[i], strangeWordsJsonArray[i] as JsonString); + } + } + [Fact] public static void TestContains() { diff --git a/src/System.Text.Json/tests/System.Text.Json.Tests.csproj b/src/System.Text.Json/tests/System.Text.Json.Tests.csproj index f4a744f60e02..d06ef9805a5e 100644 --- a/src/System.Text.Json/tests/System.Text.Json.Tests.csproj +++ b/src/System.Text.Json/tests/System.Text.Json.Tests.csproj @@ -21,6 +21,8 @@ + + @@ -118,7 +120,7 @@ - + From 2c63db6fb885ee6d6ade7fc2918e7d30a0146401 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Fri, 23 Aug 2019 10:25:56 -0700 Subject: [PATCH 26/29] minor fix --- .../src/System/Text/Json/Node/JsonObjectEnumerator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs index a1568c0d7dc2..9b7a7b79acff 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs @@ -9,7 +9,6 @@ namespace System.Text.Json public struct JsonObjectEnumerator : IEnumerator> { private Dictionary.Enumerator _enumerator; - // private IEnumerator> _enumerator; /// /// Initializes a new instance of the class supporting an interation over provided JSON object. From b5af5939a8f56314dff86623e4af9feab1dbd8f7 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Fri, 23 Aug 2019 10:30:25 -0700 Subject: [PATCH 27/29] implementation related questions removed --- src/System.Text.Json/docs/writable_json_dom_spec.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/System.Text.Json/docs/writable_json_dom_spec.md b/src/System.Text.Json/docs/writable_json_dom_spec.md index 4d7a76a39a79..1d2369086305 100644 --- a/src/System.Text.Json/docs/writable_json_dom_spec.md +++ b/src/System.Text.Json/docs/writable_json_dom_spec.md @@ -262,18 +262,12 @@ Mailbox.SendAllEmployeesData(employees.AsJsonElement()); * Should `ToString` on `JsonBoolean` and `JsonString` return the .NET or JSON representation? * Do we want to keep implicit cast operators (even though for `JsonNumber` it would mean throwing in some cases, which is against FDG)? * Do we want overloads that take a `StringComparison` enum for methods retrieving properties? It would make API easier to use where case is not important. -* Where do we want to enable user to set handling properties manner? Do we want to support it as `JsonElement` does not? (and currently `JsonNode.Parse` implementation uses `JsonDocument.Parse`) +* Where do we want to enable user to set handling properties manner? Do we want to support it as `JsonElement` does not? * Do we want to support transforming `JsonObject` into JSON string? Should `ToString` behave this way or do we want an additional method - e.g. `GetJsonString` (it might be confusing as we have a type `JsonString`) or `GetJsonRepresenation`? * `AsJsonElement` function on `JsonNode` currently does not work for `null` node. Do we want to support null case somehow? -* Do we want separate `JsonElement` implementations for `JsonNode` and `JsonDocument` or can the implementation be mixed as it is now (with a lot of ifs in each method)? -* Do we want separate `JsonElement.ArrayEnumerator` and `JsonElement.ObjectEnumerator` implementations for `JsonNode` and `JsonDocument`? Do we want separate methods in `JsonElement` returning strongly typed different implementations? -* Is it OK that `JsonElement.GetRawText` does not return the raw text for `JsonNode` parent? -* Should we support `JsonElement.WriteTo` for `JsonNode` parent? -* Private property `JsonElement.TokenType` is currently throwing an `InvalidCast` exception for `JsonNode` parent in debugger. Is it OK? * Do we want both `Clone` and `DeepCopy` methods for `JsonNode`? * Are we OK with needing to cast `JsonArray` to `JsonNode` in order to add it to `JsonObject`? (there's an ambiguous call between `JsonArray` and `IEnumerable` right now) * Do we want `IsImmutable` property for `JsonElement`? -* Is `DateTime` for `JsonString` handled correctly? ## Useful links From 0a5c15faa505d76ee2d0e6bcbcec6b5f007dd872 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Fri, 23 Aug 2019 11:03:10 -0700 Subject: [PATCH 28/29] netfx fix --- .../src/System/Text/Json/Document/JsonElement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index de6dc44aedbf..a18b0331afbc 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -388,7 +388,7 @@ public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out JsonElement if (jsonNode is JsonObject jsonObject) { - if (jsonObject.TryGetPropertyValue(Encoding.UTF8.GetString(utf8PropertyName), out JsonNode nodeValue)) + if (jsonObject.TryGetPropertyValue(JsonHelpers.Utf8GetString(utf8PropertyName), out JsonNode nodeValue)) { value = nodeValue.AsJsonElement(); return true; From 126d03e3173f547579aa62e989e0bfe7733ecd38 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Mon, 26 Aug 2019 09:58:44 -0700 Subject: [PATCH 29/29] review comments included --- .../docs/writable_json_dom_spec.md | 3 +- .../src/Resources/Strings.resx | 3 ++ .../Document/JsonElement.ArrayEnumerator.cs | 44 +++++++++++-------- .../Document/JsonElement.ObjectEnumerator.cs | 23 +++++----- .../System/Text/Json/Document/JsonElement.cs | 19 ++++++-- .../src/System/Text/Json/Node/JsonString.cs | 2 +- .../tests/JsonNodeToJsonElementTests.cs | 14 ++++++ 7 files changed, 74 insertions(+), 34 deletions(-) diff --git a/src/System.Text.Json/docs/writable_json_dom_spec.md b/src/System.Text.Json/docs/writable_json_dom_spec.md index 1d2369086305..9e033ea1413c 100644 --- a/src/System.Text.Json/docs/writable_json_dom_spec.md +++ b/src/System.Text.Json/docs/writable_json_dom_spec.md @@ -243,6 +243,7 @@ Mailbox.SendAllEmployeesData(employees.AsJsonElement()); * Do we want to add recursive equals on `JsonArray` and `JsonObject`? * Do we want to make `JsonNode` derived types implement `IComparable` (which ones)? * Do we want `JsonObject` to implement `IDictionary` and `JsonArray` to implement `IList` (currently JsonArray does, but JsonObject not)? +* Do we want `JsonArray` to support `Contains`, `IndexOf` and `LastIndexOf` if we keep reference equality for `JsonArray`/`JsonObject` and don't have a good way of comparing numbers? * Would escaped characters be supported for creating `JsonNumber` from string? * Is the API for `JsonNode` and `JsonElement` interactions sufficient? * Do we want to support duplicate and order preservation/control when adding/removing values in `JsonArray`/`JsonObject`? @@ -251,7 +252,7 @@ Mailbox.SendAllEmployeesData(employees.AsJsonElement()); | Solution | Pros | Cons | |----------|:-------------|--------| |current API| - no additional checks need to be made | - creating recursive loop by the user may be problematic | - |tracking nodes | - handles recursive loop problem | - when node is added to a parent, it needs to be checked
if it already has a parent and make a copy if it has | + |tracking nodes | - handles recursive loop problem | - when node is added to a parent, it needs to be checked
if it already has a parent and make a copy if it has | * Do we want to change `JsonNumber`'s backing field to something different than `string`? Suggestions: - `Span` or array of `Utf8String`/`Char8` (once they come online in the future) / `byte` diff --git a/src/System.Text.Json/src/Resources/Strings.resx b/src/System.Text.Json/src/Resources/Strings.resx index 0b044d0effe1..88921151ae62 100644 --- a/src/System.Text.Json/src/Resources/Strings.resx +++ b/src/System.Text.Json/src/Resources/Strings.resx @@ -441,4 +441,7 @@ The DuplicatePropertyNameHandling enum must be set to one of the supported values. + + The JSON array was modified during iteration. + \ No newline at end of file diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs index 72dc57893eb5..34edd8cd3853 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ArrayEnumerator.cs @@ -18,7 +18,7 @@ public struct ArrayEnumerator : IEnumerable, IEnumerator= jsonArray.Count) + { + return default; + } + + return jsonArray[_curIdx].AsJsonElement(); } + var document = (JsonDocument)_target._parent; return new JsonElement(document, _curIdx); } } @@ -85,7 +87,7 @@ public ArrayEnumerator GetEnumerator() /// public void Dispose() { - _curIdx = _endIdx; + _curIdx = _endIdxOrVersion; } /// @@ -102,16 +104,21 @@ public bool MoveNext() { if (_target._parent is JsonArray jsonArray) { - Debug.Assert(jsonArray._version == _endIdx); + if (jsonArray._version != _endIdxOrVersion) + { + throw new InvalidOperationException(SR.ArrayModifiedDuringIteration); + } - _curIdx++; + if (_curIdx >= jsonArray.Count) + { + return false; + } + _curIdx++; return _curIdx < jsonArray.Count; } - var document = _target._parent as JsonDocument; - - if (_curIdx >= _endIdx) + if (_curIdx >= _endIdxOrVersion) { return false; } @@ -122,10 +129,11 @@ public bool MoveNext() } else { + var document = (JsonDocument)_target._parent; _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true); } - return _curIdx < _endIdx; + return _curIdx < _endIdxOrVersion; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs index 0adae2e7a4c1..92968bacb1f0 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.ObjectEnumerator.cs @@ -51,22 +51,27 @@ public JsonProperty Current { KeyValuePair propertyPair = _jsonObjectEnumerator.Current; - if (propertyPair.Value == null) + // propertyPair.Key is null before first after last call of MoveNext + if (propertyPair.Key == null) { - return new JsonProperty(new JsonElement(null), propertyPair.Key); + return default; } - else + + // null JsonNode case + if (propertyPair.Value == null) { - return new JsonProperty(propertyPair.Value.AsJsonElement(), propertyPair.Key); + return new JsonProperty(new JsonElement(null), propertyPair.Key); } - } - var document = (JsonDocument)_target._parent; + return new JsonProperty(propertyPair.Value.AsJsonElement(), propertyPair.Key); + } if (_curIdx < 0) { return default; } + + var document = (JsonDocument)_target._parent; return new JsonProperty(new JsonElement(document, _curIdx)); } } @@ -125,12 +130,9 @@ public bool MoveNext() { if (!_target.IsImmutable) { - bool result = _jsonObjectEnumerator.MoveNext(); - return result; + return _jsonObjectEnumerator.MoveNext(); } - var document = _target._parent as JsonDocument; - if (_curIdx >= _endIdx) { return false; @@ -142,6 +144,7 @@ public bool MoveNext() } else { + var document = (JsonDocument)_target._parent; _curIdx = document.GetEndIndex(_curIdx, includeEndElement: true); } diff --git a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index a18b0331afbc..94bf57923d46 100644 --- a/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -67,8 +67,8 @@ public JsonValueKind ValueKind if (jsonNode == null) { return JsonValueKind.Null; - } + return jsonNode.ValueKind; } } @@ -477,7 +477,7 @@ public string GetString() ///
/// Receives the value. /// - /// This method does not create a byte[] representation of values other than bsae 64 encoded JSON strings. + /// This method does not create a byte[] representation of values other than base 64 encoded JSON strings. /// /// /// if the entire token value is encoded as valid Base64 text and can be successfully decoded to bytes. @@ -493,8 +493,19 @@ public bool TryGetBytesFromBase64(out byte[] value) { CheckValidInstance(); - JsonDocument document = (JsonDocument)_parent; - return document.TryGetValue(_idx, out value); + if (_parent is JsonDocument document) + { + return document.TryGetValue(_idx, out value); + } + + var jsonNode = (JsonNode)_parent; + + if (jsonNode is JsonString) + { + throw new NotSupportedException(); + } + + throw ThrowHelper.GetJsonElementWrongTypeException(JsonValueKind.String, jsonNode.ValueKind); } /// diff --git a/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs b/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs index 32ec727d48bd..de5538f4e78a 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonString.cs @@ -137,7 +137,7 @@ public string Value /// /// Text value of this instance is not in a GUID recognized format. /// - public bool TryGetGuid(out Guid value) => Guid.TryParse(_value, out value); + public bool TryGetGuid(out Guid value) => Guid.TryParseExact(_value, "D", out value); /// /// Converts a to a . diff --git a/src/System.Text.Json/tests/JsonNodeToJsonElementTests.cs b/src/System.Text.Json/tests/JsonNodeToJsonElementTests.cs index ef4e82346cf8..dc222e4aa6e6 100644 --- a/src/System.Text.Json/tests/JsonNodeToJsonElementTests.cs +++ b/src/System.Text.Json/tests/JsonNodeToJsonElementTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections; using Xunit; namespace System.Text.Json.Tests @@ -58,7 +59,10 @@ public static void TestAsJsonElement() Assert.Equal(JsonValueKind.String, innerEnumerator.Current.ValueKind); Assert.Equal("value2", innerEnumerator.Current.GetString()); + Assert.False(innerEnumerator.MoveNext()); innerEnumerator.Dispose(); + + Assert.False(enumerator.MoveNext()); enumerator.Dispose(); // Modifying JsonObject will change JsonElement: @@ -67,6 +71,16 @@ public static void TestAsJsonElement() Assert.Equal(123, jsonElement.GetProperty("text").GetInt32()); } + [Fact] + public static void TestArrayIterator() + { + JsonArray array = new JsonArray { 1, 2, 3 }; + JsonElement jsonNodeElement = array.AsJsonElement(); + IEnumerator enumerator = jsonNodeElement.EnumerateArray(); + array.Add(4); + Assert.Throws(() => enumerator.MoveNext()); + } + [Fact] public static void TestGetNode() {