Add JsonElement.WriteProperty that takes JsonEncodedText. (dotnet/corefx#38869)
authorAhson Khan <ahkha@microsoft.com>
Wed, 26 Jun 2019 14:32:53 +0000 (07:32 -0700)
committerGitHub <noreply@github.com>
Wed, 26 Jun 2019 14:32:53 +0000 (07:32 -0700)
* Add JsonElement.WriteProperty that takes JsonEncodedText.

* Add tests.

* Auto-gen the ref.

Commit migrated from https://github.com/dotnet/corefx/commit/5134166832b430ccf2d05193cda9036d9a55356b

src/libraries/System.Text.Json/ref/System.Text.Json.cs
src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs
src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs
src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.FormattedNumber.cs
src/libraries/System.Text.Json/tests/JsonDocumentTests.cs
src/libraries/System.Text.Json/tests/JsonElementWriteTests.cs

index 7be0f37..daf4623 100644 (file)
@@ -98,6 +98,7 @@ namespace System.Text.Json
         public void WriteProperty(System.ReadOnlySpan<byte> utf8PropertyName, System.Text.Json.Utf8JsonWriter writer) { }
         public void WriteProperty(System.ReadOnlySpan<char> propertyName, System.Text.Json.Utf8JsonWriter writer) { }
         public void WriteProperty(string propertyName, System.Text.Json.Utf8JsonWriter writer) { }
+        public void WriteProperty(System.Text.Json.JsonEncodedText propertyName, System.Text.Json.Utf8JsonWriter writer) { }
         public void WriteValue(System.Text.Json.Utf8JsonWriter writer) { }
         public partial struct ArrayEnumerator : System.Collections.Generic.IEnumerable<System.Text.Json.JsonElement>, System.Collections.Generic.IEnumerator<System.Text.Json.JsonElement>, System.Collections.IEnumerable, System.Collections.IEnumerator, System.IDisposable
         {
index 4969418..0a0a8d0 100644 (file)
@@ -265,7 +265,7 @@ namespace System.Text.Json
             int matchIndex = isPropertyName ? index - DbRow.Size : index;
 
             (int lastIdx, string lastString) = _lastIndexAndString;
-        
+
             if (lastIdx == matchIndex)
             {
                 return otherText.SequenceEqual(lastString.AsSpan());
@@ -312,7 +312,7 @@ namespace System.Text.Json
             DbRow row = _parsedData.Get(matchIndex);
 
             CheckExpectedType(
-                isPropertyName? JsonTokenType.PropertyName : JsonTokenType.String,
+                isPropertyName ? JsonTokenType.PropertyName : JsonTokenType.String,
                 row.TokenType);
 
             ReadOnlySpan<byte> data = _utf8Json.Span;
@@ -799,6 +799,47 @@ namespace System.Text.Json
         internal void WriteElementTo(
             int index,
             Utf8JsonWriter writer,
+            JsonEncodedText propertyName)
+        {
+            CheckNotDisposed();
+
+            DbRow row = _parsedData.Get(index);
+
+            switch (row.TokenType)
+            {
+                case JsonTokenType.StartObject:
+                    writer.WriteStartObject(propertyName);
+                    WriteComplexElement(index, writer);
+                    return;
+                case JsonTokenType.StartArray:
+                    writer.WriteStartArray(propertyName);
+                    WriteComplexElement(index, writer);
+                    return;
+                case JsonTokenType.String:
+                    WriteString(propertyName, row, writer);
+                    return;
+                case JsonTokenType.True:
+                    writer.WriteBoolean(propertyName, value: true);
+                    return;
+                case JsonTokenType.False:
+                    writer.WriteBoolean(propertyName, value: false);
+                    return;
+                case JsonTokenType.Null:
+                    writer.WriteNull(propertyName);
+                    return;
+                case JsonTokenType.Number:
+                    writer.WriteNumber(
+                        propertyName,
+                        _utf8Json.Slice(row.Location, row.SizeOrLength).Span);
+                    return;
+            }
+
+            Debug.Fail($"Unexpected encounter with JsonTokenType {row.TokenType}");
+        }
+
+        internal void WriteElementTo(
+            int index,
+            Utf8JsonWriter writer,
             ReadOnlySpan<byte> propertyName)
         {
             CheckNotDisposed();
@@ -914,45 +955,45 @@ namespace System.Text.Json
                         writer.WriteEndArray();
                         continue;
                     case JsonTokenType.PropertyName:
-                    {
-                        DbRow propertyValue = _parsedData.Get(i + DbRow.Size);
+                        {
+                            DbRow propertyValue = _parsedData.Get(i + DbRow.Size);
 
-                        ReadOnlySpan<byte> propertyName =
-                            _utf8Json.Slice(row.Location, row.SizeOrLength).Span;
+                            ReadOnlySpan<byte> propertyName =
+                                _utf8Json.Slice(row.Location, row.SizeOrLength).Span;
 
-                        // "Move" to the value.
-                        i += DbRow.Size;
+                            // "Move" to the value.
+                            i += DbRow.Size;
 
-                        switch (propertyValue.TokenType)
-                        {
-                            case JsonTokenType.String:
-                                WriteString(propertyName, propertyValue, writer);
-                                continue;
-                            case JsonTokenType.Number:
-                                writer.WriteNumber(
-                                    propertyName,
-                                    _utf8Json.Slice(propertyValue.Location, propertyValue.SizeOrLength).Span);
+                            switch (propertyValue.TokenType)
+                            {
+                                case JsonTokenType.String:
+                                    WriteString(propertyName, propertyValue, writer);
                                     continue;
-                            case JsonTokenType.True:
-                                writer.WriteBoolean(propertyName, value: true);
-                                continue;
-                            case JsonTokenType.False:
-                                writer.WriteBoolean(propertyName, value: false);
-                                continue;
-                            case JsonTokenType.Null:
-                                writer.WriteNull(propertyName);
-                                continue;
-                            case JsonTokenType.StartObject:
-                                writer.WriteStartObject(propertyName);
-                                continue;
-                            case JsonTokenType.StartArray:
-                                writer.WriteStartArray(propertyName);
-                                continue;
-                        }
+                                case JsonTokenType.Number:
+                                    writer.WriteNumber(
+                                        propertyName,
+                                        _utf8Json.Slice(propertyValue.Location, propertyValue.SizeOrLength).Span);
+                                    continue;
+                                case JsonTokenType.True:
+                                    writer.WriteBoolean(propertyName, value: true);
+                                    continue;
+                                case JsonTokenType.False:
+                                    writer.WriteBoolean(propertyName, value: false);
+                                    continue;
+                                case JsonTokenType.Null:
+                                    writer.WriteNull(propertyName);
+                                    continue;
+                                case JsonTokenType.StartObject:
+                                    writer.WriteStartObject(propertyName);
+                                    continue;
+                                case JsonTokenType.StartArray:
+                                    writer.WriteStartArray(propertyName);
+                                    continue;
+                            }
 
-                        Debug.Fail($"Unexpected encounter with JsonTokenType {row.TokenType}");
-                        break;
-                    }
+                            Debug.Fail($"Unexpected encounter with JsonTokenType {row.TokenType}");
+                            break;
+                        }
                 }
 
                 Debug.Fail($"Unexpected encounter with JsonTokenType {row.TokenType}");
@@ -992,6 +1033,22 @@ namespace System.Text.Json
             }
         }
 
+        private void WriteString(JsonEncodedText propertyName, in DbRow row, Utf8JsonWriter writer)
+        {
+            ArraySegment<byte> rented = default;
+
+            try
+            {
+                writer.WriteString(
+                    propertyName,
+                    UnescapeString(row, out rented));
+            }
+            finally
+            {
+                ClearAndReturn(rented);
+            }
+        }
+
         private void WriteString(ReadOnlySpan<byte> propertyName, in DbRow row, Utf8JsonWriter writer)
         {
             ArraySegment<byte> rented = default;
index 2c85176..4188ac3 100644 (file)
@@ -1303,6 +1303,24 @@ namespace System.Text.Json
         /// <summary>
         ///   Write the element into the provided writer as a named JSON object property.
         /// </summary>
+        /// <param name="propertyName">The pre-encoded name for this value within the JSON object.</param>
+        /// <param name="writer">The writer.</param>
+        /// <exception cref="InvalidOperationException">
+        ///   This value's <see cref="Type"/> is <see cref="JsonValueType.Undefined"/>.
+        /// </exception>
+        /// <exception cref="ObjectDisposedException">
+        ///   The parent <see cref="JsonDocument"/> has been disposed.
+        /// </exception>
+        public void WriteProperty(JsonEncodedText propertyName, Utf8JsonWriter writer)
+        {
+            CheckValidInstance();
+
+            _parent.WriteElementTo(_idx, writer, propertyName);
+        }
+
+        /// <summary>
+        ///   Write the element into the provided writer as a named JSON object property.
+        /// </summary>
         /// <param name="utf8PropertyName">
         ///   The name for this value within the JSON object, as UTF-8 text.
         /// </param>
index 940a736..f22cfd6 100644 (file)
@@ -69,6 +69,17 @@ namespace System.Text.Json
             _tokenType = JsonTokenType.Number;
         }
 
+        internal void WriteNumber(JsonEncodedText propertyName, ReadOnlySpan<byte> utf8FormattedNumber)
+        {
+            JsonWriterHelper.ValidateValue(utf8FormattedNumber);
+            JsonWriterHelper.ValidateNumber(utf8FormattedNumber);
+
+            WriteNumberByOptions(propertyName.EncodedUtf8Bytes, utf8FormattedNumber);
+
+            SetFlagToAddListSeparatorBeforeNextItem();
+            _tokenType = JsonTokenType.Number;
+        }
+
         private void WriteNumberEscape(ReadOnlySpan<char> propertyName, ReadOnlySpan<byte> value)
         {
             int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
index 775bbff..9610705 100644 (file)
@@ -1750,6 +1750,12 @@ namespace System.Text.Json.Tests
                     Utf8JsonWriter writer = default;
                     root.WriteProperty(ReadOnlySpan<byte>.Empty, writer);
                 });
