From 711a5aa682b5257daf3d543da72127f49b6c9d36 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Fri, 9 Aug 2019 18:11:56 -0700 Subject: [PATCH 01/15] JsonObject implementation added JsonObject tests added JsonObject documentation adde --- src/System.Text.Json/ref/System.Text.Json.cs | 43 ++ .../src/Resources/Strings.resx | 11 +- .../src/System.Text.Json.csproj | 5 + .../Node/DuplicatePropertyNameHandling.cs | 21 + .../src/System/Text/Json/Node/JsonObject.cs | 392 +++++++++++ .../tests/JsonObjectTests.TestData.cs | 122 ++++ src/System.Text.Json/tests/JsonObjectTests.cs | 609 ++++++++++++++++++ .../tests/System.Text.Json.Tests.csproj | 2 + 8 files changed, 1204 insertions(+), 1 deletion(-) create mode 100644 src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs create mode 100644 src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs create mode 100644 src/System.Text.Json/tests/JsonObjectTests.TestData.cs create mode 100644 src/System.Text.Json/tests/JsonObjectTests.cs diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index 07586b953d66..b27c8de4cc1a 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -7,6 +7,12 @@ namespace System.Text.Json { + public enum DuplicatePropertyNameHandling + { + Replace = 0, + Ignore = 1, + Error = 2, + } public sealed partial class JsonBoolean : System.Text.Json.JsonNode, System.IEquatable { public JsonBoolean() { } @@ -260,6 +266,43 @@ public void SetUInt64(ulong value) { } [System.CLSCompliantAttribute(false)] public bool TryGetUInt64(out ulong value) { throw null; } } + public sealed partial class JsonObject : System.Text.Json.JsonNode, System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable + { + 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 key] { get { throw null; } set { } } + public System.Collections.Generic.ICollection PropertyNames { get { throw null; } } + public System.Collections.Generic.ICollection Values { 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) { } + public void Add(string propertyName, decimal propertyValue) { } + public void Add(string propertyName, double propertyValue) { } + public void Add(string propertyName, short propertyValue) { } + public void Add(string propertyName, int propertyValue) { } + public void Add(string propertyName, long propertyValue) { } + [System.CLSCompliantAttribute(false)] + public void Add(string propertyName, sbyte propertyValue) { } + public void Add(string propertyName, float propertyValue) { } + public void Add(string propertyName, string propertyValue) { } + public void Add(string propertyName, System.Text.Json.JsonNode propertyValue) { } + [System.CLSCompliantAttribute(false)] + public void Add(string propertyName, ushort propertyValue) { } + [System.CLSCompliantAttribute(false)] + 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 bool ContainsProperty(string propertyName) { throw null; } + public System.Collections.Generic.IEnumerator> GetEnumerator() { throw null; } + public System.Text.Json.JsonObject GetJsonObjectProperty(string propertyName) { throw null; } + public System.Text.Json.JsonNode GetProperty(string propertyName) { throw null; } + public void ModifyPropertyName(string oldName, string newName) { } + public bool Remove(string propertyName) { throw null; } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + public bool TryGetJsonObjectProperty(string propertyName, out System.Text.Json.JsonObject jsonObject) { throw null; } + public bool TryGetProperty(string propertyName, out System.Text.Json.JsonNode jsonNode) { throw null; } + } public readonly partial struct JsonProperty { private readonly object _dummy; diff --git a/src/System.Text.Json/src/Resources/Strings.resx b/src/System.Text.Json/src/Resources/Strings.resx index 898e45978013..9d40e6a3435b 100644 --- a/src/System.Text.Json/src/Resources/Strings.resx +++ b/src/System.Text.Json/src/Resources/Strings.resx @@ -429,4 +429,13 @@ Expected a number, but instead got empty string. - + + Provided property name already exists. + + + Property with specified name not found + + + Property with specified name has a diffent type than expected + + \ No newline at end of file diff --git a/src/System.Text.Json/src/System.Text.Json.csproj b/src/System.Text.Json/src/System.Text.Json.csproj index a0f937e70812..0fae03e0db13 100644 --- a/src/System.Text.Json/src/System.Text.Json.csproj +++ b/src/System.Text.Json/src/System.Text.Json.csproj @@ -23,6 +23,11 @@ + + + + + diff --git a/src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs b/src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs new file mode 100644 index 000000000000..437a72de420b --- /dev/null +++ b/src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs @@ -0,0 +1,21 @@ +namespace System.Text.Json +{ + /// + /// Specifies how duplicate property names are handled when added to JSON object. + /// + public enum DuplicatePropertyNameHandling + { + /// + /// Replace the existing value when there is a duplicate property. The value of the last property in the JSON object will be used. + /// + Replace, + /// + /// Ignore the new value when there is a duplicate property. The value of the first property in the JSON object will be used. + /// + Ignore, + /// + /// Throw a when a duplicate property is encountered. + /// + Error + } +} 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 new file mode 100644 index 000000000000..601f377fe1aa --- /dev/null +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs @@ -0,0 +1,392 @@ +// 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 object. + /// + public sealed class JsonObject : JsonNode, IEnumerable> + { + private readonly Dictionary _dictionary; + private readonly DuplicatePropertyNameHandling _duplicatePropertyNameHandling; + + /// + /// Initializes a new instance of the class representing the empty object. + /// + /// Specifies the way of handling duplicate property names. + public JsonObject(DuplicatePropertyNameHandling duplicatePropertyNameHandling = default) + { + _dictionary = new Dictionary(); + _duplicatePropertyNameHandling = duplicatePropertyNameHandling; + } + + /// + /// Initializes a new instance of the class representing provided set of JSON properties. + /// + /// >Properties to represent as a JSON object. + /// Specifies the way of handling duplicate property names. + /// + /// Provided collection contains duplicates if handling duplicates is set to . + /// + public JsonObject(IEnumerable> jsonProperties, DuplicatePropertyNameHandling duplicatePropertyNameHandling = default) + : this(duplicatePropertyNameHandling) + => AddRange(jsonProperties); + + /// + /// Gets or sets the value of the specified property. + /// + /// The property name of the value to get or set. + public JsonNode this[string propertyName] + { + get => GetProperty(propertyName); + set => _dictionary[propertyName] = value; + } + + /// + /// 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() => _dictionary.GetEnumerator(); + + /// + /// Adds the specified property to the JSON object. + /// + /// The property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + public void Add(KeyValuePair jsonProperty) => Add(jsonProperty.Key, jsonProperty.Value); + + /// + /// Adds the specified property to the JSON object. + /// + /// Name of the property to add. + /// Value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + public void Add(string propertyName, JsonNode propertyValue) + { + if (_duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Replace) + { + _dictionary[propertyName] = propertyValue; + return; + } + + if (!_dictionary.TryAdd(propertyName, propertyValue)) + { + switch (_duplicatePropertyNameHandling) + { + case DuplicatePropertyNameHandling.Ignore: + return; + case DuplicatePropertyNameHandling.Error: + throw new ArgumentException(SR.JsonObjectDuplicateKey); + } + } + } + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + /// + /// Provided value is null. + /// + public void Add(string propertyName, string propertyValue) => _dictionary.Add(propertyName, new JsonString(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + public void Add(string propertyName, bool propertyValue) => _dictionary.Add(propertyName, new JsonBoolean(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + public void Add(string propertyName, byte propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + public void Add(string propertyName, short propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + public void Add(string propertyName, int propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + public void Add(string propertyName, long propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + /// + /// Provided value is not in a legal JSON number format. + /// + public void Add(string propertyName, float propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + /// + /// Provided value is not in a legal JSON number format. + /// + public void Add(string propertyName, double propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + [CLSCompliant(false)] + public void Add(string propertyName, sbyte propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + [CLSCompliant(false)] + public void Add(string propertyName, ushort propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + [CLSCompliant(false)] + public void Add(string propertyName, uint propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + [CLSCompliant(false)] + public void Add(string propertyName, ulong propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + public void Add(string propertyName, decimal propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + + /// + /// Adds the properties from the specified collection to the JSON object. + /// + /// Properties to add. + /// + /// Provided collection contains duplicates if handling duplicates is set to . + /// + public void AddRange(IEnumerable> jsonProperties) + { + foreach (KeyValuePair property in jsonProperties) + { + Add(property); + } + } + + /// + /// Removes the property with the specified name. + /// + /// + /// + public bool Remove(string propertyName) => _dictionary.Remove(propertyName); + + /// + /// Determines whether a property is in a JSON object. + /// + /// Name of the property to check. + /// + /// if the property is found in a JSON object, + /// otherwise. + /// + public bool ContainsProperty(string propertyName) => _dictionary.ContainsKey(propertyName); + + /// + /// Modifies the name of specified property. + /// + /// Old name of the property to change. + /// New name of the property to change. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + /// + /// Property with specified name is not found in JSON object. + /// + public void ModifyPropertyName(string oldName, string newName) + { + if (!_dictionary.ContainsKey(oldName)) + throw new KeyNotFoundException(SR.PropertyNotFound); + + JsonNode previousValue = _dictionary[oldName]; + _dictionary.Remove(oldName); + _dictionary.Add(newName, previousValue); + } + + /// + /// Returns the property with the specified name. + /// + /// Name of the property to return. + /// Value of the property with the specified name. + /// + /// Property with specified name is not found in JSON object. + /// + public JsonNode GetProperty(string propertyName) + { + if (!TryGetProperty(propertyName, out JsonNode jsonNode)) + { + throw new KeyNotFoundException(SR.PropertyNotFound); + } + + return jsonNode; + } + + /// + /// Returns the property with the specified name. + /// + /// Name of the property to return. + /// Value of the property with specified name. + /// + /// if property with specified name was found; + /// otherwise, + /// + public bool TryGetProperty(string propertyName, out JsonNode jsonNode) + { + if (!_dictionary.ContainsKey(propertyName)) + { + jsonNode = null; + return false; + } + + jsonNode = _dictionary[propertyName]; + return true; + } + + /// + /// Returns the JSON object property with the specified name. + /// + /// Name of the property to return. + /// JSON object property with the specified name. + /// + /// Property with specified name is not found in JSON object. + /// + /// + /// Property with specified name is not a JSON object. + /// + public JsonObject GetJsonObjectProperty(string propertyName) + { + if(GetProperty(propertyName) is JsonObject jsonObject) + { + return jsonObject; + } + + throw new InvalidCastException(SR.PropertyTypeMismatch); + } + + /// + /// Returns the JSON object property with the specified name. + /// + /// Name of the property to return. + /// JSON objec of the property with specified name. + /// + /// if JSON object property with specified name was found; + /// otherwise, + /// + public bool TryGetJsonObjectProperty(string propertyName, out JsonObject jsonObject) + { + if (TryGetProperty(propertyName, out JsonNode jsonNode)) + { + if (jsonNode is JsonObject jsonNodeCasted) + { + jsonObject = jsonNodeCasted; + return true; + } + } + + jsonObject = null; + return false; + } + + /// + /// A collection containing the property names of JSON object. + /// + public ICollection PropertyNames => _dictionary.Keys; + + /// + /// A collection containing the property values of JSON object. + /// + public ICollection Values => _dictionary.Values; + + /// + /// Returns an enumerator that iterates through the JSON object properties. + /// + /// An enumerator structure for the . + IEnumerator IEnumerable.GetEnumerator() => _dictionary.GetEnumerator(); + } +} diff --git a/src/System.Text.Json/tests/JsonObjectTests.TestData.cs b/src/System.Text.Json/tests/JsonObjectTests.TestData.cs new file mode 100644 index 000000000000..e32035b473be --- /dev/null +++ b/src/System.Text.Json/tests/JsonObjectTests.TestData.cs @@ -0,0 +1,122 @@ +// 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 bool CheckSSN(string ssnNumber) => true; + 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/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs new file mode 100644 index 000000000000..7ee6ffeb07fe --- /dev/null +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -0,0 +1,609 @@ +// 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 partial class JsonObjectTests + { + [Fact] + public static void TestDefaultConstructor() + { + var jsonObject = new JsonObject(); + Assert.Equal(0, jsonObject.PropertyNames.Count); + Assert.Equal(0, jsonObject.Values.Count); + } + + [Fact] + public static void TestIEnumerableKVPConstructor() + { + var jsonProperties = new List>(); + jsonProperties.Add(new KeyValuePair("number", new JsonNumber(17))); + jsonProperties.Add(new KeyValuePair("string", new JsonString("property value"))); + jsonProperties.Add(new KeyValuePair("boolean", new JsonBoolean(true))); + + var jsonObject = new JsonObject(jsonProperties); + + Assert.IsType(jsonObject["number"]); + Assert.Equal(17, (jsonObject["number"] as JsonNumber).GetInt32()); + + Assert.IsType(jsonObject["string"]); + Assert.Equal("property value", (jsonObject["string"] as JsonString).Value); + + Assert.IsType(jsonObject["boolean"]); + Assert.True((jsonObject["boolean"] as JsonBoolean).Value); + } + + private enum ExpectedValue + { + Previous, + New + } + + private static void TestDuplicates(DuplicatePropertyNameHandling duplicatePropertyNameHandling, ExpectedValue expected = default, bool valueConstructor = true) + { + string previousValue = "value1"; + string newValue = "value1"; + + JsonObject jsonObject = valueConstructor ? new JsonObject(duplicatePropertyNameHandling) : new JsonObject(); + jsonObject.Add("property", new JsonString(previousValue)); + + Assert.IsType(jsonObject["property"]); + Assert.Equal(previousValue, (jsonObject["property"] as JsonString).Value); + + jsonObject.Add("property", new JsonString(newValue)); + Assert.IsType(jsonObject["property"]); + + var expectedString = expected == ExpectedValue.Previous ? previousValue : newValue; + + Assert.Equal(expectedString, (jsonObject["property"] as JsonString).Value); + } + + [Fact] + public static void TestDuplicatesReplace() + { + TestDuplicates(DuplicatePropertyNameHandling.Replace, ExpectedValue.New); + } + + [Fact] + public static void TestDuplicatesReplaceDefault() + { + TestDuplicates(DuplicatePropertyNameHandling.Replace, ExpectedValue.New, false); + } + + + [Fact] + public static void TestDuplicatesIgnore() + { + TestDuplicates(DuplicatePropertyNameHandling.Ignore, ExpectedValue.Previous); + } + + [Fact] + public static void TestDuplicatesError() + { + Assert.Throws(() => TestDuplicates(DuplicatePropertyNameHandling.Error)); + } + + [Fact] + public static void TestNumerics() + { + var jsonObject = new JsonObject(); + + jsonObject.Add("byte", byte.MaxValue); + Assert.IsType(jsonObject["byte"]); + Assert.Equal(byte.MaxValue, ((JsonNumber)jsonObject["byte"]).GetByte()); + + jsonObject.Add("short", short.MaxValue); + Assert.IsType(jsonObject["short"]); + Assert.Equal(short.MaxValue, ((JsonNumber)jsonObject["short"]).GetInt16()); + + jsonObject.Add("int", int.MaxValue); + Assert.IsType(jsonObject["int"]); + Assert.Equal(int.MaxValue, ((JsonNumber)jsonObject["int"]).GetInt32()); + + jsonObject.Add("long", long.MaxValue); + Assert.IsType(jsonObject["long"]); + Assert.Equal(long.MaxValue, ((JsonNumber)jsonObject["long"]).GetInt64()); + + jsonObject.Add("float", 3.14f); + Assert.IsType(jsonObject["float"]); + Assert.Equal(3.14f, ((JsonNumber)jsonObject["float"]).GetSingle()); + + jsonObject.Add("double", 3.14); + Assert.IsType(jsonObject["double"]); + Assert.Equal(3.14, ((JsonNumber)jsonObject["double"]).GetDouble()); + + jsonObject.Add("sbyte", sbyte.MaxValue); + Assert.IsType(jsonObject["sbyte"]); + Assert.Equal(sbyte.MaxValue, ((JsonNumber)jsonObject["sbyte"]).GetSByte()); + + jsonObject.Add("ushort", ushort.MaxValue); + Assert.IsType(jsonObject["ushort"]); + Assert.Equal(ushort.MaxValue, ((JsonNumber)jsonObject["ushort"]).GetUInt16()); + + jsonObject.Add("uint", uint.MaxValue); + Assert.IsType(jsonObject["uint"]); + Assert.Equal(uint.MaxValue, ((JsonNumber)jsonObject["uint"]).GetUInt32()); + + jsonObject.Add("ulong", ulong.MaxValue); + Assert.IsType(jsonObject["ulong"]); + Assert.Equal(ulong.MaxValue, ((JsonNumber)jsonObject["ulong"]).GetUInt64()); + + jsonObject.Add("decimal", decimal.One); + Assert.IsType(jsonObject["decimal"]); + Assert.Equal(decimal.One, ((JsonNumber)jsonObject["decimal"]).GetDecimal()); + } + + [Fact] + public static void TestCreatingJsonObject() + { + var developer = new JsonObject + { + { "name", "Kasia" }, + { "age", 22 }, + { "is developer", true }, + { "null property", (JsonNode) null } + }; + + Assert.IsType(developer["name"]); + var developerNameCasted = developer["name"] as JsonString; + Assert.Equal("Kasia", developerNameCasted.Value); + + Assert.IsType(developer["age"]); + var developerAgeCasted = developer["age"] as JsonNumber; + Assert.Equal(22, developerAgeCasted.GetInt32()); + + Assert.IsType(developer["is developer"]); + var isDeveloperCasted = developer["is developer"] as JsonBoolean; + Assert.True(isDeveloperCasted.Value); + + Assert.Null(developer["null property"]); + } + + [Fact] + public static void TestCreatingJsonObjectNewMethods() + { + var developer = new JsonObject + { + { "name", new JsonString("Kasia") }, + { "age", new JsonNumber(22) }, + { "is developer", new JsonBoolean(true) } + }; + + Assert.IsType(developer["name"]); + var developerNameCasted = developer["name"] as JsonString; + Assert.Equal("Kasia", developerNameCasted.Value); + + Assert.IsType(developer["age"]); + var developerAgeCasted = developer["age"] as JsonNumber; + Assert.Equal(22, developerAgeCasted.GetInt32()); + + Assert.IsType(developer["is developer"]); + var isDeveloperCasted = developer["is developer"] as JsonBoolean; + Assert.True(isDeveloperCasted.Value); + } + + [Fact] + public static void TestCreatingNestedJsonObject() + { + var person = new JsonObject + { + { "name", "John" }, + { "surname", "Smith" }, + { + "phone numbers", new JsonObject() + { + { "work", "123-456-7890" }, + { "home", "123-456-7890" } + } + }, + { + "addresses", new JsonObject() + { + { + "office", new JsonObject() + { + { "address line 1", "One Microsoft Way" }, + { "city" , "Redmond" } , + { "zip code" , 98052 } , + { "state" , (int) AvailableStateCodes.WA } + } + }, + { + "home", new JsonObject() + { + { "address line 1", "Pear Ave" }, + { "address line 2", "1288" }, + { "city" , "Mountain View" } , + { "zip code" , 94043 } , + { "state" , (int) AvailableStateCodes.CA } + } + } + } + } + }; + + Assert.IsType(person["phone numbers"]); + var phoneNumbers = person["phone numbers"] as JsonObject; + Assert.IsType(phoneNumbers["work"]); + Assert.IsType(phoneNumbers["home"]); + + Assert.IsType(person["addresses"]); + var addresses = person["addresses"] as JsonObject; + Assert.IsType(addresses["office"]); + Assert.IsType(addresses["home"]); + } + + [Fact] + public static void TestAssignmentDefinition() + { + JsonNode employee = EmployeesDatabase.GetNextEmployee().Value; + Assert.IsType(employee); + } + + [Fact] + public static void TestAddingKeyValuePair() + { + var employees = new JsonObject + { + EmployeesDatabase.GetNextEmployee(), + EmployeesDatabase.GetNextEmployee(), + EmployeesDatabase.GetNextEmployee(), + EmployeesDatabase.GetNextEmployee(), + }; + + string prevId = ""; + foreach ((string id, JsonNode employee) in employees) + { + Assert.NotEqual(prevId, id); + prevId = id; + + Assert.IsType(employee); + } + } + + [Fact] + public static void TestAddingKeyValuePairAfterInitialization() + { + var employees = new JsonObject(); + foreach (KeyValuePair employee in EmployeesDatabase.GetTenBestEmployees()) + { + employees.Add(employee); + } + + string prevId = ""; + foreach ((string id, JsonNode employee) in employees) + { + Assert.NotEqual(prevId, id); + prevId = id; + + Assert.IsType(employee); + } + } + + [Fact] + public static void TestAddingKeyValuePairsCollection() + { + var employees = new JsonObject(EmployeesDatabase.GetTenBestEmployees()); + + string prevId = ""; + foreach ((string id, JsonNode employee) in employees) + { + Assert.NotEqual(prevId, id); + prevId = id; + + Assert.IsType(employee); + } + } + + [Fact] + public static void TestAddingKeyValuePairsCollectionAfterInitialization() + { + var employees = new JsonObject(); + employees.AddRange(EmployeesDatabase.GetTenBestEmployees()); + + string prevId = ""; + foreach ((string id, JsonNode employee) in employees) + { + Assert.NotEqual(prevId, id); + prevId = id; + + Assert.IsType(employee); + } + } + + [Fact] + public static void TestContains() + { + var person = new JsonObject + { + { "name", "John" }, + { "ssn", "123456789" }, + }; + + if (person.ContainsProperty("ssn")) + { + EmployeesDatabase.CheckSSN(((JsonString)person["ssn"]).Value); + } + + Assert.True(person.ContainsProperty("ssn")); + Assert.False(person.ContainsProperty("surname")); + } + + [Fact] + public static void TestAquiringAllValues() + { + var employees = new JsonObject(EmployeesDatabase.GetTenBestEmployees()); + ICollection employeesWithoutId = employees.Values; + + Assert.Equal(10, employees.PropertyNames.Count); + Assert.Equal(10, employees.Values.Count); + + foreach (JsonNode employee in employeesWithoutId) + { + Assert.IsType(employee); + } + } + + [Fact] + public static void TestReplacingsonObjectPrimaryTypes() + { + var person1 = new JsonObject + { + { "name", "John" }, + { "age", 45 }, + { "is_married", true } + }; + + // Assign by creating a new instance of primary Json type + person1["name"] = new JsonString("Bob"); + + Assert.IsType(person1["name"]); + Assert.Equal("Bob", person1["name"] as JsonString); + + // Assign by using an implicit operator on primary Json type + JsonNumber newAge = 55; + person1["age"] = newAge; + + Assert.IsType(person1["age"]); + Assert.Equal(55, person1["age"] as JsonNumber); + + // Assign by explicit cast from Json primary type + person1["is_married"] = (JsonBoolean)false; + + Assert.IsType(person1["is_married"]); + Assert.Equal(false, person1["is_married"] as JsonBoolean); + + var person2 = new JsonObject + { + { "name", "Bob" }, + { "age", 33 }, + { "is_married", true } + }; + + // Copy property from another JsonObject + person1["age"] = person2["age"]; + + Assert.IsType(person1["age"]); + Assert.Equal(33, person1["age"] as JsonNumber); + + // Copy property of different typoe + person1["name"] = person2["name"]; + } + + [Fact] + public static void TestModifyingJsonObjectKeyRemoveAdd() + { + JsonObject manager = EmployeesDatabase.GetManager(); + JsonObject reportingEmployees = manager.GetJsonObjectProperty("reporting employees"); + + static void ModifyProperty(JsonObject jsonObject, string previousName, string newName) + { + JsonNode previousValue = jsonObject[previousName]; + jsonObject.Remove(previousName); + jsonObject.Add(newName, previousValue); + } + + string previousName = "software developers"; + string newName = "software engineers"; + + Assert.True(reportingEmployees.ContainsProperty(previousName)); + JsonNode previousValue = reportingEmployees[previousName]; + + ModifyProperty(reportingEmployees, previousName, newName); + + Assert.False(reportingEmployees.ContainsProperty(previousName)); + Assert.True(reportingEmployees.ContainsProperty(newName)); + Assert.Equal(previousValue, reportingEmployees[newName]); + } + + [Fact] + public static void TestModifyingJsonObjectKeyModifyMethod() + { + JsonObject manager = EmployeesDatabase.GetManager(); + JsonObject reportingEmployees = manager.GetJsonObjectProperty("reporting employees"); + + Assert.True(reportingEmployees.ContainsProperty("software developers")); + JsonNode previousValue = reportingEmployees["software developers"]; + + reportingEmployees.ModifyPropertyName("software developers", "software engineers"); + + Assert.False(reportingEmployees.ContainsProperty("software developers")); + Assert.True(reportingEmployees.ContainsProperty("software engineers")); + Assert.Equal(previousValue, reportingEmployees["software engineers"]); + } + + [Fact] + public static void TestAccesingNestedJsonObjectCastWithAs() + { + JsonObject manager = EmployeesDatabase.GetManager(); + + // Should not throw any exceptions: + + var reportingEmployees = manager["reporting employees"] as JsonObject; + if (reportingEmployees == null) + throw new InvalidCastException(); + + var softwareDevelopers = reportingEmployees["software developers"] as JsonObject; + if (softwareDevelopers == null) + throw new InvalidCastException(); + + var internDevelopers = softwareDevelopers["intern employees"] as JsonObject; + if (internDevelopers == null) + throw new InvalidCastException(); + + internDevelopers.Add(EmployeesDatabase.GetNextEmployee()); + } + + [Fact] + public static void TestAccesingNestedJsonObjectCastWithIs() + { + JsonObject manager = EmployeesDatabase.GetManager(); + + static bool AddEmployee(JsonObject manager) + { + + if (manager["reporting employees"] is JsonObject reportingEmployees) + { + if (reportingEmployees["software developers"] is JsonObject softwareDevelopers) + { + if (softwareDevelopers["full time employees"] is JsonObject fullTimeEmployees) + { + fullTimeEmployees.Add(EmployeesDatabase.GetNextEmployee()); + return true; + } + } + } + return false; + } + + bool success = AddEmployee(manager); + Assert.True(success); + } + + [Fact] + public static void TestAccesingNestedJsonObjectExplicitCast() + { + JsonObject manager = EmployeesDatabase.GetManager(); + + // Should not throw any exceptions: + ((JsonObject)((JsonObject)manager["reporting employees"])["HR"]).Add(EmployeesDatabase.GetNextEmployee()); + } + + [Fact] + public static void TestAccesingNestedJsonObjectGetPropertyMethod() + { + JsonObject manager = EmployeesDatabase.GetManager(); + + // Should not throw any exceptions: + + JsonObject internDevelopers = manager.GetJsonObjectProperty("reporting employees") + .GetJsonObjectProperty("software developers") + .GetJsonObjectProperty("intern employees"); + internDevelopers.Add(EmployeesDatabase.GetNextEmployee()); + } + + [Fact] + public static void TestAccesingNestedJsonObjectTryGetPropertyMethod() + { + JsonObject manager = EmployeesDatabase.GetManager(); + + static bool AddEmployee(JsonObject manager) + { + if (manager.TryGetJsonObjectProperty("reporting employees", out JsonObject reportingEmployees)) + { + if (reportingEmployees.TryGetJsonObjectProperty("software developers", out JsonObject softwareDevelopers)) + { + if (softwareDevelopers.TryGetJsonObjectProperty("full time employees", out JsonObject fullTimeEmployees)) + { + fullTimeEmployees.Add(EmployeesDatabase.GetNextEmployee()); + return true; + } + } + } + + return false; + } + + bool success = AddEmployee(manager); + Assert.True(success); + } + + [Fact] + public static void TestModifyPropertyNameThrows() + { + Assert.Throws(() => new JsonObject().ModifyPropertyName("", "")); + Assert.Throws(() => + { + var jsonObject = new JsonObject() + { + { "oldName", "value" }, + { "newName", "value" } + }; + + jsonObject.ModifyPropertyName("oldName", "newName"); + }); + } + + [Fact] + public static void TestGetPropertyThrows() + { + Assert.Throws(() => + { + var jsonObject = new JsonObject() + { + { "name", "value" } + }; + + jsonObject.GetProperty("different name"); + }); + } + + [Fact] + public static void TestTryGetProperty () + { + var jsonObject = new JsonObject() + { + { "name", "value" } + }; + + Assert.True(jsonObject.TryGetProperty("name", out JsonNode property)); + Assert.IsType(property); + Assert.Equal("value", property as JsonString); + Assert.False(jsonObject.TryGetProperty("other", out property)); + Assert.Null(property); + } + + [Fact] + public static void TestGetJsonObjectPropertyThrows() + { + Assert.Throws(() => + { + var jsonObject = new JsonObject() + { + { "name", "value" } + }; + + jsonObject.GetJsonObjectProperty("name"); + }); + } + + [Fact] + public static void TestTryGetObjectPropertyFails() + { + var jsonObject = new JsonObject() + { + { "name", "value" } + }; + + Assert.False(jsonObject.TryGetJsonObjectProperty("name", out JsonObject property)); + Assert.Null(property); + + Assert.False(jsonObject.TryGetJsonObjectProperty("other", out property)); + Assert.Null(property); + } + } +} 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 5609afaffdfa..6a31350aeef0 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 @@ + @@ -112,6 +113,7 @@ + From 437b8ba197ab433168c654e808fa95654f5aa322 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Fri, 9 Aug 2019 18:20:09 -0700 Subject: [PATCH 02/15] rebase fixes --- .../src/System.Text.Json.csproj | 7 ++----- .../tests/NewtonsoftTests/ILLinkTrim.xml | 20 +++++++++++++++++++ .../tests/System.Text.Json.Tests.csproj | 15 ++++++++------ 3 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 src/System.Text.Json/tests/NewtonsoftTests/ILLinkTrim.xml diff --git a/src/System.Text.Json/src/System.Text.Json.csproj b/src/System.Text.Json/src/System.Text.Json.csproj index 0fae03e0db13..98bce1becd3d 100644 --- a/src/System.Text.Json/src/System.Text.Json.csproj +++ b/src/System.Text.Json/src/System.Text.Json.csproj @@ -23,11 +23,6 @@ - - - - - @@ -214,9 +209,11 @@ + + diff --git a/src/System.Text.Json/tests/NewtonsoftTests/ILLinkTrim.xml b/src/System.Text.Json/tests/NewtonsoftTests/ILLinkTrim.xml new file mode 100644 index 000000000000..3ba61ee53be5 --- /dev/null +++ b/src/System.Text.Json/tests/NewtonsoftTests/ILLinkTrim.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + 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 6a31350aeef0..2e3f842586c8 100644 --- a/src/System.Text.Json/tests/System.Text.Json.Tests.csproj +++ b/src/System.Text.Json/tests/System.Text.Json.Tests.csproj @@ -21,8 +21,6 @@ - - @@ -111,15 +109,20 @@ CommonTest\System\Buffers\ArrayBufferWriter.cs - - - - + + + + + + + $(AssemblyName).xml + + From 7887ad43cce2779c19e984ade8766e5ab70a5376 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Mon, 12 Aug 2019 09:50:22 -0700 Subject: [PATCH 03/15] review comments included --- src/System.Text.Json/ref/System.Text.Json.cs | 12 +- .../src/Resources/Strings.resx | 7 +- .../src/System.Text.Json.csproj | 2 +- .../Node/DuplicatePropertyNameHandling.cs | 2 +- .../src/System/Text/Json/Node/JsonObject.cs | 149 +++++++++++++----- .../src/System/Text/Json/Node/JsonString.cs | 2 +- .../tests/JsonObjectTests.TestData.cs | 2 - src/System.Text.Json/tests/JsonObjectTests.cs | 86 ++++------ .../tests/NewtonsoftTests/ILLinkTrim.xml | 20 --- .../tests/System.Text.Json.Tests.csproj | 7 +- 10 files changed, 162 insertions(+), 127 deletions(-) delete mode 100644 src/System.Text.Json/tests/NewtonsoftTests/ILLinkTrim.xml diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index b27c8de4cc1a..62ddb026cb8c 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -270,9 +270,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 key] { get { throw null; } set { } } + public System.Text.Json.JsonNode this[string propertyName] { get { throw null; } set { } } public System.Collections.Generic.ICollection PropertyNames { get { throw null; } } - public System.Collections.Generic.ICollection Values { get { throw null; } } + public System.Collections.Generic.ICollection PropertyValues { 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) { } @@ -295,13 +295,13 @@ 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.JsonObject GetJsonObjectProperty(string propertyName) { throw null; } - public System.Text.Json.JsonNode GetProperty(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 void ModifyPropertyName(string oldName, string newName) { } public bool Remove(string propertyName) { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - public bool TryGetJsonObjectProperty(string propertyName, out System.Text.Json.JsonObject jsonObject) { throw null; } - public bool TryGetProperty(string propertyName, out System.Text.Json.JsonNode jsonNode) { 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; } } public readonly partial struct JsonProperty { diff --git a/src/System.Text.Json/src/Resources/Strings.resx b/src/System.Text.Json/src/Resources/Strings.resx index 9d40e6a3435b..ada4813ce5f5 100644 --- a/src/System.Text.Json/src/Resources/Strings.resx +++ b/src/System.Text.Json/src/Resources/Strings.resx @@ -433,9 +433,12 @@ Provided property name already exists. - Property with specified name not found + Property with name {0} not found. - Property with specified name has a diffent type than expected + Property with specified name has a different type than expected + + + Provided manner of handling duplicates does not exist. \ No newline at end of file diff --git a/src/System.Text.Json/src/System.Text.Json.csproj b/src/System.Text.Json/src/System.Text.Json.csproj index 98bce1becd3d..fa2981f12a9b 100644 --- a/src/System.Text.Json/src/System.Text.Json.csproj +++ b/src/System.Text.Json/src/System.Text.Json.csproj @@ -212,7 +212,7 @@ - + diff --git a/src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs b/src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs index 437a72de420b..03152f52e2ac 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs @@ -14,7 +14,7 @@ public enum DuplicatePropertyNameHandling /// Ignore, /// - /// Throw a when a duplicate property is encountered. + /// Throw an when a duplicate property is encountered. /// Error } 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 601f377fe1aa..788b2d011cfc 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 @@ -19,8 +19,16 @@ public sealed class JsonObject : JsonNode, IEnumerable class representing the empty object. /// /// Specifies the way of handling duplicate property names. + /// + /// Provided manner of handling duplicates does not exist. + /// public JsonObject(DuplicatePropertyNameHandling duplicatePropertyNameHandling = default) { + if(!Enum.IsDefined(typeof(DuplicatePropertyNameHandling), duplicatePropertyNameHandling)) + { + throw new ArgumentException(SR.InvalidDuplicatePropertyNameHandling); + } + _dictionary = new Dictionary(); _duplicatePropertyNameHandling = duplicatePropertyNameHandling; } @@ -41,10 +49,13 @@ public JsonObject(IEnumerable> jsonProperties, Du /// Gets or sets the value of the specified property. /// /// The property name of the value to get or set. + /// + /// Provided property name is null. + /// public JsonNode this[string propertyName] { - get => GetProperty(propertyName); - set => _dictionary[propertyName] = value; + get => propertyName != null ? GetPropertyValue(propertyName) : throw new ArgumentNullException(nameof(propertyName)); + set => _dictionary[propertyName] = value ?? throw new ArgumentNullException(nameof(propertyName)); } /// @@ -70,11 +81,22 @@ public JsonNode this[string propertyName] /// /// Name of the property to add. /// Value of the property to add. + /// + /// Provided property name is null. + /// /// /// Property name to set already exists if handling duplicates is set to . /// + /// + /// Null property value is allowed and represents a null JSON node. + /// public void Add(string propertyName, JsonNode propertyValue) { + if(propertyName == null) + { + throw new ArgumentNullException(nameof(propertyName)); + } + if (_duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Replace) { _dictionary[propertyName] = propertyValue; @@ -102,9 +124,9 @@ public void Add(string propertyName, JsonNode propertyValue) /// Property name to set already exists if handling duplicates is set to . /// /// - /// Provided value is null. + /// Provided value or property name is null. /// - public void Add(string propertyName, string propertyValue) => _dictionary.Add(propertyName, new JsonString(propertyValue)); + public void Add(string propertyName, string propertyValue) => Add(propertyName, new JsonString(propertyValue)); /// /// Adds the specified property as a to the JSON object. @@ -114,7 +136,10 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Property name to set already exists if handling duplicates is set to . /// - public void Add(string propertyName, bool propertyValue) => _dictionary.Add(propertyName, new JsonBoolean(propertyValue)); + /// + /// Provided property name is null. + /// + public void Add(string propertyName, bool propertyValue) => Add(propertyName, new JsonBoolean(propertyValue)); /// /// Adds the specified property as a to the JSON object. @@ -124,7 +149,10 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Property name to set already exists if handling duplicates is set to . /// - public void Add(string propertyName, byte propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + /// + /// Provided property name is null. + /// + public void Add(string propertyName, byte propertyValue) => Add(propertyName, new JsonNumber(propertyValue)); /// /// Adds the specified property as a to the JSON object. @@ -134,7 +162,10 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Property name to set already exists if handling duplicates is set to . /// - public void Add(string propertyName, short propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + /// + /// Provided property name is null. + /// + public void Add(string propertyName, short propertyValue) => Add(propertyName, new JsonNumber(propertyValue)); /// /// Adds the specified property as a to the JSON object. @@ -144,7 +175,10 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Property name to set already exists if handling duplicates is set to . /// - public void Add(string propertyName, int propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + /// + /// Provided property name is null. + /// + public void Add(string propertyName, int propertyValue) => Add(propertyName, new JsonNumber(propertyValue)); /// /// Adds the specified property as a to the JSON object. @@ -154,7 +188,10 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Property name to set already exists if handling duplicates is set to . /// - public void Add(string propertyName, long propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + /// + /// Provided property name is null. + /// + public void Add(string propertyName, long propertyValue) => Add(propertyName, new JsonNumber(propertyValue)); /// /// Adds the specified property as a to the JSON object. @@ -167,7 +204,10 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Provided value is not in a legal JSON number format. /// - public void Add(string propertyName, float propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + /// + /// Provided property name is null. + /// + public void Add(string propertyName, float propertyValue) => Add(propertyName, new JsonNumber(propertyValue)); /// /// Adds the specified property as a to the JSON object. @@ -180,7 +220,10 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Provided value is not in a legal JSON number format. /// - public void Add(string propertyName, double propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + /// + /// Provided property name is null. + /// + public void Add(string propertyName, double propertyValue) => Add(propertyName, new JsonNumber(propertyValue)); /// /// Adds the specified property as a to the JSON object. @@ -190,8 +233,11 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Property name to set already exists if handling duplicates is set to . /// + /// + /// Provided property name is null. + /// [CLSCompliant(false)] - public void Add(string propertyName, sbyte propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + public void Add(string propertyName, sbyte propertyValue) => Add(propertyName, new JsonNumber(propertyValue)); /// /// Adds the specified property as a to the JSON object. @@ -201,8 +247,11 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Property name to set already exists if handling duplicates is set to . /// + /// + /// Provided property name is null. + /// [CLSCompliant(false)] - public void Add(string propertyName, ushort propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + public void Add(string propertyName, ushort propertyValue) => Add(propertyName, new JsonNumber(propertyValue)); /// /// Adds the specified property as a to the JSON object. @@ -212,8 +261,11 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Property name to set already exists if handling duplicates is set to . /// + /// + /// Provided property name is null. + /// [CLSCompliant(false)] - public void Add(string propertyName, uint propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + public void Add(string propertyName, uint propertyValue) => Add(propertyName, new JsonNumber(propertyValue)); /// /// Adds the specified property as a to the JSON object. @@ -223,8 +275,11 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Property name to set already exists if handling duplicates is set to . /// + /// + /// Provided property name is null. + /// [CLSCompliant(false)] - public void Add(string propertyName, ulong propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + public void Add(string propertyName, ulong propertyValue) => Add(propertyName, new JsonNumber(propertyValue)); /// /// Adds the specified property as a to the JSON object. @@ -234,7 +289,10 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Property name to set already exists if handling duplicates is set to . /// - public void Add(string propertyName, decimal propertyValue) => _dictionary.Add(propertyName, new JsonNumber(propertyValue)); + /// + /// Provided property name is null. + /// + public void Add(string propertyName, decimal propertyValue) => Add(propertyName, new JsonNumber(propertyValue)); /// /// Adds the properties from the specified collection to the JSON object. @@ -243,6 +301,9 @@ public void Add(string propertyName, JsonNode propertyValue) /// /// Provided collection contains duplicates if handling duplicates is set to . /// + /// + /// Some of property names are null. + /// public void AddRange(IEnumerable> jsonProperties) { foreach (KeyValuePair property in jsonProperties) @@ -255,18 +316,27 @@ public void AddRange(IEnumerable> jsonProperties) /// Removes the property with the specified name. /// /// - /// - public bool Remove(string propertyName) => _dictionary.Remove(propertyName); + /// + /// if the property is successfully found in a JSON object and removed, + /// otherwise. + /// + /// + /// Provided property name is null. + /// + public bool Remove(string propertyName) => propertyName != null ? _dictionary.Remove(propertyName) : throw new ArgumentNullException(nameof(propertyName)); /// /// Determines whether a property is in a JSON object. /// /// Name of the property to check. /// - /// if the property is found in a JSON object, + /// if the property is successfully found in a JSON object, /// otherwise. /// - public bool ContainsProperty(string propertyName) => _dictionary.ContainsKey(propertyName); + /// + /// Provided property name is null. + /// + public bool ContainsProperty(string propertyName) => propertyName != null ? _dictionary.ContainsKey(propertyName) : throw new ArgumentNullException(nameof(propertyName)); /// /// Modifies the name of specified property. @@ -279,10 +349,13 @@ public void AddRange(IEnumerable> jsonProperties) /// /// Property with specified name is not found in JSON object. /// + /// + /// Does not guarantee keeping the order the same. + /// public void ModifyPropertyName(string oldName, string newName) { if (!_dictionary.ContainsKey(oldName)) - throw new KeyNotFoundException(SR.PropertyNotFound); + throw new KeyNotFoundException(string.Format(SR.PropertyNotFound, oldName)); JsonNode previousValue = _dictionary[oldName]; _dictionary.Remove(oldName); @@ -290,25 +363,25 @@ public void ModifyPropertyName(string oldName, string newName) } /// - /// Returns the property with the specified name. + /// Returns the value of a property with the specified name. /// /// Name of the property to return. /// Value of the property with the specified name. /// /// Property with specified name is not found in JSON object. /// - public JsonNode GetProperty(string propertyName) + public JsonNode GetPropertyValue(string propertyName) { - if (!TryGetProperty(propertyName, out JsonNode jsonNode)) + if (!TryGetPropertyValue(propertyName, out JsonNode jsonNode)) { - throw new KeyNotFoundException(SR.PropertyNotFound); + throw new KeyNotFoundException(string.Format(SR.PropertyNotFound, propertyName)); } return jsonNode; } /// - /// Returns the property with the specified name. + /// Returns the value of a property with the specified name. /// /// Name of the property to return. /// Value of the property with specified name. @@ -316,7 +389,11 @@ public JsonNode GetProperty(string propertyName) /// if property with specified name was found; /// otherwise, /// - public bool TryGetProperty(string propertyName, out JsonNode jsonNode) + /// + /// When returns , the value of is meaningless. + /// Null doesn't mean the property value was "null" unless is returned. + /// + public bool TryGetPropertyValue(string propertyName, out JsonNode jsonNode) { if (!_dictionary.ContainsKey(propertyName)) { @@ -329,19 +406,19 @@ public bool TryGetProperty(string propertyName, out JsonNode jsonNode) } /// - /// Returns the JSON object property with the specified name. + /// Returns the JSON object value of a property with the specified name. /// /// Name of the property to return. - /// JSON object property with the specified name. + /// JSON objectvalue of a property with the specified name. /// /// Property with specified name is not found in JSON object. /// /// /// Property with specified name is not a JSON object. /// - public JsonObject GetJsonObjectProperty(string propertyName) + public JsonObject GetJsonObjectPropertyValue(string propertyName) { - if(GetProperty(propertyName) is JsonObject jsonObject) + if (GetPropertyValue(propertyName) is JsonObject jsonObject) { return jsonObject; } @@ -350,17 +427,17 @@ public JsonObject GetJsonObjectProperty(string propertyName) } /// - /// Returns the JSON object property with the specified name. + /// Returns the JSON object value of a property with the specified name. /// /// Name of the property to return. - /// JSON objec of the property with specified name. + /// JSON object value of the property with specified name. /// /// if JSON object property with specified name was found; /// otherwise, /// - public bool TryGetJsonObjectProperty(string propertyName, out JsonObject jsonObject) + public bool TryGetJsonObjectPropertyValue(string propertyName, out JsonObject jsonObject) { - if (TryGetProperty(propertyName, out JsonNode jsonNode)) + if (TryGetPropertyValue(propertyName, out JsonNode jsonNode)) { if (jsonNode is JsonObject jsonNodeCasted) { @@ -381,7 +458,7 @@ public bool TryGetJsonObjectProperty(string propertyName, out JsonObject jsonObj /// /// A collection containing the property values of JSON object. /// - public ICollection Values => _dictionary.Values; + public ICollection PropertyValues => _dictionary.Values; /// /// Returns an enumerator that iterates through the JSON object properties. 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 601272d43164..2454bf07f674 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 @@ -40,7 +40,7 @@ public sealed class JsonString : JsonNode, IEquatable public string Value { get => _value; - set => _value = value ?? throw new ArgumentNullException(); + set => _value = value ?? throw new ArgumentNullException(nameof(value)); } /// diff --git a/src/System.Text.Json/tests/JsonObjectTests.TestData.cs b/src/System.Text.Json/tests/JsonObjectTests.TestData.cs index e32035b473be..fb324ed49400 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.TestData.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.TestData.cs @@ -99,8 +99,6 @@ public static JsonObject GetManager() return manager; } - - public static bool CheckSSN(string ssnNumber) => true; public static void PerformHeavyOperations(JsonElement employee) { } } diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index 7ee6ffeb07fe..47c79f5b635d 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -14,7 +14,7 @@ public static void TestDefaultConstructor() { var jsonObject = new JsonObject(); Assert.Equal(0, jsonObject.PropertyNames.Count); - Assert.Equal(0, jsonObject.Values.Count); + Assert.Equal(0, jsonObject.PropertyValues.Count); } [Fact] @@ -37,7 +37,7 @@ public static void TestIEnumerableKVPConstructor() Assert.True((jsonObject["boolean"] as JsonBoolean).Value); } - private enum ExpectedValue + public enum ExpectedValue { Previous, New @@ -57,28 +57,19 @@ private static void TestDuplicates(DuplicatePropertyNameHandling duplicateProper jsonObject.Add("property", new JsonString(newValue)); Assert.IsType(jsonObject["property"]); - var expectedString = expected == ExpectedValue.Previous ? previousValue : newValue; + string expectedString = expected == ExpectedValue.Previous ? previousValue : newValue; Assert.Equal(expectedString, (jsonObject["property"] as JsonString).Value); } - [Fact] - public static void TestDuplicatesReplace() - { - TestDuplicates(DuplicatePropertyNameHandling.Replace, ExpectedValue.New); - } - - [Fact] - public static void TestDuplicatesReplaceDefault() - { - TestDuplicates(DuplicatePropertyNameHandling.Replace, ExpectedValue.New, false); - } - - [Fact] - public static void TestDuplicatesIgnore() + [Theory] + [InlineData(DuplicatePropertyNameHandling.Replace, ExpectedValue.New)] + [InlineData(DuplicatePropertyNameHandling.Replace, ExpectedValue.New, false)] + [InlineData(DuplicatePropertyNameHandling.Ignore, ExpectedValue.Previous)] + public static void TestDuplicatesReplaceAndIgnore(DuplicatePropertyNameHandling duplicatePropertyNameHandling, ExpectedValue expected = default, bool valueConstructor = true) { - TestDuplicates(DuplicatePropertyNameHandling.Ignore, ExpectedValue.Previous); + TestDuplicates(duplicatePropertyNameHandling, expected, valueConstructor); } [Fact] @@ -324,12 +315,8 @@ public static void TestContains() { "ssn", "123456789" }, }; - if (person.ContainsProperty("ssn")) - { - EmployeesDatabase.CheckSSN(((JsonString)person["ssn"]).Value); - } - Assert.True(person.ContainsProperty("ssn")); + Assert.Equal("123456789", (JsonString)person["ssn"]); Assert.False(person.ContainsProperty("surname")); } @@ -337,10 +324,10 @@ public static void TestContains() public static void TestAquiringAllValues() { var employees = new JsonObject(EmployeesDatabase.GetTenBestEmployees()); - ICollection employeesWithoutId = employees.Values; + ICollection employeesWithoutId = employees.PropertyValues; Assert.Equal(10, employees.PropertyNames.Count); - Assert.Equal(10, employees.Values.Count); + Assert.Equal(10, employees.PropertyValues.Count); foreach (JsonNode employee in employeesWithoutId) { @@ -398,7 +385,7 @@ public static void TestReplacingsonObjectPrimaryTypes() public static void TestModifyingJsonObjectKeyRemoveAdd() { JsonObject manager = EmployeesDatabase.GetManager(); - JsonObject reportingEmployees = manager.GetJsonObjectProperty("reporting employees"); + JsonObject reportingEmployees = manager.GetJsonObjectPropertyValue("reporting employees"); static void ModifyProperty(JsonObject jsonObject, string previousName, string newName) { @@ -424,7 +411,7 @@ static void ModifyProperty(JsonObject jsonObject, string previousName, string ne public static void TestModifyingJsonObjectKeyModifyMethod() { JsonObject manager = EmployeesDatabase.GetManager(); - JsonObject reportingEmployees = manager.GetJsonObjectProperty("reporting employees"); + JsonObject reportingEmployees = manager.GetJsonObjectPropertyValue("reporting employees"); Assert.True(reportingEmployees.ContainsProperty("software developers")); JsonNode previousValue = reportingEmployees["software developers"]; @@ -437,29 +424,24 @@ public static void TestModifyingJsonObjectKeyModifyMethod() } [Fact] - public static void TestAccesingNestedJsonObjectCastWithAs() + public static void TestAccessingNestedJsonObjectCastWithAs() { JsonObject manager = EmployeesDatabase.GetManager(); - // Should not throw any exceptions: - var reportingEmployees = manager["reporting employees"] as JsonObject; - if (reportingEmployees == null) - throw new InvalidCastException(); + Assert.NotNull(reportingEmployees); var softwareDevelopers = reportingEmployees["software developers"] as JsonObject; - if (softwareDevelopers == null) - throw new InvalidCastException(); + Assert.NotNull(softwareDevelopers); var internDevelopers = softwareDevelopers["intern employees"] as JsonObject; - if (internDevelopers == null) - throw new InvalidCastException(); + Assert.NotNull(internDevelopers); internDevelopers.Add(EmployeesDatabase.GetNextEmployee()); } [Fact] - public static void TestAccesingNestedJsonObjectCastWithIs() + public static void TestAccessingNestedJsonObjectCastWithIs() { JsonObject manager = EmployeesDatabase.GetManager(); @@ -485,7 +467,7 @@ static bool AddEmployee(JsonObject manager) } [Fact] - public static void TestAccesingNestedJsonObjectExplicitCast() + public static void TestAccessingNestedJsonObjectExplicitCast() { JsonObject manager = EmployeesDatabase.GetManager(); @@ -494,30 +476,30 @@ public static void TestAccesingNestedJsonObjectExplicitCast() } [Fact] - public static void TestAccesingNestedJsonObjectGetPropertyMethod() + public static void TestAccessingNestedJsonObjectGetPropertyMethod() { JsonObject manager = EmployeesDatabase.GetManager(); // Should not throw any exceptions: - JsonObject internDevelopers = manager.GetJsonObjectProperty("reporting employees") - .GetJsonObjectProperty("software developers") - .GetJsonObjectProperty("intern employees"); + JsonObject internDevelopers = manager.GetJsonObjectPropertyValue("reporting employees") + .GetJsonObjectPropertyValue("software developers") + .GetJsonObjectPropertyValue("intern employees"); internDevelopers.Add(EmployeesDatabase.GetNextEmployee()); } [Fact] - public static void TestAccesingNestedJsonObjectTryGetPropertyMethod() + public static void TestAccessingNestedJsonObjectTryGetPropertyMethod() { JsonObject manager = EmployeesDatabase.GetManager(); static bool AddEmployee(JsonObject manager) { - if (manager.TryGetJsonObjectProperty("reporting employees", out JsonObject reportingEmployees)) + if (manager.TryGetJsonObjectPropertyValue("reporting employees", out JsonObject reportingEmployees)) { - if (reportingEmployees.TryGetJsonObjectProperty("software developers", out JsonObject softwareDevelopers)) + if (reportingEmployees.TryGetJsonObjectPropertyValue("software developers", out JsonObject softwareDevelopers)) { - if (softwareDevelopers.TryGetJsonObjectProperty("full time employees", out JsonObject fullTimeEmployees)) + if (softwareDevelopers.TryGetJsonObjectPropertyValue("full time employees", out JsonObject fullTimeEmployees)) { fullTimeEmployees.Add(EmployeesDatabase.GetNextEmployee()); return true; @@ -558,7 +540,7 @@ public static void TestGetPropertyThrows() { "name", "value" } }; - jsonObject.GetProperty("different name"); + jsonObject.GetPropertyValue("different name"); }); } @@ -570,10 +552,10 @@ public static void TestTryGetProperty () { "name", "value" } }; - Assert.True(jsonObject.TryGetProperty("name", out JsonNode property)); + Assert.True(jsonObject.TryGetPropertyValue("name", out JsonNode property)); Assert.IsType(property); Assert.Equal("value", property as JsonString); - Assert.False(jsonObject.TryGetProperty("other", out property)); + Assert.False(jsonObject.TryGetPropertyValue("other", out property)); Assert.Null(property); } @@ -587,7 +569,7 @@ public static void TestGetJsonObjectPropertyThrows() { "name", "value" } }; - jsonObject.GetJsonObjectProperty("name"); + jsonObject.GetJsonObjectPropertyValue("name"); }); } @@ -599,10 +581,10 @@ public static void TestTryGetObjectPropertyFails() { "name", "value" } }; - Assert.False(jsonObject.TryGetJsonObjectProperty("name", out JsonObject property)); + Assert.False(jsonObject.TryGetJsonObjectPropertyValue("name", out JsonObject property)); Assert.Null(property); - Assert.False(jsonObject.TryGetJsonObjectProperty("other", out property)); + Assert.False(jsonObject.TryGetJsonObjectPropertyValue("other", out property)); Assert.Null(property); } } diff --git a/src/System.Text.Json/tests/NewtonsoftTests/ILLinkTrim.xml b/src/System.Text.Json/tests/NewtonsoftTests/ILLinkTrim.xml deleted file mode 100644 index 3ba61ee53be5..000000000000 --- a/src/System.Text.Json/tests/NewtonsoftTests/ILLinkTrim.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - 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 2e3f842586c8..3f6fe6d737c3 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,7 @@ + @@ -115,14 +116,8 @@ - - - - $(AssemblyName).xml - - From 0cd91e12f6d6f7240a51886a97547dcf4700b324 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Mon, 12 Aug 2019 10:36:48 -0700 Subject: [PATCH 04/15] more tests added --- src/System.Text.Json/tests/JsonObjectTests.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index 47c79f5b635d..d426bb8e2475 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -60,6 +60,10 @@ private static void TestDuplicates(DuplicatePropertyNameHandling duplicateProper string expectedString = expected == ExpectedValue.Previous ? previousValue : newValue; Assert.Equal(expectedString, (jsonObject["property"] as JsonString).Value); + + // with indexer, property should change no matter which duplicates handling option is chosen: + jsonObject["property"] = (JsonString)"indexer value"; + Assert.Equal("indexer value", (JsonString)jsonObject["property"]); } @@ -76,6 +80,10 @@ public static void TestDuplicatesReplaceAndIgnore(DuplicatePropertyNameHandling public static void TestDuplicatesError() { Assert.Throws(() => TestDuplicates(DuplicatePropertyNameHandling.Error)); + + JsonObject jsonObject = new JsonObject(DuplicatePropertyNameHandling.Error) { { "property", "" } }; + jsonObject["property"] = (JsonString) "indexer value"; + Assert.Equal("indexer value", (JsonString) jsonObject["property"]); } [Fact] @@ -587,5 +595,43 @@ public static void TestTryGetObjectPropertyFails() Assert.False(jsonObject.TryGetJsonObjectPropertyValue("other", out property)); Assert.Null(property); } + + [Fact] + public static void TestArgumentNullValidation() + { + Assert.Throws(() => new JsonObject().Add(null, "")); + Assert.Throws(() => new JsonObject().Add(null, new JsonObject())); + Assert.Throws(() => new JsonObject().Add(null, (byte)17)); + Assert.Throws(() => new JsonObject().Add(null, (short)17)); + Assert.Throws(() => new JsonObject().Add(null, 17)); + Assert.Throws(() => new JsonObject().Add(null, (long)17)); + Assert.Throws(() => new JsonObject().Add(null, 3.14f)); + Assert.Throws(() => new JsonObject().Add(null, 3.14)); + Assert.Throws(() => new JsonObject().Add(null, decimal.One)); + Assert.Throws(() => new JsonObject().Add(null, (sbyte)17)); + Assert.Throws(() => new JsonObject().Add(null, (ushort)17)); + Assert.Throws(() => new JsonObject().Add(null, (uint)17)); + Assert.Throws(() => new JsonObject().Add(null, (ulong)17)); + Assert.Throws(() => new JsonObject().Add(new KeyValuePair(null, new JsonObject()))); + Assert.Throws(() => + { + var property = new KeyValuePair(null, new JsonObject()); + new JsonObject().AddRange(new List>() { property }); + }); + Assert.Throws(() => new JsonObject()[null] = new JsonString()); + Assert.Throws(() => + { + var jsonObject = new JsonObject(); + JsonNode x = jsonObject[null]; + }); + Assert.Throws(() => new JsonObject().Remove(null)); + Assert.Throws(() => new JsonObject().ContainsProperty(null)); + } + + [Fact] + public static void TestDuplicatesEnumOutOfRange() + { + Assert.Throws(() => new JsonObject((DuplicatePropertyNameHandling)123)); + } } } From 17244e9c303fafe5c12a2792f3bc89c5b0d0d2e0 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Mon, 12 Aug 2019 10:39:57 -0700 Subject: [PATCH 05/15] failing on netfx TryAdd call removed --- .../src/System/Text/Json/Node/JsonObject.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) 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 788b2d011cfc..924261372f1f 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 @@ -97,13 +97,7 @@ public void Add(string propertyName, JsonNode propertyValue) throw new ArgumentNullException(nameof(propertyName)); } - if (_duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Replace) - { - _dictionary[propertyName] = propertyValue; - return; - } - - if (!_dictionary.TryAdd(propertyName, propertyValue)) + if (_dictionary.ContainsKey(propertyName)) { switch (_duplicatePropertyNameHandling) { @@ -113,6 +107,8 @@ public void Add(string propertyName, JsonNode propertyValue) throw new ArgumentException(SR.JsonObjectDuplicateKey); } } + + _dictionary[propertyName] = propertyValue; } /// From 292a28ba3205205f633807abecde2043da6cedef Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Mon, 12 Aug 2019 10:46:13 -0700 Subject: [PATCH 06/15] specification changes --- src/System.Text.Json/docs/writable_json_dom_spec.md | 2 ++ 1 file changed, 2 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 927119eaba28..48f09e69d49f 100644 --- a/src/System.Text.Json/docs/writable_json_dom_spec.md +++ b/src/System.Text.Json/docs/writable_json_dom_spec.md @@ -260,6 +260,8 @@ Mailbox.SendAllEmployeesData(employees.AsJsonElement()); * Do we want to support creating `JsonNumber` from `BigInterger` without changing it to string? * 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? ## Useful links From 86bfe7765d75e33add1e5dd9621b8085d09b5264 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Mon, 12 Aug 2019 10:51:53 -0700 Subject: [PATCH 07/15] specification changes --- src/System.Text.Json/docs/writable_json_dom_spec.md | 1 + 1 file changed, 1 insertion(+) 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 48f09e69d49f..8a3288a3456c 100644 --- a/src/System.Text.Json/docs/writable_json_dom_spec.md +++ b/src/System.Text.Json/docs/writable_json_dom_spec.md @@ -242,6 +242,7 @@ Mailbox.SendAllEmployeesData(employees.AsJsonElement()); ## Open questions * 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)? * 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`? From 5faf9a9ea72d437c6affc519fe753db8e6f93d01 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Mon, 12 Aug 2019 11:19:17 -0700 Subject: [PATCH 08/15] netfx kvp deconstruction fixes --- src/System.Text.Json/tests/JsonObjectTests.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index d426bb8e2475..f08d0f71562f 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -255,12 +255,12 @@ public static void TestAddingKeyValuePair() }; string prevId = ""; - foreach ((string id, JsonNode employee) in employees) + foreach (KeyValuePair employee in employees) { - Assert.NotEqual(prevId, id); - prevId = id; + Assert.NotEqual(prevId, employee.Key); + prevId = employee.Key; - Assert.IsType(employee); + Assert.IsType(employee.Value); } } @@ -274,12 +274,12 @@ public static void TestAddingKeyValuePairAfterInitialization() } string prevId = ""; - foreach ((string id, JsonNode employee) in employees) + foreach (KeyValuePair employee in employees) { - Assert.NotEqual(prevId, id); - prevId = id; + Assert.NotEqual(prevId, employee.Key); + prevId = employee.Key; - Assert.IsType(employee); + Assert.IsType(employee.Value); } } @@ -289,12 +289,12 @@ public static void TestAddingKeyValuePairsCollection() var employees = new JsonObject(EmployeesDatabase.GetTenBestEmployees()); string prevId = ""; - foreach ((string id, JsonNode employee) in employees) + foreach (KeyValuePair employee in employees) { - Assert.NotEqual(prevId, id); - prevId = id; + Assert.NotEqual(prevId, employee.Key); + prevId = employee.Key; - Assert.IsType(employee); + Assert.IsType(employee.Value); } } @@ -305,12 +305,12 @@ public static void TestAddingKeyValuePairsCollectionAfterInitialization() employees.AddRange(EmployeesDatabase.GetTenBestEmployees()); string prevId = ""; - foreach ((string id, JsonNode employee) in employees) + foreach (KeyValuePair employee in employees) { - Assert.NotEqual(prevId, id); - prevId = id; + Assert.NotEqual(prevId, employee.Key); + prevId = employee.Key; - Assert.IsType(employee); + Assert.IsType(employee.Value); } } From 255851fa7fe3c36c2ecb75b648e680c9813a962f Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Mon, 12 Aug 2019 11:24:29 -0700 Subject: [PATCH 09/15] code refactor --- src/System.Text.Json/tests/JsonObjectTests.cs | 48 +++++++------------ 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index f08d0f71562f..68c84ccda223 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -243,6 +243,18 @@ public static void TestAssignmentDefinition() Assert.IsType(employee); } + private static void CheckEmployeesAreDifferent(JsonObject employees) + { + string prevId = ""; + foreach (KeyValuePair employee in employees) + { + Assert.NotEqual(prevId, employee.Key); + prevId = employee.Key; + + Assert.IsType(employee.Value); + } + } + [Fact] public static void TestAddingKeyValuePair() { @@ -254,14 +266,7 @@ public static void TestAddingKeyValuePair() EmployeesDatabase.GetNextEmployee(), }; - string prevId = ""; - foreach (KeyValuePair employee in employees) - { - Assert.NotEqual(prevId, employee.Key); - prevId = employee.Key; - - Assert.IsType(employee.Value); - } + CheckEmployeesAreDifferent(employees); } [Fact] @@ -273,14 +278,7 @@ public static void TestAddingKeyValuePairAfterInitialization() employees.Add(employee); } - string prevId = ""; - foreach (KeyValuePair employee in employees) - { - Assert.NotEqual(prevId, employee.Key); - prevId = employee.Key; - - Assert.IsType(employee.Value); - } + CheckEmployeesAreDifferent(employees); } [Fact] @@ -288,14 +286,7 @@ public static void TestAddingKeyValuePairsCollection() { var employees = new JsonObject(EmployeesDatabase.GetTenBestEmployees()); - string prevId = ""; - foreach (KeyValuePair employee in employees) - { - Assert.NotEqual(prevId, employee.Key); - prevId = employee.Key; - - Assert.IsType(employee.Value); - } + CheckEmployeesAreDifferent(employees); } [Fact] @@ -304,14 +295,7 @@ public static void TestAddingKeyValuePairsCollectionAfterInitialization() var employees = new JsonObject(); employees.AddRange(EmployeesDatabase.GetTenBestEmployees()); - string prevId = ""; - foreach (KeyValuePair employee in employees) - { - Assert.NotEqual(prevId, employee.Key); - prevId = employee.Key; - - Assert.IsType(employee.Value); - } + CheckEmployeesAreDifferent(employees); } [Fact] From 42b50f532c96d2f5ddc0b7db381740553ac8b9b4 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 13 Aug 2019 09:57:51 -0700 Subject: [PATCH 10/15] work on review comments --- src/System.Text.Json/ref/System.Text.Json.cs | 3 ++ .../src/Resources/Strings.resx | 8 ++-- .../src/System.Text.Json.csproj | 1 + .../Node/DuplicatePropertyNameHandling.cs | 2 +- .../src/System/Text/Json/Node/JsonBoolean.cs | 2 +- .../src/System/Text/Json/Node/JsonNode.cs | 2 +- .../src/System/Text/Json/Node/JsonNumber.cs | 2 +- .../src/System/Text/Json/Node/JsonObject.cs | 32 ++++++++----- .../Text/Json/Node/JsonObjectEnumerator.cs | 45 +++++++++++++++++++ .../src/System/Text/Json/Node/JsonString.cs | 20 ++++++++- src/System.Text.Json/tests/JsonObjectTests.cs | 20 +++++++-- src/System.Text.Json/tests/JsonStringTests.cs | 27 ++++++++++- 12 files changed, 139 insertions(+), 25 deletions(-) create mode 100644 src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index 62ddb026cb8c..88ac047b50b6 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -367,6 +367,9 @@ public JsonSerializerOptions() { } public sealed partial class JsonString : System.Text.Json.JsonNode, System.IEquatable { public JsonString() { } + public JsonString(System.DateTime value) { } + public JsonString(System.DateTimeOffset value) { } + public JsonString(System.Guid value) { } public JsonString(System.ReadOnlySpan value) { } public JsonString(string value) { } public string Value { get { throw null; } set { } } diff --git a/src/System.Text.Json/src/Resources/Strings.resx b/src/System.Text.Json/src/Resources/Strings.resx index ada4813ce5f5..c44104175bc2 100644 --- a/src/System.Text.Json/src/Resources/Strings.resx +++ b/src/System.Text.Json/src/Resources/Strings.resx @@ -430,15 +430,15 @@ Expected a number, but instead got empty string. - Provided property name already exists. + Property with name '{0}' already exists. - Property with name {0} not found. + Property with name '{0}' not found. - Property with specified name has a different type than expected + Property with name '{0}' has a different type than expected. - Provided manner of handling duplicates does not exist. + The DuplicatePropertyNameHandling enum must be set to one of the supported values. \ No newline at end of file diff --git a/src/System.Text.Json/src/System.Text.Json.csproj b/src/System.Text.Json/src/System.Text.Json.csproj index fa2981f12a9b..11ffc0b67fc8 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/DuplicatePropertyNameHandling.cs b/src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs index 03152f52e2ac..df4840318ba0 100644 --- a/src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs +++ b/src/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs @@ -16,6 +16,6 @@ public enum DuplicatePropertyNameHandling /// /// Throw an when a duplicate property is encountered. /// - Error + Error, } } 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 a1af8734e0cb..985ed4843def 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 @@ -5,7 +5,7 @@ namespace System.Text.Json { /// - /// Represents a boolean JSON value. + /// Represents a mutable boolean JSON value. /// public sealed class JsonBoolean : JsonNode, IEquatable { 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 8b40fec4b3c8..ec16a6642c76 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 @@ -5,7 +5,7 @@ namespace System.Text.Json { /// - /// The base class that represents a single node within a JSON document. + /// The base class that represents a single node within a mutable JSON document. /// public abstract class JsonNode { 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 57b4c754bcfc..a4c639d6da35 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 @@ -7,7 +7,7 @@ namespace System.Text.Json { /// - /// Represents a numeric JSON value. + /// Represents a mutable numeric JSON value. /// public sealed class JsonNumber : JsonNode, IEquatable { 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 924261372f1f..083f9605043e 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 @@ -8,11 +8,11 @@ namespace System.Text.Json { /// - /// Represents a JSON object. + /// Represents a mutable JSON object. /// public sealed class JsonObject : JsonNode, IEnumerable> { - private readonly Dictionary _dictionary; + internal readonly Dictionary _dictionary; private readonly DuplicatePropertyNameHandling _duplicatePropertyNameHandling; /// @@ -22,11 +22,11 @@ public sealed class JsonObject : JsonNode, IEnumerable /// Provided manner of handling duplicates does not exist. /// - public JsonObject(DuplicatePropertyNameHandling duplicatePropertyNameHandling = default) + public JsonObject(DuplicatePropertyNameHandling duplicatePropertyNameHandling = DuplicatePropertyNameHandling.Replace) { if(!Enum.IsDefined(typeof(DuplicatePropertyNameHandling), duplicatePropertyNameHandling)) { - throw new ArgumentException(SR.InvalidDuplicatePropertyNameHandling); + throw new ArgumentOutOfRangeException(SR.InvalidDuplicatePropertyNameHandling); } _dictionary = new Dictionary(); @@ -41,7 +41,9 @@ public JsonObject(DuplicatePropertyNameHandling duplicatePropertyNameHandling = /// /// Provided collection contains duplicates if handling duplicates is set to . /// - public JsonObject(IEnumerable> jsonProperties, DuplicatePropertyNameHandling duplicatePropertyNameHandling = default) + public JsonObject( + IEnumerable> jsonProperties, + DuplicatePropertyNameHandling duplicatePropertyNameHandling = DuplicatePropertyNameHandling.Replace) : this(duplicatePropertyNameHandling) => AddRange(jsonProperties); @@ -55,7 +57,13 @@ public JsonObject(IEnumerable> jsonProperties, Du public JsonNode this[string propertyName] { get => propertyName != null ? GetPropertyValue(propertyName) : throw new ArgumentNullException(nameof(propertyName)); - set => _dictionary[propertyName] = value ?? throw new ArgumentNullException(nameof(propertyName)); + set + { + if (propertyName == null) + throw new ArgumentNullException(nameof(propertyName)); + + _dictionary[propertyName] = value; + } } /// @@ -65,7 +73,7 @@ public JsonNode this[string propertyName] /// /// Property name to set already exists if handling duplicates is set to . /// - public IEnumerator> GetEnumerator() => _dictionary.GetEnumerator(); + public IEnumerator> GetEnumerator() => new JsonObjectEnumerator(this); /// /// Adds the specified property to the JSON object. @@ -92,7 +100,7 @@ public JsonNode this[string propertyName] /// public void Add(string propertyName, JsonNode propertyValue) { - if(propertyName == null) + if (propertyName == null) { throw new ArgumentNullException(nameof(propertyName)); } @@ -104,7 +112,7 @@ public void Add(string propertyName, JsonNode propertyValue) case DuplicatePropertyNameHandling.Ignore: return; case DuplicatePropertyNameHandling.Error: - throw new ArgumentException(SR.JsonObjectDuplicateKey); + throw new ArgumentException(string.Format(SR.JsonObjectDuplicateKey, propertyName)); } } @@ -346,7 +354,7 @@ public void AddRange(IEnumerable> jsonProperties) /// Property with specified name is not found in JSON object. /// /// - /// Does not guarantee keeping the order the same. + /// Does not guarantee keeping the same order. /// public void ModifyPropertyName(string oldName, string newName) { @@ -419,7 +427,7 @@ public JsonObject GetJsonObjectPropertyValue(string propertyName) return jsonObject; } - throw new InvalidCastException(SR.PropertyTypeMismatch); + throw new InvalidCastException(string.Format(SR.PropertyTypeMismatch, propertyName)); } /// @@ -460,6 +468,6 @@ public bool TryGetJsonObjectPropertyValue(string propertyName, out JsonObject js /// Returns an enumerator that iterates through the JSON object properties. /// /// An enumerator structure for the . - IEnumerator IEnumerable.GetEnumerator() => _dictionary.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => new JsonObjectEnumerator(this); } } 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 new file mode 100644 index 000000000000..7895681a1499 --- /dev/null +++ b/src/System.Text.Json/src/System/Text/Json/Node/JsonObjectEnumerator.cs @@ -0,0 +1,45 @@ +using System.Collections; +using System.Collections.Generic; + +namespace System.Text.Json.System.Text.Json.Node +{ + /// + /// Supports an iteration over a JSON object. + /// + public class JsonObjectEnumerator : IEnumerator> + { + private readonly 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(); + + /// + /// Gets the property in the JSON object at the current position of the enumerator. + /// + public KeyValuePair Current => _enumerator.Current; + + /// + /// Gets the property in the JSON object at the current position of the enumerator. + /// + object IEnumerator.Current => _enumerator.Current; + + /// + /// + /// + public void Dispose() => _enumerator.Dispose(); + + /// + /// Advances the enumerator to the next property of the JSON object. + /// + /// + public bool MoveNext() => _enumerator.MoveNext(); + + /// + /// Sets the enumerator to its initial position, which is before the first element in the JSON object. + /// + public void Reset() => _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 2454bf07f674..bcf459c7e30b 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 @@ -5,7 +5,7 @@ namespace System.Text.Json { /// - /// Represents a text JSON value. + /// Represents a mutable text JSON value. /// public sealed class JsonString : JsonNode, IEquatable { @@ -31,6 +31,24 @@ public sealed class JsonString : JsonNode, IEquatable /// The value to represent as a JSON string. public JsonString(ReadOnlySpan value) => Value = value.ToString(); + /// + /// 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(); + + /// + /// Initializes a new instance of the with a string representation of the structure. + /// + /// The value to represent as a JSON string. + public JsonString(DateTime value) => Value = value.ToString(); + + /// + /// Initializes a new instance of the with a string representation of the structure. + /// + /// The value to represent as a JSON string. + public JsonString(DateTimeOffset value) => Value = value.ToString(); + /// /// Gets or sets the text value represented by the instance. /// diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index 68c84ccda223..cd2674cd23cb 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -46,7 +46,7 @@ public enum ExpectedValue private static void TestDuplicates(DuplicatePropertyNameHandling duplicatePropertyNameHandling, ExpectedValue expected = default, bool valueConstructor = true) { string previousValue = "value1"; - string newValue = "value1"; + string newValue = "value2"; JsonObject jsonObject = valueConstructor ? new JsonObject(duplicatePropertyNameHandling) : new JsonObject(); jsonObject.Add("property", new JsonString(previousValue)); @@ -58,7 +58,6 @@ private static void TestDuplicates(DuplicatePropertyNameHandling duplicateProper Assert.IsType(jsonObject["property"]); string expectedString = expected == ExpectedValue.Previous ? previousValue : newValue; - Assert.Equal(expectedString, (jsonObject["property"] as JsonString).Value); // with indexer, property should change no matter which duplicates handling option is chosen: @@ -142,6 +141,7 @@ public static void TestCreatingJsonObject() var developer = new JsonObject { { "name", "Kasia" }, + { "n\\u0061me", "Kasia" }, // different property name than above one { "age", 22 }, { "is developer", true }, { "null property", (JsonNode) null } @@ -151,6 +151,10 @@ public static void TestCreatingJsonObject() var developerNameCasted = developer["name"] as JsonString; Assert.Equal("Kasia", developerNameCasted.Value); + Assert.IsType(developer["n\\u0061me"]); + developerNameCasted = developer["n\\u0061me"] as JsonString; + Assert.Equal("Kasia", developerNameCasted.Value); + Assert.IsType(developer["age"]); var developerAgeCasted = developer["age"] as JsonNumber; Assert.Equal(22, developerAgeCasted.GetInt32()); @@ -602,6 +606,14 @@ public static void TestArgumentNullValidation() var property = new KeyValuePair(null, new JsonObject()); new JsonObject().AddRange(new List>() { property }); }); + Assert.Throws(() => + { + var property1 = new KeyValuePair("regular property", new JsonObject()); + var property2 = new KeyValuePair("regular property2", new JsonObject()); + var nullProperty = new KeyValuePair(null, new JsonObject()); + var propertyList = new List>() { property1, nullProperty, property2 }; + var jsonObject = new JsonObject(propertyList); + }); Assert.Throws(() => new JsonObject()[null] = new JsonString()); Assert.Throws(() => { @@ -615,7 +627,9 @@ public static void TestArgumentNullValidation() [Fact] public static void TestDuplicatesEnumOutOfRange() { - Assert.Throws(() => new JsonObject((DuplicatePropertyNameHandling)123)); + Assert.Throws(() => new JsonObject((DuplicatePropertyNameHandling)123)); + Assert.Throws(() => new JsonObject((DuplicatePropertyNameHandling)(-1))); + Assert.Throws(() => new JsonObject((DuplicatePropertyNameHandling)3)); } } } diff --git a/src/System.Text.Json/tests/JsonStringTests.cs b/src/System.Text.Json/tests/JsonStringTests.cs index 94339e131b53..d285d019c36e 100644 --- a/src/System.Text.Json/tests/JsonStringTests.cs +++ b/src/System.Text.Json/tests/JsonStringTests.cs @@ -58,10 +58,35 @@ public static void TestNulls() Assert.Throws(() => new JsonString().Value = null); } + [Fact] + public static void TestGuid() + { + var guidString = "ca761232-ed42-11ce-bacd-00aa0057b223"; + Guid guid = new Guid(guidString); + var jsonString = new JsonString(guid); + Assert.Equal(guidString, jsonString); + } + + [Fact] + public static void TestDateTime() + { + DateTime dateTime = DateTime.Now; + var jsonString = new JsonString(dateTime); + Assert.Equal(dateTime.ToString(), jsonString); + } + + [Fact] + public static void TestDateTimeOffset() + { + DateTimeOffset dateTimeOffset = DateTimeOffset.Now; + var jsonString = new JsonString(dateTimeOffset); + Assert.Equal(dateTimeOffset.ToString(), jsonString); + } + [Fact] public static void TestReadonlySpan() { - var spanValue = new ReadOnlySpan(new char[] { 's', 'p', 'a', 'n'}); + var spanValue = new ReadOnlySpan(new char[] { 's', 'p', 'a', 'n' }); Assert.Equal("span", new JsonString(spanValue).Value); string property = null; From 46aff0460210ab59e752cb3dcb4ad6ebb2c4a45a Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 13 Aug 2019 10:35:44 -0700 Subject: [PATCH 11/15] work on review comments --- .../src/System/Text/Json/Node/JsonObject.cs | 38 +------- .../Text/Json/Node/JsonObjectEnumerator.cs | 4 +- src/System.Text.Json/tests/JsonObjectTests.cs | 95 ++++--------------- 3 files changed, 22 insertions(+), 115 deletions(-) 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 083f9605043e..69fed5c3cdd0 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 @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Generic; +using System.Diagnostics; namespace System.Text.Json { @@ -116,6 +117,7 @@ public void Add(string propertyName, JsonNode propertyValue) } } + Debug.Assert(_duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Replace); _dictionary[propertyName] = propertyValue; } @@ -342,30 +344,6 @@ public void AddRange(IEnumerable> jsonProperties) /// public bool ContainsProperty(string propertyName) => propertyName != null ? _dictionary.ContainsKey(propertyName) : throw new ArgumentNullException(nameof(propertyName)); - /// - /// Modifies the name of specified property. - /// - /// Old name of the property to change. - /// New name of the property to change. - /// - /// Property name to set already exists if handling duplicates is set to . - /// - /// - /// Property with specified name is not found in JSON object. - /// - /// - /// Does not guarantee keeping the same order. - /// - public void ModifyPropertyName(string oldName, string newName) - { - if (!_dictionary.ContainsKey(oldName)) - throw new KeyNotFoundException(string.Format(SR.PropertyNotFound, oldName)); - - JsonNode previousValue = _dictionary[oldName]; - _dictionary.Remove(oldName); - _dictionary.Add(newName, previousValue); - } - /// /// Returns the value of a property with the specified name. /// @@ -397,17 +375,7 @@ public JsonNode GetPropertyValue(string propertyName) /// When returns , the value of is meaningless. /// Null doesn't mean the property value was "null" unless is returned. /// - public bool TryGetPropertyValue(string propertyName, out JsonNode jsonNode) - { - if (!_dictionary.ContainsKey(propertyName)) - { - jsonNode = null; - return false; - } - - jsonNode = _dictionary[propertyName]; - return true; - } + public bool TryGetPropertyValue(string propertyName, out JsonNode jsonNode) => _dictionary.TryGetValue(propertyName, out jsonNode); /// /// Returns the JSON object value of a property with the specified name. 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 7895681a1499..94cb0567ea14 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 @@ -1,7 +1,7 @@ using System.Collections; using System.Collections.Generic; -namespace System.Text.Json.System.Text.Json.Node +namespace System.Text.Json { /// /// Supports an iteration over a JSON object. @@ -27,7 +27,7 @@ public class JsonObjectEnumerator : IEnumerator> object IEnumerator.Current => _enumerator.Current; /// - /// + /// Releases all resources used by the . /// public void Dispose() => _enumerator.Dispose(); diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index cd2674cd23cb..f4e7af0eb7b5 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -27,14 +27,9 @@ public static void TestIEnumerableKVPConstructor() var jsonObject = new JsonObject(jsonProperties); - Assert.IsType(jsonObject["number"]); - Assert.Equal(17, (jsonObject["number"] as JsonNumber).GetInt32()); - - Assert.IsType(jsonObject["string"]); - Assert.Equal("property value", (jsonObject["string"] as JsonString).Value); - - Assert.IsType(jsonObject["boolean"]); - Assert.True((jsonObject["boolean"] as JsonBoolean).Value); + Assert.Equal(17, ((JsonNumber)jsonObject["number"]).GetInt32()); + Assert.Equal("property value", ((JsonString)jsonObject["string"]).Value); + Assert.True(((JsonBoolean)jsonObject["boolean"]).Value); } public enum ExpectedValue @@ -51,14 +46,10 @@ private static void TestDuplicates(DuplicatePropertyNameHandling duplicateProper JsonObject jsonObject = valueConstructor ? new JsonObject(duplicatePropertyNameHandling) : new JsonObject(); jsonObject.Add("property", new JsonString(previousValue)); - Assert.IsType(jsonObject["property"]); - Assert.Equal(previousValue, (jsonObject["property"] as JsonString).Value); - - jsonObject.Add("property", new JsonString(newValue)); - Assert.IsType(jsonObject["property"]); + Assert.Equal(previousValue, ((JsonString)jsonObject["property"]).Value); string expectedString = expected == ExpectedValue.Previous ? previousValue : newValue; - Assert.Equal(expectedString, (jsonObject["property"] as JsonString).Value); + Assert.Equal(expectedString, ((JsonString) jsonObject["property"]).Value); // with indexer, property should change no matter which duplicates handling option is chosen: jsonObject["property"] = (JsonString)"indexer value"; @@ -91,47 +82,36 @@ public static void TestNumerics() var jsonObject = new JsonObject(); jsonObject.Add("byte", byte.MaxValue); - Assert.IsType(jsonObject["byte"]); Assert.Equal(byte.MaxValue, ((JsonNumber)jsonObject["byte"]).GetByte()); jsonObject.Add("short", short.MaxValue); - Assert.IsType(jsonObject["short"]); Assert.Equal(short.MaxValue, ((JsonNumber)jsonObject["short"]).GetInt16()); jsonObject.Add("int", int.MaxValue); - Assert.IsType(jsonObject["int"]); Assert.Equal(int.MaxValue, ((JsonNumber)jsonObject["int"]).GetInt32()); jsonObject.Add("long", long.MaxValue); - Assert.IsType(jsonObject["long"]); Assert.Equal(long.MaxValue, ((JsonNumber)jsonObject["long"]).GetInt64()); jsonObject.Add("float", 3.14f); - Assert.IsType(jsonObject["float"]); Assert.Equal(3.14f, ((JsonNumber)jsonObject["float"]).GetSingle()); jsonObject.Add("double", 3.14); - Assert.IsType(jsonObject["double"]); Assert.Equal(3.14, ((JsonNumber)jsonObject["double"]).GetDouble()); jsonObject.Add("sbyte", sbyte.MaxValue); - Assert.IsType(jsonObject["sbyte"]); Assert.Equal(sbyte.MaxValue, ((JsonNumber)jsonObject["sbyte"]).GetSByte()); jsonObject.Add("ushort", ushort.MaxValue); - Assert.IsType(jsonObject["ushort"]); Assert.Equal(ushort.MaxValue, ((JsonNumber)jsonObject["ushort"]).GetUInt16()); jsonObject.Add("uint", uint.MaxValue); - Assert.IsType(jsonObject["uint"]); Assert.Equal(uint.MaxValue, ((JsonNumber)jsonObject["uint"]).GetUInt32()); jsonObject.Add("ulong", ulong.MaxValue); - Assert.IsType(jsonObject["ulong"]); Assert.Equal(ulong.MaxValue, ((JsonNumber)jsonObject["ulong"]).GetUInt64()); jsonObject.Add("decimal", decimal.One); - Assert.IsType(jsonObject["decimal"]); Assert.Equal(decimal.One, ((JsonNumber)jsonObject["decimal"]).GetDecimal()); } @@ -147,22 +127,10 @@ public static void TestCreatingJsonObject() { "null property", (JsonNode) null } }; - Assert.IsType(developer["name"]); - var developerNameCasted = developer["name"] as JsonString; - Assert.Equal("Kasia", developerNameCasted.Value); - - Assert.IsType(developer["n\\u0061me"]); - developerNameCasted = developer["n\\u0061me"] as JsonString; - Assert.Equal("Kasia", developerNameCasted.Value); - - Assert.IsType(developer["age"]); - var developerAgeCasted = developer["age"] as JsonNumber; - Assert.Equal(22, developerAgeCasted.GetInt32()); - - Assert.IsType(developer["is developer"]); - var isDeveloperCasted = developer["is developer"] as JsonBoolean; - Assert.True(isDeveloperCasted.Value); - + Assert.Equal("Kasia", ((JsonString)developer["name"]).Value); + Assert.Equal("Kasia", ((JsonString)developer["n\\u0061me"]).Value); + Assert.Equal(22, ((JsonNumber)developer["age"]).GetInt32()); + Assert.True(((JsonBoolean)developer["is developer"]).Value); Assert.Null(developer["null property"]); } @@ -176,17 +144,9 @@ public static void TestCreatingJsonObjectNewMethods() { "is developer", new JsonBoolean(true) } }; - Assert.IsType(developer["name"]); - var developerNameCasted = developer["name"] as JsonString; - Assert.Equal("Kasia", developerNameCasted.Value); - - Assert.IsType(developer["age"]); - var developerAgeCasted = developer["age"] as JsonNumber; - Assert.Equal(22, developerAgeCasted.GetInt32()); - - Assert.IsType(developer["is developer"]); - var isDeveloperCasted = developer["is developer"] as JsonBoolean; - Assert.True(isDeveloperCasted.Value); + Assert.Equal("Kasia", ((JsonString)developer["name"]).Value); + Assert.Equal("Kasia", ((JsonString)developer["n\\u0061me"]).Value); + Assert.Equal(22, ((JsonNumber)developer["age"]).GetInt32()); } [Fact] @@ -344,21 +304,18 @@ public static void TestReplacingsonObjectPrimaryTypes() // Assign by creating a new instance of primary Json type person1["name"] = new JsonString("Bob"); - Assert.IsType(person1["name"]); - Assert.Equal("Bob", person1["name"] as JsonString); + Assert.Equal("Bob", (JsonString)person1["name"]); // Assign by using an implicit operator on primary Json type JsonNumber newAge = 55; person1["age"] = newAge; - Assert.IsType(person1["age"]); - Assert.Equal(55, person1["age"] as JsonNumber); + Assert.Equal(55, (JsonNumber)person1["age"]); // Assign by explicit cast from Json primary type person1["is_married"] = (JsonBoolean)false; - Assert.IsType(person1["is_married"]); - Assert.Equal(false, person1["is_married"] as JsonBoolean); + Assert.Equal(false, (JsonBoolean) person1["is_married"]); var person2 = new JsonObject { @@ -370,8 +327,7 @@ public static void TestReplacingsonObjectPrimaryTypes() // Copy property from another JsonObject person1["age"] = person2["age"]; - Assert.IsType(person1["age"]); - Assert.Equal(33, person1["age"] as JsonNumber); + Assert.Equal(33, (JsonNumber) person1["age"]); // Copy property of different typoe person1["name"] = person2["name"]; @@ -403,22 +359,6 @@ static void ModifyProperty(JsonObject jsonObject, string previousName, string ne Assert.Equal(previousValue, reportingEmployees[newName]); } - [Fact] - public static void TestModifyingJsonObjectKeyModifyMethod() - { - JsonObject manager = EmployeesDatabase.GetManager(); - JsonObject reportingEmployees = manager.GetJsonObjectPropertyValue("reporting employees"); - - Assert.True(reportingEmployees.ContainsProperty("software developers")); - JsonNode previousValue = reportingEmployees["software developers"]; - - reportingEmployees.ModifyPropertyName("software developers", "software engineers"); - - Assert.False(reportingEmployees.ContainsProperty("software developers")); - Assert.True(reportingEmployees.ContainsProperty("software engineers")); - Assert.Equal(previousValue, reportingEmployees["software engineers"]); - } - [Fact] public static void TestAccessingNestedJsonObjectCastWithAs() { @@ -549,8 +489,7 @@ public static void TestTryGetProperty () }; Assert.True(jsonObject.TryGetPropertyValue("name", out JsonNode property)); - Assert.IsType(property); - Assert.Equal("value", property as JsonString); + Assert.Equal("value", (JsonString)property); Assert.False(jsonObject.TryGetPropertyValue("other", out property)); Assert.Null(property); } From a384394e17ac4d80d56d8a07b29cde25d8affffb Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 13 Aug 2019 11:56:15 -0700 Subject: [PATCH 12/15] review comments included --- src/System.Text.Json/ref/System.Text.Json.cs | 14 +++- .../src/System/Text/Json/Node/JsonObject.cs | 55 +++++++++++++- src/System.Text.Json/tests/JsonObjectTests.cs | 74 ++++++++++++------- src/System.Text.Json/tests/JsonStringTests.cs | 23 +++--- 4 files changed, 126 insertions(+), 40 deletions(-) diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index 88ac047b50b6..8696e7d4d819 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -276,11 +276,15 @@ 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.DateTime propertyValue) { } + public void Add(string propertyName, System.DateTimeOffset propertyValue) { } public void Add(string propertyName, decimal propertyValue) { } public void Add(string propertyName, double propertyValue) { } + public void Add(string propertyName, System.Guid propertyValue) { } public void Add(string propertyName, short propertyValue) { } public void Add(string propertyName, int propertyValue) { } public void Add(string propertyName, long propertyValue) { } + public void Add(string propertyName, System.ReadOnlySpan propertyValue) { } [System.CLSCompliantAttribute(false)] public void Add(string propertyName, sbyte propertyValue) { } public void Add(string propertyName, float propertyValue) { } @@ -297,12 +301,20 @@ public void AddRange(System.Collections.Generic.IEnumerable> GetEnumerator() { throw null; } public System.Text.Json.JsonObject GetJsonObjectPropertyValue(string propertyName) { throw null; } public System.Text.Json.JsonNode GetPropertyValue(string propertyName) { throw null; } - public void ModifyPropertyName(string oldName, string newName) { } public bool Remove(string propertyName) { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { 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; } } + public partial class JsonObjectEnumerator : System.Collections.Generic.IEnumerator>, System.Collections.IEnumerator, System.IDisposable + { + public JsonObjectEnumerator(System.Text.Json.JsonObject jsonObject) { } + public System.Collections.Generic.KeyValuePair Current { get { throw null; } } + object System.Collections.IEnumerator.Current { get { throw null; } } + public void Dispose() { } + public bool MoveNext() { throw null; } + public void Reset() { } + } public readonly partial struct JsonProperty { private readonly object _dummy; 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 69fed5c3cdd0..6418bfb099e8 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 @@ -115,9 +115,10 @@ public void Add(string propertyName, JsonNode propertyValue) case DuplicatePropertyNameHandling.Error: throw new ArgumentException(string.Format(SR.JsonObjectDuplicateKey, propertyName)); } + + Debug.Assert(_duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Replace); } - Debug.Assert(_duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Replace); _dictionary[propertyName] = propertyValue; } @@ -134,6 +135,58 @@ public void Add(string propertyName, JsonNode propertyValue) /// public void Add(string propertyName, string propertyValue) => Add(propertyName, new JsonString(propertyValue)); + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + /// + /// Provided property name is null. + /// + public void Add(string propertyName, ReadOnlySpan propertyValue) => Add(propertyName, new JsonString(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + /// + /// Provided property name is null. + /// + public void Add(string propertyName, Guid propertyValue) => Add(propertyName, new JsonString(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + /// + /// Provided property name is null. + /// + public void Add(string propertyName, DateTime propertyValue) => Add(propertyName, new JsonString(propertyValue)); + + /// + /// Adds the specified property as a to the JSON object. + /// + /// Name of the property to add. + /// value of the property to add. + /// + /// Property name to set already exists if handling duplicates is set to . + /// + /// + /// Provided property name is null. + /// + public void Add(string propertyName, DateTimeOffset propertyValue) => Add(propertyName, new JsonString(propertyValue)); + /// /// Adds the specified property as a to the JSON object. /// diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index f4e7af0eb7b5..38e121899c1e 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -48,6 +48,8 @@ private static void TestDuplicates(DuplicatePropertyNameHandling duplicateProper Assert.Equal(previousValue, ((JsonString)jsonObject["property"]).Value); + jsonObject.Add("property", new JsonString(newValue)); + string expectedString = expected == ExpectedValue.Previous ? previousValue : newValue; Assert.Equal(expectedString, ((JsonString) jsonObject["property"]).Value); @@ -115,6 +117,46 @@ public static void TestNumerics() Assert.Equal(decimal.One, ((JsonNumber)jsonObject["decimal"]).GetDecimal()); } + [Fact] + public static void TestReadonlySpan() + { + var jsonObject = new JsonObject(); + + var spanValue = new ReadOnlySpan(new char[] { 's', 'p', 'a', 'n' }); + jsonObject.Add("span", spanValue); + Assert.Equal("span", (JsonString)jsonObject["span"]); + + string property = null; + spanValue = property.AsSpan(); + jsonObject.Add("span", spanValue); + Assert.Equal("", (JsonString)jsonObject["span"]); + } + + [Fact] + public static void TestGuid() + { + var guidString = "ca761232-ed42-11ce-bacd-00aa0057b223"; + Guid guid = new Guid(guidString); + var jsonObject = new JsonObject{ { "guid", guid } }; + Assert.Equal(guidString, (JsonString)jsonObject["guid"]); + } + + [Fact] + public static void TestDateTime() + { + DateTime dateTime = DateTime.Now; + var jsonObject = new JsonObject { { "dateTime", dateTime } }; + Assert.Equal(dateTime.ToString(), (JsonString)jsonObject["dateTime"]); + } + + [Fact] + public static void TestDateTimeOffset() + { + DateTimeOffset dateTimeOffset = DateTimeOffset.Now; + var jsonObject = new JsonObject { { "dateTimeOffset", dateTimeOffset } }; + Assert.Equal(dateTimeOffset.ToString(), (JsonString)jsonObject["dateTimeOffset"]); + } + [Fact] public static void TestCreatingJsonObject() { @@ -145,8 +187,8 @@ public static void TestCreatingJsonObjectNewMethods() }; Assert.Equal("Kasia", ((JsonString)developer["name"]).Value); - Assert.Equal("Kasia", ((JsonString)developer["n\\u0061me"]).Value); Assert.Equal(22, ((JsonNumber)developer["age"]).GetInt32()); + Assert.True(((JsonBoolean)developer["is developer"]).Value); } [Fact] @@ -301,21 +343,15 @@ public static void TestReplacingsonObjectPrimaryTypes() { "is_married", true } }; - // Assign by creating a new instance of primary Json type - person1["name"] = new JsonString("Bob"); + person1["name"] = new JsonString("Bob"); Assert.Equal("Bob", (JsonString)person1["name"]); - // Assign by using an implicit operator on primary Json type - JsonNumber newAge = 55; - person1["age"] = newAge; - + person1["age"] = new JsonNumber(55); Assert.Equal(55, (JsonNumber)person1["age"]); - // Assign by explicit cast from Json primary type - person1["is_married"] = (JsonBoolean)false; - - Assert.Equal(false, (JsonBoolean) person1["is_married"]); + person1["is_married"] = new JsonBoolean(false); + Assert.Equal(false, (JsonBoolean)person1["is_married"]); var person2 = new JsonObject { @@ -450,22 +486,6 @@ static bool AddEmployee(JsonObject manager) Assert.True(success); } - [Fact] - public static void TestModifyPropertyNameThrows() - { - Assert.Throws(() => new JsonObject().ModifyPropertyName("", "")); - Assert.Throws(() => - { - var jsonObject = new JsonObject() - { - { "oldName", "value" }, - { "newName", "value" } - }; - - jsonObject.ModifyPropertyName("oldName", "newName"); - }); - } - [Fact] public static void TestGetPropertyThrows() { diff --git a/src/System.Text.Json/tests/JsonStringTests.cs b/src/System.Text.Json/tests/JsonStringTests.cs index d285d019c36e..0ae4368bb208 100644 --- a/src/System.Text.Json/tests/JsonStringTests.cs +++ b/src/System.Text.Json/tests/JsonStringTests.cs @@ -58,6 +58,18 @@ public static void TestNulls() Assert.Throws(() => new JsonString().Value = null); } + [Fact] + public static void TestReadonlySpan() + { + var spanValue = new ReadOnlySpan(new char[] { 's', 'p', 'a', 'n' }); + Assert.Equal("span", new JsonString(spanValue).Value); + + string property = null; + spanValue = property.AsSpan(); + var jsonString = new JsonString(spanValue); + Assert.Equal("", jsonString.Value); + } + [Fact] public static void TestGuid() { @@ -83,17 +95,6 @@ public static void TestDateTimeOffset() Assert.Equal(dateTimeOffset.ToString(), jsonString); } - [Fact] - public static void TestReadonlySpan() - { - var spanValue = new ReadOnlySpan(new char[] { 's', 'p', 'a', 'n' }); - Assert.Equal("span", new JsonString(spanValue).Value); - - string property = null; - spanValue = property.AsSpan(); - var jsonString = new JsonString(spanValue); - } - [Fact] public static void TestChangingValue() { From 180b478dc160821650c4c760cc9ee74abb543901 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 13 Aug 2019 13:02:22 -0700 Subject: [PATCH 13/15] space fix --- src/System.Text.Json/src/System/Text/Json/Node/JsonObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6418bfb099e8..037461b070ab 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 @@ -25,7 +25,7 @@ public sealed class JsonObject : JsonNode, IEnumerable public JsonObject(DuplicatePropertyNameHandling duplicatePropertyNameHandling = DuplicatePropertyNameHandling.Replace) { - if(!Enum.IsDefined(typeof(DuplicatePropertyNameHandling), duplicatePropertyNameHandling)) + if (!Enum.IsDefined(typeof(DuplicatePropertyNameHandling), duplicatePropertyNameHandling)) { throw new ArgumentOutOfRangeException(SR.InvalidDuplicatePropertyNameHandling); } From 02b805fb86026d11950b4b241413604f7cd132e0 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Tue, 13 Aug 2019 13:11:36 -0700 Subject: [PATCH 14/15] csproj fix --- src/System.Text.Json/src/System.Text.Json.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Text.Json/src/System.Text.Json.csproj b/src/System.Text.Json/src/System.Text.Json.csproj index 11ffc0b67fc8..88f49ff23519 100644 --- a/src/System.Text.Json/src/System.Text.Json.csproj +++ b/src/System.Text.Json/src/System.Text.Json.csproj @@ -23,7 +23,6 @@ - @@ -215,6 +214,7 @@ + From c2f0e488cb490fa9c44221b200aa1053b84c5e97 Mon Sep 17 00:00:00 2001 From: Katarzyna Bulat Date: Wed, 14 Aug 2019 08:13:18 -0700 Subject: [PATCH 15/15] review comments included --- src/System.Text.Json/ref/System.Text.Json.cs | 5 +-- .../src/Resources/Strings.resx | 2 +- .../src/System/Text/Json/Node/JsonObject.cs | 8 ++--- .../Text/Json/Node/JsonObjectEnumerator.cs | 2 +- src/System.Text.Json/tests/JsonObjectTests.cs | 32 +++++++------------ src/System.Text.Json/tests/JsonStringTests.cs | 4 +-- 6 files changed, 22 insertions(+), 31 deletions(-) diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index 8696e7d4d819..2c9c06d4451b 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -306,9 +306,10 @@ public void AddRange(System.Collections.Generic.IEnumerable>, System.Collections.IEnumerator, System.IDisposable + public partial struct JsonObjectEnumerator : System.Collections.Generic.IEnumerator>, System.Collections.IEnumerator, System.IDisposable { - public JsonObjectEnumerator(System.Text.Json.JsonObject jsonObject) { } + private object _dummy; + public JsonObjectEnumerator(System.Text.Json.JsonObject jsonObject) { throw null; } public System.Collections.Generic.KeyValuePair Current { get { throw null; } } object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } diff --git a/src/System.Text.Json/src/Resources/Strings.resx b/src/System.Text.Json/src/Resources/Strings.resx index c44104175bc2..0b044d0effe1 100644 --- a/src/System.Text.Json/src/Resources/Strings.resx +++ b/src/System.Text.Json/src/Resources/Strings.resx @@ -436,7 +436,7 @@ Property with name '{0}' not found. - Property with name '{0}' has a different type than expected. + Property with name '{0}' has a different type than expected. The DuplicatePropertyNameHandling enum must be set to one of the supported values. 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 037461b070ab..19a35a163fc1 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 @@ -25,7 +25,7 @@ public sealed class JsonObject : JsonNode, IEnumerable public JsonObject(DuplicatePropertyNameHandling duplicatePropertyNameHandling = DuplicatePropertyNameHandling.Replace) { - if (!Enum.IsDefined(typeof(DuplicatePropertyNameHandling), duplicatePropertyNameHandling)) + if ((uint)duplicatePropertyNameHandling > (uint)DuplicatePropertyNameHandling.Error) { throw new ArgumentOutOfRangeException(SR.InvalidDuplicatePropertyNameHandling); } @@ -113,7 +113,7 @@ public void Add(string propertyName, JsonNode propertyValue) case DuplicatePropertyNameHandling.Ignore: return; case DuplicatePropertyNameHandling.Error: - throw new ArgumentException(string.Format(SR.JsonObjectDuplicateKey, propertyName)); + throw new ArgumentException(SR.Format(SR.JsonObjectDuplicateKey, propertyName)); } Debug.Assert(_duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Replace); @@ -409,7 +409,7 @@ public JsonNode GetPropertyValue(string propertyName) { if (!TryGetPropertyValue(propertyName, out JsonNode jsonNode)) { - throw new KeyNotFoundException(string.Format(SR.PropertyNotFound, propertyName)); + throw new KeyNotFoundException(SR.Format(SR.PropertyNotFound, propertyName)); } return jsonNode; @@ -448,7 +448,7 @@ public JsonObject GetJsonObjectPropertyValue(string propertyName) return jsonObject; } - throw new InvalidCastException(string.Format(SR.PropertyTypeMismatch, propertyName)); + throw new InvalidCastException(SR.Format(SR.PropertyTypeMismatch, propertyName)); } /// 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 94cb0567ea14..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 @@ -6,7 +6,7 @@ namespace System.Text.Json /// /// Supports an iteration over a JSON object. /// - public class JsonObjectEnumerator : IEnumerator> + public struct JsonObjectEnumerator : IEnumerator> { private readonly IEnumerator> _enumerator; diff --git a/src/System.Text.Json/tests/JsonObjectTests.cs b/src/System.Text.Json/tests/JsonObjectTests.cs index 38e121899c1e..776b5e559850 100644 --- a/src/System.Text.Json/tests/JsonObjectTests.cs +++ b/src/System.Text.Json/tests/JsonObjectTests.cs @@ -32,26 +32,16 @@ public static void TestIEnumerableKVPConstructor() Assert.True(((JsonBoolean)jsonObject["boolean"]).Value); } - public enum ExpectedValue + private static void TestDuplicates(DuplicatePropertyNameHandling duplicatePropertyNameHandling, string previousValue, string newValue, string expectedValue, bool useDefaultCtor = false) { - Previous, - New - } - - private static void TestDuplicates(DuplicatePropertyNameHandling duplicatePropertyNameHandling, ExpectedValue expected = default, bool valueConstructor = true) - { - string previousValue = "value1"; - string newValue = "value2"; - - JsonObject jsonObject = valueConstructor ? new JsonObject(duplicatePropertyNameHandling) : new JsonObject(); + JsonObject jsonObject = useDefaultCtor ? new JsonObject() : new JsonObject(duplicatePropertyNameHandling); jsonObject.Add("property", new JsonString(previousValue)); Assert.Equal(previousValue, ((JsonString)jsonObject["property"]).Value); jsonObject.Add("property", new JsonString(newValue)); - string expectedString = expected == ExpectedValue.Previous ? previousValue : newValue; - Assert.Equal(expectedString, ((JsonString) jsonObject["property"]).Value); + Assert.Equal(expectedValue, ((JsonString) jsonObject["property"]).Value); // with indexer, property should change no matter which duplicates handling option is chosen: jsonObject["property"] = (JsonString)"indexer value"; @@ -60,18 +50,18 @@ private static void TestDuplicates(DuplicatePropertyNameHandling duplicateProper [Theory] - [InlineData(DuplicatePropertyNameHandling.Replace, ExpectedValue.New)] - [InlineData(DuplicatePropertyNameHandling.Replace, ExpectedValue.New, false)] - [InlineData(DuplicatePropertyNameHandling.Ignore, ExpectedValue.Previous)] - public static void TestDuplicatesReplaceAndIgnore(DuplicatePropertyNameHandling duplicatePropertyNameHandling, ExpectedValue expected = default, bool valueConstructor = true) + [InlineData(DuplicatePropertyNameHandling.Replace, "value1", "value2", "value2")] + [InlineData(DuplicatePropertyNameHandling.Replace, "value1", "value2", "value2", true)] + [InlineData(DuplicatePropertyNameHandling.Ignore, "value1", "value2", "value1")] + public static void TestDuplicatesReplaceAndIgnore(DuplicatePropertyNameHandling duplicatePropertyNameHandling, string previousValue, string newValue, string expectedValue, bool useDefaultCtor = false) { - TestDuplicates(duplicatePropertyNameHandling, expected, valueConstructor); + TestDuplicates(duplicatePropertyNameHandling, previousValue, newValue, expectedValue, useDefaultCtor); } [Fact] public static void TestDuplicatesError() { - Assert.Throws(() => TestDuplicates(DuplicatePropertyNameHandling.Error)); + Assert.Throws(() => TestDuplicates(DuplicatePropertyNameHandling.Error, "", "", "")); JsonObject jsonObject = new JsonObject(DuplicatePropertyNameHandling.Error) { { "property", "" } }; jsonObject["property"] = (JsonString) "indexer value"; @@ -144,7 +134,7 @@ public static void TestGuid() [Fact] public static void TestDateTime() { - DateTime dateTime = DateTime.Now; + DateTime dateTime = new DateTime(DateTime.MinValue.Ticks); var jsonObject = new JsonObject { { "dateTime", dateTime } }; Assert.Equal(dateTime.ToString(), (JsonString)jsonObject["dateTime"]); } @@ -152,7 +142,7 @@ public static void TestDateTime() [Fact] public static void TestDateTimeOffset() { - DateTimeOffset dateTimeOffset = DateTimeOffset.Now; + DateTimeOffset dateTimeOffset = new DateTime(DateTime.MinValue.Ticks); var jsonObject = new JsonObject { { "dateTimeOffset", dateTimeOffset } }; Assert.Equal(dateTimeOffset.ToString(), (JsonString)jsonObject["dateTimeOffset"]); } diff --git a/src/System.Text.Json/tests/JsonStringTests.cs b/src/System.Text.Json/tests/JsonStringTests.cs index 0ae4368bb208..449117877ac3 100644 --- a/src/System.Text.Json/tests/JsonStringTests.cs +++ b/src/System.Text.Json/tests/JsonStringTests.cs @@ -82,7 +82,7 @@ public static void TestGuid() [Fact] public static void TestDateTime() { - DateTime dateTime = DateTime.Now; + DateTime dateTime = new DateTime(DateTime.MinValue.Ticks); var jsonString = new JsonString(dateTime); Assert.Equal(dateTime.ToString(), jsonString); } @@ -90,7 +90,7 @@ public static void TestDateTime() [Fact] public static void TestDateTimeOffset() { - DateTimeOffset dateTimeOffset = DateTimeOffset.Now; + DateTimeOffset dateTimeOffset = new DateTime(DateTime.MinValue.Ticks); var jsonString = new JsonString(dateTimeOffset); Assert.Equal(dateTimeOffset.ToString(), jsonString); }