JsonNode options (dotnet/corefx#41030)
authorKatarzyna Bułat <t-kabul@microsoft.com>
Fri, 20 Sep 2019 00:47:40 +0000 (17:47 -0700)
committerAhson Khan <ahson_ahmedk@yahoo.com>
Fri, 20 Sep 2019 00:47:40 +0000 (17:47 -0700)
* JsonNodeOptions added

* review comments included

* review comments included

Commit migrated from https://github.com/dotnet/corefx/commit/5bc2806f33090e78b38fafe4d5b46d5a0a4c1f08

src/libraries/System.Text.Json/ref/System.Text.Json.cs
src/libraries/System.Text.Json/src/System.Text.Json.csproj
src/libraries/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandlingStrategy.cs [moved from src/libraries/System.Text.Json/src/System/Text/Json/Node/DuplicatePropertyNameHandling.cs with 68% similarity]
src/libraries/System.Text.Json/src/System/Text/Json/Node/JsonNode.Traversal.cs
src/libraries/System.Text.Json/src/System/Text/Json/Node/JsonNode.TraversalHelpers.cs
src/libraries/System.Text.Json/src/System/Text/Json/Node/JsonNodeOptions.cs [new file with mode: 0644]
src/libraries/System.Text.Json/tests/JsonElementWriteTests.cs
src/libraries/System.Text.Json/tests/JsonNode.TraversalTests.cs

index cfd9ef9..c960513 100644 (file)
@@ -7,7 +7,7 @@
 
 namespace System.Text.Json
 {
-    public enum DuplicatePropertyNameHandling
+    public enum DuplicatePropertyNameHandlingStrategy
     {
         Replace = 0,
         Ignore = 1,
@@ -257,11 +257,19 @@ namespace System.Text.Json
         public static implicit operator System.Text.Json.JsonNode (uint value) { throw null; }
         [System.CLSCompliantAttribute(false)]
         public static implicit operator System.Text.Json.JsonNode (ulong value) { throw null; }
-        public static System.Text.Json.JsonNode Parse(string json, System.Text.Json.JsonDocumentOptions options = default(System.Text.Json.JsonDocumentOptions), System.Text.Json.DuplicatePropertyNameHandling duplicatePropertyNameHandling = System.Text.Json.DuplicatePropertyNameHandling.Replace) { throw null; }
+        public static System.Text.Json.JsonNode Parse(string json, System.Text.Json.JsonNodeOptions options = default(System.Text.Json.JsonNodeOptions)) { throw null; }
         public string ToJsonString() { throw null; }
         public static bool TryGetNode(System.Text.Json.JsonElement jsonElement, out System.Text.Json.JsonNode jsonNode) { throw null; }
         public void WriteTo(System.Text.Json.Utf8JsonWriter writer) { }
     }
+    public partial struct JsonNodeOptions
+    {
+        private int _dummyPrimitive;
+        public bool AllowTrailingCommas { readonly get { throw null; } set { } }
+        public System.Text.Json.JsonCommentHandling CommentHandling { readonly get { throw null; } set { } }
+        public System.Text.Json.DuplicatePropertyNameHandlingStrategy DuplicatePropertyNameHandling { readonly get { throw null; } set { } }
+        public int MaxDepth { readonly get { throw null; } set { } }
+    }
     public sealed partial class JsonNull : System.Text.Json.JsonNode, System.IEquatable<System.Text.Json.JsonNull>
     {
         public JsonNull() { }
index ba0c886..661f0c0 100644 (file)
     <Reference Include="System.Threading.Tasks.Extensions" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="System\Text\Json\Node\DuplicatePropertyNameHandling.cs" />
+    <Compile Include="System\Text\Json\Node\DuplicatePropertyNameHandlingStrategy.cs" />
     <Compile Include="System\Text\Json\Node\JsonArray.cs" />
     <Compile Include="System\Text\Json\Node\JsonArrayEnumerator.cs" />
     <Compile Include="System\Text\Json\Node\JsonBoolean.cs" />
     <Compile Include="System\Text\Json\Node\JsonNode.cs" /> 
     <Compile Include="System\Text\Json\Node\JsonNode.Traversal.cs" /> 
     <Compile Include="System\Text\Json\Node\JsonNode.TraversalHelpers.cs" /> 
+    <Compile Include="System\Text\Json\Node\JsonNodeOptions.cs" />
     <Compile Include="System\Text\Json\Node\JsonNull.cs" />
     <Compile Include="System\Text\Json\Node\JsonNumber.cs" />
     <Compile Include="System\Text\Json\Node\JsonObject.cs" />
@@ -1,9 +1,13 @@
-namespace System.Text.Json
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json
 {
     /// <summary>
-    /// Specifies how duplicate property names are handled when added to JSON object.
+    /// Specifies how duplicate property names are handled when added to JSON object.
     /// </summary>
-    public enum DuplicatePropertyNameHandling
+    public enum DuplicatePropertyNameHandlingStrategy
     {
         /// <summary>
         /// Replace the existing value when there is a duplicate property. The value of the last property in the JSON object will be used.
index 7f84345..e093aa4 100644 (file)
@@ -122,10 +122,9 @@ namespace System.Text.Json
         ///   Parses a string representing JSON document into <see cref="JsonNode"/>.
         /// </summary>
         /// <param name="json">JSON to parse.</param>
-        /// <param name="options">Options to control the reader behavior during parsing.</param>
-        /// <param name="duplicatePropertyNameHandling">Specifies the way of handling duplicate property names.</param>
+        /// <param name="options">Options to control the parsing behavior.</param>
         /// <returns><see cref="JsonNode"/> representation of <paramref name="json"/>.</returns>
-        public static JsonNode Parse(string json, JsonDocumentOptions options = default, DuplicatePropertyNameHandling duplicatePropertyNameHandling = DuplicatePropertyNameHandling.Replace)
+        public static JsonNode Parse(string json, JsonNodeOptions options = default)
         {
             Utf8JsonReader reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json), options.GetReaderOptions());
 
@@ -177,7 +176,7 @@ namespace System.Text.Json
                     }
                     else
                     {
-                        AddToParent(newProperty, ref currentNodes, ref toReturn, duplicatePropertyNameHandling);
+                        AddToParent(newProperty, ref currentNodes, ref toReturn, options.DuplicatePropertyNameHandling);
                     }
                 }
 
@@ -190,7 +189,7 @@ namespace System.Text.Json
                         Debug.Assert(currentPair.Value is JsonObject);
 
                         currentNodes.Pop();
-                        AddToParent(currentPair, ref currentNodes, ref toReturn, duplicatePropertyNameHandling);
+                        AddToParent(currentPair, ref currentNodes, ref toReturn, options.DuplicatePropertyNameHandling);
                         break;
                     case JsonTokenType.StartArray:
                         AddNewPair(new JsonArray(), true);
@@ -199,7 +198,7 @@ namespace System.Text.Json
                         Debug.Assert(currentPair.Value is JsonArray);
 
                         currentNodes.Pop();
-                        AddToParent(currentPair, ref currentNodes, ref toReturn, duplicatePropertyNameHandling);
+                        AddToParent(currentPair, ref currentNodes, ref toReturn, options.DuplicatePropertyNameHandling);
                         break;
                     case JsonTokenType.PropertyName:
                         currentNodes.Push(new KeyValuePair<string, JsonNode>(reader.GetString(), null));
index 9448d9a..d5dd531 100644 (file)
@@ -17,7 +17,7 @@ namespace System.Text.Json
             KeyValuePair<string, JsonNode> nodePair,
             ref Stack<KeyValuePair<string, JsonNode>> currentNodes,
             ref JsonNode toReturn,
-            DuplicatePropertyNameHandling duplicatePropertyNameHandling = DuplicatePropertyNameHandling.Replace)
+            DuplicatePropertyNameHandlingStrategy duplicatePropertyNameHandling = DuplicatePropertyNameHandlingStrategy.Replace)
         {
             if (currentNodes.Any())
             {
@@ -32,16 +32,16 @@ namespace System.Text.Json
 
                     // Handle duplicate properties accordingly to duplicatePropertyNameHandling:
 
-                    if (duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Replace)
+                    if (duplicatePropertyNameHandling == DuplicatePropertyNameHandlingStrategy.Replace)
                     {
                         jsonObject[nodePair.Key] = nodePair.Value;
                     }
                     else if (jsonObject._dictionary.ContainsKey(nodePair.Key))
                     {
-                        if (duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Error)
+                        if (duplicatePropertyNameHandling == DuplicatePropertyNameHandlingStrategy.Error)
                             throw new ArgumentException(SR.JsonObjectDuplicateKey);
 
-                        Debug.Assert(duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Ignore);
+                        Debug.Assert(duplicatePropertyNameHandling == DuplicatePropertyNameHandlingStrategy.Ignore);
                     }
                     else
                     {
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Node/JsonNodeOptions.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Node/JsonNodeOptions.cs
new file mode 100644 (file)
index 0000000..07742f0
--- /dev/null
@@ -0,0 +1,109 @@
+// 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.Diagnostics;
+
+namespace System.Text.Json
+{
+    /// <summary>
+    ///   Provides the ability for the user to define custom behavior when parsing JSON to create a <see cref="JsonNode"/>.
+    /// </summary>
+    public struct JsonNodeOptions
+    {
+        internal const int DefaultMaxDepth = 64;
+
+        private int _maxDepth;
+        private JsonCommentHandling _commentHandling;
+
+        /// <summary>
+        ///   Gets or sets a value that determines how the <see cref="JsonNode"/> handles comments when reading through the JSON data.
+        /// </summary>
+        /// <exception cref="ArgumentOutOfRangeException">
+        ///   The comment handling enum is set to a value that is not supported (or not within the <see cref="JsonCommentHandling"/> enum range).
+        /// </exception>
+        /// <remarks>
+        ///   By default <exception cref="JsonException"/> is thrown if a comment is encountered.
+        /// </remarks>
+        public JsonCommentHandling CommentHandling
+        {
+            readonly get => _commentHandling;
+            set
+            {
+                Debug.Assert(value >= 0);
+                if (value > JsonCommentHandling.Skip)
+                    throw new ArgumentOutOfRangeException(nameof(value), SR.JsonDocumentDoesNotSupportComments);
+
+                _commentHandling = value;
+            }
+        }
+
+        /// <summary>
+        ///   Gets or sets the maximum depth allowed when parsing JSON data,
+        ///   with the default (that is, 0) indicating a maximum depth of 64.
+        /// </summary>
+        /// <exception cref="ArgumentOutOfRangeException">
+        ///   The max depth is set to a negative value.
+        /// </exception>
+        /// <value>
+        ///   The maximum depth allowed when parsing JSON data.
+        /// </value>
+        /// <remarks>
+        ///   Parsing past this depth will throw a <exception cref="JsonException"/>.
+        /// </remarks>
+        public int MaxDepth
+        {
+            readonly get => _maxDepth;
+            set
+            {
+                if (value < 0)
+                    throw ThrowHelper.GetArgumentOutOfRangeException_MaxDepthMustBePositive(nameof(value));
+
+                _maxDepth = value;
+            }
+        }
+
+        /// <summary>
+        ///   Gets or sets a value that indicates whether an extra comma at the end of a list of JSON values
+        ///   in an object or array is allowed (and ignored) within the JSON payload being read.
+        /// </summary>
+        /// <returns>
+        ///   <see langword="true"/> if an extra comma at the end of a list of JSON values in an object or array is allowed;
+        ///   otherwise, <see langword="false"/>. Default is <see langword="false"/>.
+        /// </returns>
+        /// <remarks>
+        ///   By default, it's set to false, and <exception cref="JsonException"/> is thrown if a trailing comma is encountered.
+        /// </remarks>
+        public bool AllowTrailingCommas { get; set; }
+
+        private DuplicatePropertyNameHandlingStrategy _duplicatePropertyNameHandlingStrategy;
+
+        /// <summary>
+        ///   Gets or sets the duplicate property name handling strategy.
+        /// </summary>
+        /// <remarks>
+        ///   By default, it's set to <exception cref="DuplicatePropertyNameHandlingStrategy.Replace"/>.
+        /// </remarks>
+        public DuplicatePropertyNameHandlingStrategy DuplicatePropertyNameHandling
+        {
+            readonly get => _duplicatePropertyNameHandlingStrategy;
+            set
+            {
+                if ((uint)value > (uint)DuplicatePropertyNameHandlingStrategy.Error)
+                    throw new ArgumentOutOfRangeException(SR.InvalidDuplicatePropertyNameHandling);
+
+                _duplicatePropertyNameHandlingStrategy = value;
+            }
+        }
+
+        internal JsonReaderOptions GetReaderOptions()
+        {
+            return new JsonReaderOptions
+            {
+                AllowTrailingCommas = AllowTrailingCommas,
+                CommentHandling = CommentHandling,
+                MaxDepth = MaxDepth
+            };
+        }
+    }
+}
index 0150f73..4f654e9 100644 (file)
@@ -41,7 +41,12 @@ namespace System.Text.Json.Tests
     {
         protected override JsonDocument PrepareDocument(string jsonIn)
         {
-            JsonNode jsonNode = JsonNode.Parse(jsonIn, s_options);
+            JsonNode jsonNode = JsonNode.Parse(jsonIn, new JsonNodeOptions
+            {
+                AllowTrailingCommas = s_options.AllowTrailingCommas,
+                CommentHandling = s_options.CommentHandling,
+                MaxDepth = s_options.MaxDepth
+            });
 
             using (MemoryStream stream = new MemoryStream())
             {
index 8d84084..cb04733 100644 (file)
@@ -103,7 +103,7 @@ namespace System.Text.Json.Tests
                 builder.Append("]");
             }
 
-            var options = new JsonDocumentOptions { MaxDepth = 5_000 };
+            var options = new JsonNodeOptions { MaxDepth = 5_000 };
             JsonNode jsonNode = JsonNode.Parse(builder.ToString(), options);
         }
 
@@ -186,17 +186,17 @@ namespace System.Text.Json.Tests
             Assert.Equal(2, jsonObject.GetPropertyValues().Count);
             Assert.Equal("last duplicate value", jsonObject["property"]);
 
-            jsonObject = (JsonObject) JsonNode.Parse(stringWithDuplicates, default, DuplicatePropertyNameHandling.Replace);
+            jsonObject = (JsonObject) JsonNode.Parse(stringWithDuplicates, new JsonNodeOptions() { DuplicatePropertyNameHandling = DuplicatePropertyNameHandlingStrategy.Replace });
             Assert.Equal(2, jsonObject.GetPropertyNames().Count);
             Assert.Equal(2, jsonObject.GetPropertyValues().Count);
             Assert.Equal("last duplicate value", jsonObject["property"]);
 
-            jsonObject = (JsonObject)JsonNode.Parse(stringWithDuplicates, default, DuplicatePropertyNameHandling.Ignore);
+            jsonObject = (JsonObject)JsonNode.Parse(stringWithDuplicates, new JsonNodeOptions() { DuplicatePropertyNameHandling = DuplicatePropertyNameHandlingStrategy.Ignore });
             Assert.Equal(2, jsonObject.GetPropertyNames().Count);
             Assert.Equal(2, jsonObject.GetPropertyValues().Count);
             Assert.Equal("first value", jsonObject["property"]);
 
-            Assert.Throws<ArgumentException>(() => JsonNode.Parse(stringWithDuplicates, default, DuplicatePropertyNameHandling.Error));
+            Assert.Throws<ArgumentException>(() => JsonNode.Parse(stringWithDuplicates, new JsonNodeOptions() { DuplicatePropertyNameHandling = DuplicatePropertyNameHandlingStrategy.Error }));
         }
     }
 }