+
+                Assert.Throws<ObjectDisposedException>(() =>
+                {
+                    Utf8JsonWriter writer = default;
+                    root.WriteProperty(JsonEncodedText.Encode(ReadOnlySpan<byte>.Empty), writer);
+                });
             }
         }
 
index 32d8ea6..73a3c76 100644 (file)
@@ -882,18 +882,19 @@ null,
                         val.WriteProperty(CharLabel, writer);
                         val.WriteProperty(CharLabel.AsSpan(), writer);
                         val.WriteProperty(byteUtf8, writer);
+                        val.WriteProperty(JsonEncodedText.Encode(CharLabel), writer);
                     }
 
                     writer.Flush();
 
                     AssertContents(
-                        "\"char\":null,\"char\":null,\"byte\":null," +
-                            "\"char\":false,\"char\":false,\"byte\":false," +
-                            "\"char\":true,\"char\":true,\"byte\":true," +
-                            "\"char\":\"hi\",\"char\":\"hi\",\"byte\":\"hi\"," +
-                            "\"char\":5,\"char\":5,\"byte\":5," +
-                            "\"char\":{},\"char\":{},\"byte\":{}," +
-                            "\"char\":[],\"char\":[],\"byte\":[]",
+                        "\"char\":null,\"char\":null,\"byte\":null,\"char\":null," +
+                            "\"char\":false,\"char\":false,\"byte\":false,\"char\":false," +
+                            "\"char\":true,\"char\":true,\"byte\":true,\"char\":true," +
+                            "\"char\":\"hi\",\"char\":\"hi\",\"byte\":\"hi\",\"char\":\"hi\"," +
+                            "\"char\":5,\"char\":5,\"byte\":5,\"char\":5," +
+                            "\"char\":{},\"char\":{},\"byte\":{},\"char\":{}," +
+                            "\"char\":[],\"char\":[],\"byte\":[],\"char\":[]",
                         buffer);
                 }
                 else
@@ -911,6 +912,10 @@ null,
                         JsonTestHelper.AssertThrows<InvalidOperationException>(
                             ref writer,
                             (ref Utf8JsonWriter w) => val.WriteProperty(byteUtf8, w));
+
+                        JsonTestHelper.AssertThrows<InvalidOperationException>(
+                            ref writer,
+                            (ref Utf8JsonWriter w) => val.WriteProperty(JsonEncodedText.Encode(CharLabel), w));
                     }
 
                     writer.Flush();
@@ -1048,6 +1053,13 @@ null,
                 jsonIn,
                 expectedIndent,
                 expectedMinimal);
+
+            WritePropertyValue(
+                indented,
+                JsonEncodedText.Encode(propertyName.AsSpan()),
+                jsonIn,
+                expectedIndent,
+                expectedMinimal);
         }
 
         private static void WritePropertyValue(
@@ -1156,6 +1168,41 @@ null,
             }
         }
 
+        private static void WritePropertyValue(
+            bool indented,
+            JsonEncodedText propertyName,
+            string jsonIn,
+            string expectedIndent,
+            string expectedMinimal)
+        {
+            var buffer = new ArrayBufferWriter<byte>(1024);
+            using (JsonDocument doc = JsonDocument.Parse($" [  {jsonIn}  ]", s_readerOptions))
+            {
+                JsonElement target = doc.RootElement[0];
+
+                var options = new JsonWriterOptions
+                {
+                    Indented = indented,
+                };
+
+                var writer = new Utf8JsonWriter(buffer, options);
+
+                writer.WriteStartObject();
+                target.WriteProperty(propertyName, writer);
+                writer.WriteEndObject();
+                writer.Flush();
+
+                if (indented && s_replaceNewlines)
+                {
+                    AssertContents(
+                        expectedIndent.Replace(CompiledNewline, Environment.NewLine),
+                        buffer);
+                }
+
+                AssertContents(indented ? expectedIndent : expectedMinimal, buffer);
+            }
+        }
+
         private static void AssertContents(string expectedValue, ArrayBufferWriter<byte> buffer)
         {
             Assert.Equal(