diff --git a/src/System.Text.Json/ref/System.Text.Json.cs b/src/System.Text.Json/ref/System.Text.Json.cs index 5492c8f7baf5..4f1635eac8b5 100644 --- a/src/System.Text.Json/ref/System.Text.Json.cs +++ b/src/System.Text.Json/ref/System.Text.Json.cs @@ -363,6 +363,10 @@ public void WriteNumberValue(float value) { } public void WriteNumberValue(uint value) { } [System.CLSCompliantAttribute(false)] public void WriteNumberValue(ulong value) { } + public void WritePropertyName(System.ReadOnlySpan utf8PropertyName) { } + public void WritePropertyName(System.ReadOnlySpan propertyName) { } + public void WritePropertyName(string propertyName) { } + public void WritePropertyName(System.Text.Json.JsonEncodedText propertyName) { } public void WriteStartArray() { } public void WriteStartArray(System.ReadOnlySpan utf8PropertyName) { } public void WriteStartArray(System.ReadOnlySpan propertyName) { } diff --git a/src/System.Text.Json/src/Resources/Strings.resx b/src/System.Text.Json/src/Resources/Strings.resx index 6bfa41a508ae..7cf20965419a 100644 --- a/src/System.Text.Json/src/Resources/Strings.resx +++ b/src/System.Text.Json/src/Resources/Strings.resx @@ -153,6 +153,9 @@ Cannot write a JSON property within an array or as the first JSON token. Current token type is '{0}'. + + Cannot write a JSON property name following another property name. A JSON value is missing. + Cannot write a JSON value after a single JSON value. Current token type is '{0}'. @@ -243,6 +246,9 @@ '{0}' is invalid without a matching open. + + '{0}' is invalid following a property name. + The maximum configured depth of {0} has been exceeded. Cannot read next JSON object. diff --git a/src/System.Text.Json/src/System/Text/Json/ThrowHelper.cs b/src/System.Text.Json/src/System/Text/Json/ThrowHelper.cs index a90dd8dd93bf..059bf891d157 100644 --- a/src/System.Text.Json/src/System/Text/Json/ThrowHelper.cs +++ b/src/System.Text.Json/src/System/Text/Json/ThrowHelper.cs @@ -461,7 +461,9 @@ private static string GetResourceString(ExceptionResource resource, int currentD { case ExceptionResource.MismatchedObjectArray: Debug.Assert(token == JsonConstants.CloseBracket || token == JsonConstants.CloseBrace); - message = SR.Format(SR.MismatchedObjectArray, (char)token); + message = (tokenType == JsonTokenType.PropertyName) ? + SR.Format(SR.CannotWriteEndAfterProperty, (char)token) : + SR.Format(SR.MismatchedObjectArray, (char)token); break; case ExceptionResource.DepthTooLarge: message = SR.Format(SR.DepthTooLarge, currentDepth & JsonConstants.RemoveFlagsBitMask, JsonConstants.MaxWriterDepth); @@ -476,7 +478,9 @@ private static string GetResourceString(ExceptionResource resource, int currentD message = SR.Format(SR.CannotWriteValueWithinObject, tokenType); break; case ExceptionResource.CannotWritePropertyWithinArray: - message = SR.Format(SR.CannotWritePropertyWithinArray, tokenType); + message = (tokenType == JsonTokenType.PropertyName) ? + SR.Format(SR.CannotWritePropertyAfterProperty) : + SR.Format(SR.CannotWritePropertyWithinArray, tokenType); break; case ExceptionResource.CannotWriteValueAfterPrimitive: message = SR.Format(SR.CannotWriteValueAfterPrimitive, tokenType); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs index dd4cf735d2a0..cf10dcd8f18b 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs @@ -301,6 +301,8 @@ private void WriteBase64Indented(ReadOnlySpan escapedPropertyName, ReadOnl output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -349,6 +351,8 @@ private void WriteBase64Indented(ReadOnlySpan escapedPropertyName, ReadOnl output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs index 51645058a2ac..b944e4a0a33a 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs @@ -306,6 +306,8 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, DateTim output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -356,6 +358,8 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, DateTim output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs index 723bc6b11667..0e3f9075ed17 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs @@ -306,6 +306,8 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, DateTim output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -356,6 +358,8 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, DateTim output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs index e7feaab50728..0b27787844dd 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs @@ -292,6 +292,8 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, decimal output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -335,6 +337,8 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, decimal output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs index c4067c43b050..0fb4ee4df48f 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs @@ -296,6 +296,8 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, double output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -339,6 +341,8 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, double output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs index f19a26a9dc9b..2c8898c26329 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs @@ -296,6 +296,8 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, float v output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -339,6 +341,8 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, float v output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs index 41fc32227902..db80b2bfcdcf 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs @@ -300,6 +300,8 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, Guid va output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -347,6 +349,8 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, Guid va output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Helpers.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Helpers.cs index 8786cc903d9a..27fa38c35ef0 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Helpers.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Helpers.cs @@ -37,7 +37,7 @@ private void ValidateWritingProperty() { if (!Options.SkipValidation) { - if (!_inObject) + if (!_inObject || _tokenType == JsonTokenType.PropertyName) { Debug.Assert(_tokenType != JsonTokenType.StartObject); ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWritePropertyWithinArray, currentDepth: default, token: default, _tokenType); @@ -50,7 +50,7 @@ private void ValidateWritingProperty(byte token) { if (!Options.SkipValidation) { - if (!_inObject) + if (!_inObject || _tokenType == JsonTokenType.PropertyName) { Debug.Assert(_tokenType != JsonTokenType.StartObject); ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWritePropertyWithinArray, currentDepth: default, token: default, _tokenType); @@ -109,6 +109,8 @@ private void WritePropertyNameIndented(ReadOnlySpan escapedPropertyName, b output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -180,6 +182,8 @@ private void WritePropertyNameIndented(ReadOnlySpan escapedPropertyName, b output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs index 912bfa41746a..703433954829 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs @@ -384,6 +384,8 @@ private void WriteLiteralIndented(ReadOnlySpan escapedPropertyName, ReadOn output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -427,6 +429,8 @@ private void WriteLiteralIndented(ReadOnlySpan escapedPropertyName, ReadOn output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs index 210be724f547..3a64538a1070 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs @@ -361,6 +361,8 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, long va output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -404,6 +406,8 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, long va output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.String.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.String.cs index 51b317789604..cea0c82957ee 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.String.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.String.cs @@ -9,6 +9,353 @@ namespace System.Text.Json { public sealed partial class Utf8JsonWriter { + /// + /// Writes the pre-encoded property name (as a JSON string) as the first part of a name/value pair of a JSON object. + /// + /// The JSON encoded property name of the JSON object to be transcoded and written as UTF-8. + /// + /// The property name should already be escaped when the instance of was created. + /// + /// + /// Thrown if this would result in an invalid JSON to be written (while validation is enabled). + /// + public void WritePropertyName(JsonEncodedText propertyName) + => WritePropertyNameHelper(propertyName.EncodedUtf8Bytes); + + private void WritePropertyNameHelper(ReadOnlySpan utf8PropertyName) + { + Debug.Assert(utf8PropertyName.Length <= JsonConstants.MaxTokenSize); + + WriteStringByOptionsPropertyName(utf8PropertyName); + + _currentDepth &= JsonConstants.RemoveFlagsBitMask; + _tokenType = JsonTokenType.PropertyName; + } + + /// + /// Writes the property name (as a JSON string) as the first part of a name/value pair of a JSON object. + /// + /// The property name of the JSON object to be transcoded and written as UTF-8. + /// + /// The property name is escaped before writing. + /// + /// + /// Thrown when the specified property name is too large. + /// + /// + /// Thrown if this would result in an invalid JSON to be written (while validation is enabled). + /// + public void WritePropertyName(string propertyName) + => WritePropertyName(propertyName.AsSpan()); + + /// + /// Writes the property name (as a JSON string) as the first part of a name/value pair of a JSON object. + /// + /// The property name of the JSON object to be transcoded and written as UTF-8. + /// + /// The property name is escaped before writing. + /// + /// + /// Thrown when the specified property name is too large. + /// + /// + /// Thrown if this would result in an invalid JSON to be written (while validation is enabled). + /// + public void WritePropertyName(ReadOnlySpan propertyName) + { + JsonWriterHelper.ValidateProperty(propertyName); + + int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName); + + Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length && propertyIdx < int.MaxValue / 2); + + if (propertyIdx != -1) + { + WriteStringEscapeProperty(propertyName, propertyIdx); + } + else + { + WriteStringByOptionsPropertyName(propertyName); + } + _currentDepth &= JsonConstants.RemoveFlagsBitMask; + _tokenType = JsonTokenType.PropertyName; + } + + private void WriteStringEscapeProperty(ReadOnlySpan propertyName, int firstEscapeIndexProp) + { + Debug.Assert(int.MaxValue / JsonConstants.MaxExpansionFactorWhileEscaping >= propertyName.Length); + + char[] propertyArray = null; + + if (firstEscapeIndexProp != -1) + { + int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); + + Span escapedPropertyName; + if (length > JsonConstants.StackallocThreshold) + { + propertyArray = ArrayPool.Shared.Rent(length); + escapedPropertyName = propertyArray; + } + else + { + // Cannot create a span directly since it gets assigned to parameter and passed down. + unsafe + { + char* ptr = stackalloc char[length]; + escapedPropertyName = new Span(ptr, length); + } + } + + JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written); + propertyName = escapedPropertyName.Slice(0, written); + } + + WriteStringByOptionsPropertyName(propertyName); + + if (propertyArray != null) + { + ArrayPool.Shared.Return(propertyArray); + } + } + + private void WriteStringByOptionsPropertyName(ReadOnlySpan propertyName) + { + ValidateWritingProperty(); + if (Options.Indented) + { + WriteStringIndentedPropertyName(propertyName); + } + else + { + WriteStringMinimizedPropertyName(propertyName); + } + } + + private void WriteStringMinimizedPropertyName(ReadOnlySpan escapedPropertyName) + { + Debug.Assert(escapedPropertyName.Length <= JsonConstants.MaxTokenSize); + Debug.Assert(escapedPropertyName.Length < (int.MaxValue - 4) / JsonConstants.MaxExpansionFactorWhileTranscoding); + + // All ASCII, 2 quotes for property name, and 1 colon => escapedPropertyName.Length + 3 + // Optionally, 1 list separator, and up to 3x growth when transcoding + int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + 4; + + if (_memory.Length - BytesPending < maxRequired) + { + Grow(maxRequired); + } + + Span output = _memory.Span; + + if (_currentDepth < 0) + { + output[BytesPending++] = JsonConstants.ListSeparator; + } + output[BytesPending++] = JsonConstants.Quote; + + TranscodeAndWrite(escapedPropertyName, output); + + output[BytesPending++] = JsonConstants.Quote; + output[BytesPending++] = JsonConstants.KeyValueSeperator; + } + + private void WriteStringIndentedPropertyName(ReadOnlySpan escapedPropertyName) + { + int indent = Indentation; + Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + + Debug.Assert(escapedPropertyName.Length <= JsonConstants.MaxTokenSize); + Debug.Assert(escapedPropertyName.Length < (int.MaxValue - 5 - indent - s_newLineLength) / JsonConstants.MaxExpansionFactorWhileTranscoding); + + // All ASCII, 2 quotes for property name, 1 colon, and 1 space => escapedPropertyName.Length + 4 + // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding + int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + 5 + s_newLineLength; + + if (_memory.Length - BytesPending < maxRequired) + { + Grow(maxRequired); + } + + Span output = _memory.Span; + + if (_currentDepth < 0) + { + output[BytesPending++] = JsonConstants.ListSeparator; + } + + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; + + output[BytesPending++] = JsonConstants.Quote; + + TranscodeAndWrite(escapedPropertyName, output); + + output[BytesPending++] = JsonConstants.Quote; + output[BytesPending++] = JsonConstants.KeyValueSeperator; + output[BytesPending++] = JsonConstants.Space; + } + + /// + /// Writes the UTF-8 property name (as a JSON string) as the first part of a name/value pair of a JSON object. + /// + /// The UTF-8 encoded property name of the JSON object to be written. + /// + /// The property name is escaped before writing. + /// + /// + /// Thrown when the specified property name is too large. + /// + /// + /// Thrown if this would result in an invalid JSON to be written (while validation is enabled). + /// + public void WritePropertyName(ReadOnlySpan utf8PropertyName) + { + JsonWriterHelper.ValidateProperty(utf8PropertyName); + + int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName); + + Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length && propertyIdx < int.MaxValue / 2); + + if (propertyIdx != -1) + { + WriteStringEscapeProperty(utf8PropertyName, propertyIdx); + } + else + { + WriteStringByOptionsPropertyName(utf8PropertyName); + } + _currentDepth &= JsonConstants.RemoveFlagsBitMask; + _tokenType = JsonTokenType.PropertyName; + } + + private void WriteStringEscapeProperty(ReadOnlySpan utf8PropertyName, int firstEscapeIndexProp) + { + Debug.Assert(int.MaxValue / JsonConstants.MaxExpansionFactorWhileEscaping >= utf8PropertyName.Length); + + byte[] propertyArray = null; + + if (firstEscapeIndexProp != -1) + { + int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); + + Span escapedPropertyName; + if (length > JsonConstants.StackallocThreshold) + { + propertyArray = ArrayPool.Shared.Rent(length); + escapedPropertyName = propertyArray; + } + else + { + // Cannot create a span directly since it gets assigned to parameter and passed down. + unsafe + { + byte* ptr = stackalloc byte[length]; + escapedPropertyName = new Span(ptr, length); + } + } + + JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written); + utf8PropertyName = escapedPropertyName.Slice(0, written); + } + + WriteStringByOptionsPropertyName(utf8PropertyName); + + if (propertyArray != null) + { + ArrayPool.Shared.Return(propertyArray); + } + } + + private void WriteStringByOptionsPropertyName(ReadOnlySpan utf8PropertyName) + { + ValidateWritingProperty(); + if (Options.Indented) + { + WriteStringIndentedPropertyName(utf8PropertyName); + } + else + { + WriteStringMinimizedPropertyName(utf8PropertyName); + } + } + + private void WriteStringMinimizedPropertyName(ReadOnlySpan escapedPropertyName) + { + Debug.Assert(escapedPropertyName.Length <= JsonConstants.MaxTokenSize); + Debug.Assert(escapedPropertyName.Length < int.MaxValue - 4); + + int minRequired = escapedPropertyName.Length + 3; // 2 quotes for property name, and 1 colon + int maxRequired = minRequired + 1; // Optionally, 1 list separator + + if (_memory.Length - BytesPending < maxRequired) + { + Grow(maxRequired); + } + + Span output = _memory.Span; + + if (_currentDepth < 0) + { + output[BytesPending++] = JsonConstants.ListSeparator; + } + output[BytesPending++] = JsonConstants.Quote; + + escapedPropertyName.CopyTo(output.Slice(BytesPending)); + BytesPending += escapedPropertyName.Length; + + output[BytesPending++] = JsonConstants.Quote; + output[BytesPending++] = JsonConstants.KeyValueSeperator; + } + + private void WriteStringIndentedPropertyName(ReadOnlySpan escapedPropertyName) + { + int indent = Indentation; + Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + + Debug.Assert(escapedPropertyName.Length <= JsonConstants.MaxTokenSize); + Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - 5 - s_newLineLength); + + int minRequired = indent + escapedPropertyName.Length + 4; // 2 quotes for property name, 1 colon, and 1 space + int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line + + if (_memory.Length - BytesPending < maxRequired) + { + Grow(maxRequired); + } + + Span output = _memory.Span; + + if (_currentDepth < 0) + { + output[BytesPending++] = JsonConstants.ListSeparator; + } + + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; + + output[BytesPending++] = JsonConstants.Quote; + + escapedPropertyName.CopyTo(output.Slice(BytesPending)); + BytesPending += escapedPropertyName.Length; + + output[BytesPending++] = JsonConstants.Quote; + output[BytesPending++] = JsonConstants.KeyValueSeperator; + output[BytesPending++] = JsonConstants.Space; + } + /// /// Writes the pre-encoded property name and pre-encoded value (as a JSON string) as part of a name/value pair of a JSON object. /// @@ -1072,6 +1419,8 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, ReadOnl output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -1119,6 +1468,8 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, ReadOnl output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -1169,6 +1520,8 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, ReadOnl output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -1218,6 +1571,8 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, ReadOnl output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs index 7a0a2a81bdc7..b57ebea60273 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs @@ -369,6 +369,8 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, ulong v output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); @@ -412,6 +414,8 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, ulong v output[BytesPending++] = JsonConstants.ListSeparator; } + Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.PropertyName); + if (_tokenType != JsonTokenType.None) { WriteNewLine(output); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Bytes.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Bytes.cs index ef1fdb895ef4..35900c033181 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Bytes.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Bytes.cs @@ -101,14 +101,16 @@ private void WriteBase64Indented(ReadOnlySpan bytes) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - output[BytesPending++] = JsonConstants.Quote; Base64EncodeAndWrite(bytes, output, encodingLength); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Comment.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Comment.cs index 4e5178b88220..baee4d2d079a 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Comment.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Comment.cs @@ -199,14 +199,16 @@ private void WriteCommentIndented(ReadOnlySpan utf8Value) Span output = _memory.Span; - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - output[BytesPending++] = JsonConstants.Slash; output[BytesPending++] = JsonConstants.Asterisk; diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTime.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTime.cs index b62679a6c7a9..f9cd8cbfdb42 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTime.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTime.cs @@ -84,14 +84,16 @@ private void WriteStringValueIndented(DateTime value) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - output[BytesPending++] = JsonConstants.Quote; Span tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength]; diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTimeOffset.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTimeOffset.cs index 45d9aeb30ec6..1c99640ac492 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTimeOffset.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTimeOffset.cs @@ -85,14 +85,16 @@ private void WriteStringValueIndented(DateTimeOffset value) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - output[BytesPending++] = JsonConstants.Quote; Span tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength]; diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Decimal.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Decimal.cs index 2ad2b1d8197d..412b39512915 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Decimal.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Decimal.cs @@ -76,14 +76,16 @@ private void WriteNumberValueIndented(decimal value) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten); Debug.Assert(result); BytesPending += bytesWritten; diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Double.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Double.cs index 74315e7ae1a8..432247fa5fdc 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Double.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Double.cs @@ -78,14 +78,16 @@ private void WriteNumberValueIndented(double value) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten); Debug.Assert(result); BytesPending += bytesWritten; diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Float.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Float.cs index 47aca2564ba9..23fcc7345ab6 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Float.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Float.cs @@ -78,14 +78,16 @@ private void WriteNumberValueIndented(float value) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten); Debug.Assert(result); BytesPending += bytesWritten; diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.FormattedNumber.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.FormattedNumber.cs index f5c245886145..4c468f373edb 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.FormattedNumber.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.FormattedNumber.cs @@ -82,14 +82,16 @@ private void WriteNumberValueIndented(ReadOnlySpan utf8Value) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - utf8Value.CopyTo(output.Slice(BytesPending)); BytesPending += utf8Value.Length; } diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Guid.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Guid.cs index 59b87f102ced..0a80cdcd6c80 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Guid.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Guid.cs @@ -81,14 +81,16 @@ private void WriteStringValueIndented(Guid value) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - output[BytesPending++] = JsonConstants.Quote; bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs index 9c26cb3e8364..9a9e690a8e68 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs @@ -17,11 +17,15 @@ private void ValidateWritingValue() { if (_inObject) { - Debug.Assert(_tokenType != JsonTokenType.None && _tokenType != JsonTokenType.StartArray); - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWriteValueWithinObject, currentDepth: default, token: default, _tokenType); + if (_tokenType != JsonTokenType.PropertyName) + { + Debug.Assert(_tokenType != JsonTokenType.None && _tokenType != JsonTokenType.StartArray); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWriteValueWithinObject, currentDepth: default, token: default, _tokenType); + } } else { + Debug.Assert(_tokenType != JsonTokenType.PropertyName); if (!_isNotPrimitive && _tokenType != JsonTokenType.None) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWriteValueAfterPrimitive, currentDepth: default, token: default, _tokenType); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Literal.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Literal.cs index 2a7abf4dcd70..2a718f12a743 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Literal.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Literal.cs @@ -100,14 +100,16 @@ private void WriteLiteralIndented(ReadOnlySpan utf8Value) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - utf8Value.CopyTo(output.Slice(BytesPending)); BytesPending += utf8Value.Length; } diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.SignedNumber.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.SignedNumber.cs index 5f772ea1f907..994aa78979a5 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.SignedNumber.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.SignedNumber.cs @@ -89,14 +89,16 @@ private void WriteNumberValueIndented(long value) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten); Debug.Assert(result); BytesPending += bytesWritten; diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs index 60d35380d5d5..797b40ddf080 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs @@ -151,14 +151,16 @@ private void WriteStringIndented(ReadOnlySpan escapedValue) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - output[BytesPending++] = JsonConstants.Quote; TranscodeAndWrite(escapedValue, output); @@ -291,14 +293,16 @@ private void WriteStringIndented(ReadOnlySpan escapedValue) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - output[BytesPending++] = JsonConstants.Quote; escapedValue.CopyTo(output.Slice(BytesPending)); diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.UnsignedNumber.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.UnsignedNumber.cs index 7659709a690c..e961cbd5e3e6 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.UnsignedNumber.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.UnsignedNumber.cs @@ -91,14 +91,16 @@ private void WriteNumberValueIndented(ulong value) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten); Debug.Assert(result); BytesPending += bytesWritten; diff --git a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs index 7b44253e625d..0954db160d53 100644 --- a/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs +++ b/src/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs @@ -506,11 +506,15 @@ private void ValidateStart() { if (_inObject) { - Debug.Assert(_tokenType != JsonTokenType.None && _tokenType != JsonTokenType.StartArray); - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotStartObjectArrayWithoutProperty, currentDepth: default, token: default, _tokenType); + if (_tokenType != JsonTokenType.PropertyName) + { + Debug.Assert(_tokenType != JsonTokenType.None && _tokenType != JsonTokenType.StartArray); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotStartObjectArrayWithoutProperty, currentDepth: default, token: default, _tokenType); + } } else { + Debug.Assert(_tokenType != JsonTokenType.PropertyName); Debug.Assert(_tokenType != JsonTokenType.StartObject); if (_tokenType != JsonTokenType.None && (!_isNotPrimitive || CurrentDepth == 0)) { @@ -539,14 +543,16 @@ private void WriteStartIndented(byte token) output[BytesPending++] = JsonConstants.ListSeparator; } - if (_tokenType != JsonTokenType.None) + if (_tokenType != JsonTokenType.PropertyName) { - WriteNewLine(output); + if (_tokenType != JsonTokenType.None) + { + WriteNewLine(output); + } + JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); + BytesPending += indent; } - JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent); - BytesPending += indent; - output[BytesPending++] = token; } @@ -917,15 +923,15 @@ private void WriteEndSlow(byte token) private void ValidateEnd(byte token) { - if (_bitStack.CurrentDepth <= 0) - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.MismatchedObjectArray, currentDepth: default, token, tokenType: default); + if (_bitStack.CurrentDepth <= 0 || _tokenType == JsonTokenType.PropertyName) + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.MismatchedObjectArray, currentDepth: default, token, _tokenType); if (token == JsonConstants.CloseBracket) { if (_inObject) { Debug.Assert(_tokenType != JsonTokenType.None); - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.MismatchedObjectArray, currentDepth: default, token, tokenType: default); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.MismatchedObjectArray, currentDepth: default, token, _tokenType); } } else @@ -934,7 +940,7 @@ private void ValidateEnd(byte token) if (!_inObject) { - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.MismatchedObjectArray, currentDepth: default, token, tokenType: default); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.MismatchedObjectArray, currentDepth: default, token, _tokenType); } } diff --git a/src/System.Text.Json/tests/Utf8JsonWriterTests.cs b/src/System.Text.Json/tests/Utf8JsonWriterTests.cs index d2375b15812f..867061ca17ca 100644 --- a/src/System.Text.Json/tests/Utf8JsonWriterTests.cs +++ b/src/System.Text.Json/tests/Utf8JsonWriterTests.cs @@ -439,7 +439,6 @@ public void DisposeAutoFlushes(bool formatted, bool skipValidation) Assert.Equal(2, stream.Position); } -#if !netfx [Theory] [InlineData(true, true)] [InlineData(true, false)] @@ -469,7 +468,6 @@ public async Task DisposeAutoFlushesAsync(bool formatted, bool skipValidation) Assert.Equal(0, writeToStream.BytesCommitted); Assert.Equal(2, stream.Position); } -#endif [Theory] [InlineData(true, true)] @@ -517,7 +515,6 @@ public void UseAfterDisposeInvalid(bool formatted, bool skipValidation) Assert.Throws(() => jsonUtf8.Reset(output)); } -#if !netfx [Theory] [InlineData(true, true)] [InlineData(true, false)] @@ -563,7 +560,6 @@ public async Task UseAfterDisposeInvalidAsync(bool formatted, bool skipValidatio Assert.Throws(() => jsonUtf8.Reset(output)); } -#endif [Theory] [InlineData(true, true)] @@ -602,10 +598,7 @@ private static async Task WriteLargeToStreamHelper(Stream stream, JsonWriterOpti { const int SyncWriteThreshold = 25_000; -#if !netfx - await -#endif - using var jsonUtf8 = new Utf8JsonWriter(stream, options); + await using var jsonUtf8 = new Utf8JsonWriter(stream, options); byte[] utf8String = Encoding.UTF8.GetBytes("some string 1234"); @@ -977,6 +970,17 @@ public void InvalidJsonMismatch(bool formatted, bool skipValidation) Assert.Throws(() => jsonUtf8.WriteEndArray()); } + jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteStartObject(); + if (skipValidation) + { + jsonUtf8.WriteStartArray(); + } + else + { + Assert.Throws(() => jsonUtf8.WriteStartArray()); + } + jsonUtf8 = new Utf8JsonWriter(output, options); jsonUtf8.WriteStartArray(); jsonUtf8.WriteStartArray(); @@ -1076,6 +1080,81 @@ public void InvalidJsonMismatch(bool formatted, bool skipValidation) { Assert.Throws(() => jsonUtf8.WriteEndArray()); } + + jsonUtf8 = new Utf8JsonWriter(output, options); + if (skipValidation) + { + jsonUtf8.WritePropertyName("test name"); + jsonUtf8.WritePropertyName(JsonEncodedText.Encode("test name")); + jsonUtf8.WritePropertyName("test name".AsSpan()); + jsonUtf8.WritePropertyName(Encoding.UTF8.GetBytes("test name")); + } + else + { + Assert.Throws(() => jsonUtf8.WritePropertyName("test name")); + Assert.Throws(() => jsonUtf8.WritePropertyName(JsonEncodedText.Encode("test name"))); + Assert.Throws(() => jsonUtf8.WritePropertyName("test name".AsSpan())); + Assert.Throws(() => jsonUtf8.WritePropertyName(Encoding.UTF8.GetBytes("test name"))); + } + + jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteStartArray(); + if (skipValidation) + { + jsonUtf8.WritePropertyName("test name"); + } + else + { + Assert.Throws(() => jsonUtf8.WritePropertyName("test name")); + } + + jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteStartObject(); + jsonUtf8.WritePropertyName("first name"); + if (skipValidation) + { + jsonUtf8.WritePropertyName("test name"); + } + else + { + Assert.Throws(() => jsonUtf8.WritePropertyName("test name")); + } + + jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteStartObject(); + jsonUtf8.WritePropertyName("first name"); + if (skipValidation) + { + jsonUtf8.WriteStartArray("test name"); + } + else + { + Assert.Throws(() => jsonUtf8.WriteStartArray("test name")); + } + + jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteStartObject(); + jsonUtf8.WritePropertyName("first name"); + if (skipValidation) + { + jsonUtf8.WriteStartObject("test name"); + } + else + { + Assert.Throws(() => jsonUtf8.WriteStartObject("test name")); + } + + jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteStartObject(); + jsonUtf8.WritePropertyName("first name"); + if (skipValidation) + { + jsonUtf8.WriteEndObject(); + } + else + { + Assert.Throws(() => jsonUtf8.WriteEndObject()); + } } [Theory] @@ -1228,6 +1307,50 @@ public void InvalidJsonPrimitive(bool formatted, bool skipValidation) { Assert.Throws(() => jsonUtf8.WriteEndObject()); } + + jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteNumberValue(12345); + if (skipValidation) + { + jsonUtf8.WritePropertyName("test name"); + } + else + { + Assert.Throws(() => jsonUtf8.WritePropertyName("test name")); + } + + jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteBooleanValue(true); + if (skipValidation) + { + jsonUtf8.WritePropertyName("test name"); + } + else + { + Assert.Throws(() => jsonUtf8.WritePropertyName("test name")); + } + + jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteNullValue(); + if (skipValidation) + { + jsonUtf8.WritePropertyName("test name"); + } + else + { + Assert.Throws(() => jsonUtf8.WritePropertyName("test name")); + } + + jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteStringValue("some string"); + if (skipValidation) + { + jsonUtf8.WritePropertyName("test name"); + } + else + { + Assert.Throws(() => jsonUtf8.WritePropertyName("test name")); + } } [Theory] @@ -1316,6 +1439,58 @@ public void InvalidJsonContinueShouldSucceed(bool formatted) AssertContents(sb.ToString(), output); } + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void WriteSeparateProperties(bool formatted, bool skipValidation) + { + var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation }; + var output = new ArrayBufferWriter(1024); + + var stringWriter = new StringWriter(); + var json = new JsonTextWriter(stringWriter) + { + Formatting = formatted ? Formatting.Indented : Formatting.None, + }; + + json.WriteStartObject(); + json.WritePropertyName("foo1"); + json.WriteValue("bar1"); + json.WritePropertyName("foo2"); + json.WriteValue("bar2"); + json.WritePropertyName("foo3"); + json.WriteValue("bar3"); + json.WritePropertyName("foo4"); + json.WriteValue("bar4"); + json.WritePropertyName("array"); + json.WriteStartArray(); + json.WriteEndArray(); + json.WriteEnd(); + + json.Flush(); + + string expectedStr = stringWriter.ToString(); + + var jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteStartObject(); + jsonUtf8.WritePropertyName(Encoding.UTF8.GetBytes("foo1")); + jsonUtf8.WriteStringValue("bar1"); + jsonUtf8.WritePropertyName("foo2"); + jsonUtf8.WriteStringValue("bar2"); + jsonUtf8.WritePropertyName(JsonEncodedText.Encode("foo3")); + jsonUtf8.WriteStringValue("bar3"); + jsonUtf8.WritePropertyName("foo4".AsSpan()); + jsonUtf8.WriteStringValue("bar4"); + jsonUtf8.WritePropertyName("array"); + jsonUtf8.WriteStartArray(); + jsonUtf8.WriteEndArray(); + jsonUtf8.WriteEndObject(); + jsonUtf8.Flush(); + Assert.Equal(expectedStr, Encoding.UTF8.GetString(output.WrittenMemory.ToArray())); + } + [Theory] [InlineData(true, true)] [InlineData(true, false)] @@ -1408,6 +1583,42 @@ public void WritingTooLargeProperty(bool formatted, bool skipValidation) Assert.Throws(() => jsonUtf8.WriteStartArray(key)); } + [ConditionalTheory(nameof(IsX64))] + [OuterLoop] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void WritingTooLargePropertyStandalone(bool formatted, bool skipValidation) + { + byte[] key; + char[] keyChars; + + try + { + key = new byte[1_000_000_000]; + keyChars = new char[1_000_000_000]; + } + catch (OutOfMemoryException) + { + return; + } + + key.AsSpan().Fill((byte)'a'); + keyChars.AsSpan().Fill('a'); + + var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation }; + var output = new ArrayBufferWriter(1024); + + var jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteStartObject(); + Assert.Throws(() => jsonUtf8.WritePropertyName(keyChars)); + + jsonUtf8 = new Utf8JsonWriter(output, options); + jsonUtf8.WriteStartObject(); + Assert.Throws(() => jsonUtf8.WritePropertyName(key)); + } + [ConditionalTheory(nameof(IsX64))] [OuterLoop] [InlineData(true, true)] @@ -1492,7 +1703,7 @@ public void WriteHelloWorld(bool formatted, bool skipValidation) var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation }; - for (int i = 0; i < 16; i++) + for (int i = 0; i < 32; i++) { var output = new ArrayBufferWriter(32); var jsonUtf8 = new Utf8JsonWriter(output, options); @@ -1565,6 +1776,102 @@ public void WriteHelloWorld(bool formatted, bool skipValidation) jsonUtf8.WriteString(utf8PropertyName, encodedValue); jsonUtf8.WriteString(utf8PropertyName, encodedValue); break; + case 16: + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(value); + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(value); + break; + case 17: + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(value.AsSpan()); + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(value.AsSpan()); + break; + case 18: + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(utf8Value); + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(utf8Value); + break; + case 19: + jsonUtf8.WritePropertyName(propertyName.AsSpan()); + jsonUtf8.WriteStringValue(value); + jsonUtf8.WritePropertyName(propertyName.AsSpan()); + jsonUtf8.WriteStringValue(value); + break; + case 20: + jsonUtf8.WritePropertyName(propertyName.AsSpan()); + jsonUtf8.WriteStringValue(value.AsSpan()); + jsonUtf8.WritePropertyName(propertyName.AsSpan()); + jsonUtf8.WriteStringValue(value.AsSpan()); + break; + case 21: + jsonUtf8.WritePropertyName(propertyName.AsSpan()); + jsonUtf8.WriteStringValue(utf8Value); + jsonUtf8.WritePropertyName(propertyName.AsSpan()); + jsonUtf8.WriteStringValue(utf8Value); + break; + case 22: + jsonUtf8.WritePropertyName(utf8PropertyName); + jsonUtf8.WriteStringValue(value); + jsonUtf8.WritePropertyName(utf8PropertyName); + jsonUtf8.WriteStringValue(value); + break; + case 23: + jsonUtf8.WritePropertyName(utf8PropertyName); + jsonUtf8.WriteStringValue(value.AsSpan()); + jsonUtf8.WritePropertyName(utf8PropertyName); + jsonUtf8.WriteStringValue(value.AsSpan()); + break; + case 24: + jsonUtf8.WritePropertyName(utf8PropertyName); + jsonUtf8.WriteStringValue(utf8Value); + jsonUtf8.WritePropertyName(utf8PropertyName); + jsonUtf8.WriteStringValue(utf8Value); + break; + case 25: + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(value); + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(value); + break; + case 26: + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(value.AsSpan()); + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(value.AsSpan()); + break; + case 27: + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(utf8Value); + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(utf8Value); + break; + case 28: + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(encodedValue); + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(encodedValue); + break; + case 29: + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(encodedValue); + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(encodedValue); + break; + case 30: + jsonUtf8.WritePropertyName(propertyName.AsSpan()); + jsonUtf8.WriteStringValue(encodedValue); + jsonUtf8.WritePropertyName(propertyName.AsSpan()); + jsonUtf8.WriteStringValue(encodedValue); + break; + case 31: + jsonUtf8.WritePropertyName(utf8PropertyName); + jsonUtf8.WriteStringValue(encodedValue); + jsonUtf8.WritePropertyName(utf8PropertyName); + jsonUtf8.WriteStringValue(encodedValue); + break; } jsonUtf8.WriteEndObject(); @@ -1595,7 +1902,7 @@ public void WriteHelloWorldEscaped(bool formatted, bool skipValidation) JsonEncodedText encodedPropertyName = JsonEncodedText.Encode(propertyName); JsonEncodedText encodedValue = JsonEncodedText.Encode(value); - for (int i = 0; i < 16; i++) + for (int i = 0; i < 32; i++) { var output = new ArrayBufferWriter(32); var jsonUtf8 = new Utf8JsonWriter(output, options); @@ -1668,6 +1975,102 @@ public void WriteHelloWorldEscaped(bool formatted, bool skipValidation) jsonUtf8.WriteString(propertyNameSpanUtf8, encodedValue); jsonUtf8.WriteString(propertyNameSpanUtf8, encodedValue); break; + case 16: + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(value); + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(value); + break; + case 17: + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(valueSpan); + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(valueSpan); + break; + case 18: + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(valueSpanUtf8); + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(valueSpanUtf8); + break; + case 19: + jsonUtf8.WritePropertyName(propertyNameSpan); + jsonUtf8.WriteStringValue(value); + jsonUtf8.WritePropertyName(propertyNameSpan); + jsonUtf8.WriteStringValue(value); + break; + case 20: + jsonUtf8.WritePropertyName(propertyNameSpan); + jsonUtf8.WriteStringValue(valueSpan); + jsonUtf8.WritePropertyName(propertyNameSpan); + jsonUtf8.WriteStringValue(valueSpan); + break; + case 21: + jsonUtf8.WritePropertyName(propertyNameSpan); + jsonUtf8.WriteStringValue(valueSpanUtf8); + jsonUtf8.WritePropertyName(propertyNameSpan); + jsonUtf8.WriteStringValue(valueSpanUtf8); + break; + case 22: + jsonUtf8.WritePropertyName(propertyNameSpanUtf8); + jsonUtf8.WriteStringValue(value); + jsonUtf8.WritePropertyName(propertyNameSpanUtf8); + jsonUtf8.WriteStringValue(value); + break; + case 23: + jsonUtf8.WritePropertyName(propertyNameSpanUtf8); + jsonUtf8.WriteStringValue(valueSpan); + jsonUtf8.WritePropertyName(propertyNameSpanUtf8); + jsonUtf8.WriteStringValue(valueSpan); + break; + case 24: + jsonUtf8.WritePropertyName(propertyNameSpanUtf8); + jsonUtf8.WriteStringValue(valueSpanUtf8); + jsonUtf8.WritePropertyName(propertyNameSpanUtf8); + jsonUtf8.WriteStringValue(valueSpanUtf8); + break; + case 25: + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(value); + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(value); + break; + case 26: + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(value.AsSpan()); + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(value.AsSpan()); + break; + case 27: + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(valueSpanUtf8); + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(valueSpanUtf8); + break; + case 28: + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(encodedValue); + jsonUtf8.WritePropertyName(encodedPropertyName); + jsonUtf8.WriteStringValue(encodedValue); + break; + case 29: + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(encodedValue); + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(encodedValue); + break; + case 30: + jsonUtf8.WritePropertyName(propertyName.AsSpan()); + jsonUtf8.WriteStringValue(encodedValue); + jsonUtf8.WritePropertyName(propertyName.AsSpan()); + jsonUtf8.WriteStringValue(encodedValue); + break; + case 31: + jsonUtf8.WritePropertyName(propertyNameSpanUtf8); + jsonUtf8.WriteStringValue(encodedValue); + jsonUtf8.WritePropertyName(propertyNameSpanUtf8); + jsonUtf8.WriteStringValue(encodedValue); + break; } jsonUtf8.WriteEndObject(); @@ -2322,7 +2725,7 @@ public void WriteHelloWorldEscaped(bool formatted, bool skipValidation, string k JsonEncodedText encodedKey = JsonEncodedText.Encode(key); JsonEncodedText encodedValue = JsonEncodedText.Encode(value); - for (int i = 0; i < 16; i++) + for (int i = 0; i < 32; i++) { var output = new ArrayBufferWriter(1024); var jsonUtf8 = new Utf8JsonWriter(output, options); @@ -2379,6 +2782,70 @@ public void WriteHelloWorldEscaped(bool formatted, bool skipValidation, string k case 15: jsonUtf8.WriteString(keyUtf8, encodedValue); break; + case 16: + jsonUtf8.WritePropertyName(key); + jsonUtf8.WriteStringValue(value); + break; + case 17: + jsonUtf8.WritePropertyName(key); + jsonUtf8.WriteStringValue(value.AsSpan()); + break; + case 18: + jsonUtf8.WritePropertyName(key); + jsonUtf8.WriteStringValue(valueUtf8); + break; + case 19: + jsonUtf8.WritePropertyName(key.AsSpan()); + jsonUtf8.WriteStringValue(value); + break; + case 20: + jsonUtf8.WritePropertyName(key.AsSpan()); + jsonUtf8.WriteStringValue(value.AsSpan()); + break; + case 21: + jsonUtf8.WritePropertyName(key.AsSpan()); + jsonUtf8.WriteStringValue(valueUtf8); + break; + case 22: + jsonUtf8.WritePropertyName(keyUtf8); + jsonUtf8.WriteStringValue(value); + break; + case 23: + jsonUtf8.WritePropertyName(keyUtf8); + jsonUtf8.WriteStringValue(value.AsSpan()); + break; + case 24: + jsonUtf8.WritePropertyName(keyUtf8); + jsonUtf8.WriteStringValue(valueUtf8); + break; + case 25: + jsonUtf8.WritePropertyName(encodedKey); + jsonUtf8.WriteStringValue(value); + break; + case 26: + jsonUtf8.WritePropertyName(encodedKey); + jsonUtf8.WriteStringValue(value.AsSpan()); + break; + case 27: + jsonUtf8.WritePropertyName(encodedKey); + jsonUtf8.WriteStringValue(valueUtf8); + break; + case 28: + jsonUtf8.WritePropertyName(encodedKey); + jsonUtf8.WriteStringValue(encodedValue); + break; + case 29: + jsonUtf8.WritePropertyName(key); + jsonUtf8.WriteStringValue(encodedValue); + break; + case 30: + jsonUtf8.WritePropertyName(key.AsSpan()); + jsonUtf8.WriteStringValue(encodedValue); + break; + case 31: + jsonUtf8.WritePropertyName(keyUtf8); + jsonUtf8.WriteStringValue(encodedValue); + break; } jsonUtf8.WriteEndObject(); @@ -2416,7 +2883,7 @@ public void EscapeAsciiCharacters(bool formatted, bool skipValidation) string expectedStr = GetEscapedExpectedString(prettyPrint: formatted, propertyName, value, StringEscapeHandling.EscapeHtml); var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation }; - for (int i = 0; i < 3; i++) + for (int i = 0; i < 6; i++) { var output = new ArrayBufferWriter(1024); var jsonUtf8 = new Utf8JsonWriter(output, options); @@ -2434,6 +2901,18 @@ public void EscapeAsciiCharacters(bool formatted, bool skipValidation) case 2: jsonUtf8.WriteString(JsonEncodedText.Encode(propertyName), JsonEncodedText.Encode(value)); break; + case 3: + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(value); + break; + case 4: + jsonUtf8.WritePropertyName(Encoding.UTF8.GetBytes(propertyName)); + jsonUtf8.WriteStringValue(Encoding.UTF8.GetBytes(value)); + break; + case 5: + jsonUtf8.WritePropertyName(JsonEncodedText.Encode(propertyName)); + jsonUtf8.WriteStringValue(JsonEncodedText.Encode(value)); + break; } jsonUtf8.WriteEndObject(); @@ -2467,7 +2946,7 @@ public void EscapeCharacters(bool formatted, bool skipValidation) string expectedStr = GetEscapedExpectedString(prettyPrint: formatted, propertyName, value, StringEscapeHandling.EscapeNonAscii); var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation }; - for (int i = 0; i < 3; i++) + for (int i = 0; i < 6; i++) { var output = new ArrayBufferWriter(1024); var jsonUtf8 = new Utf8JsonWriter(output, options); @@ -2485,6 +2964,18 @@ public void EscapeCharacters(bool formatted, bool skipValidation) case 2: jsonUtf8.WriteString(JsonEncodedText.Encode(propertyName), JsonEncodedText.Encode(value)); break; + case 3: + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(value); + break; + case 4: + jsonUtf8.WritePropertyName(Encoding.UTF8.GetBytes(propertyName)); + jsonUtf8.WriteStringValue(Encoding.UTF8.GetBytes(value)); + break; + case 5: + jsonUtf8.WritePropertyName(JsonEncodedText.Encode(propertyName)); + jsonUtf8.WriteStringValue(JsonEncodedText.Encode(value)); + break; } jsonUtf8.WriteEndObject(); @@ -2509,7 +3000,7 @@ public void EscapeSurrogatePairs(bool formatted, bool skipValidation) string expectedStr = GetEscapedExpectedString(prettyPrint: formatted, propertyName, value, StringEscapeHandling.EscapeNonAscii); var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation }; - for (int i = 0; i < 3; i++) + for (int i = 0; i < 6; i++) { var output = new ArrayBufferWriter(1024); var jsonUtf8 = new Utf8JsonWriter(output, options); @@ -2527,6 +3018,18 @@ public void EscapeSurrogatePairs(bool formatted, bool skipValidation) case 2: jsonUtf8.WriteString(JsonEncodedText.Encode(propertyName), JsonEncodedText.Encode(value)); break; + case 3: + jsonUtf8.WritePropertyName(propertyName); + jsonUtf8.WriteStringValue(value); + break; + case 4: + jsonUtf8.WritePropertyName(Encoding.UTF8.GetBytes(propertyName)); + jsonUtf8.WriteStringValue(Encoding.UTF8.GetBytes(value)); + break; + case 5: + jsonUtf8.WritePropertyName(JsonEncodedText.Encode(propertyName)); + jsonUtf8.WriteStringValue(JsonEncodedText.Encode(value)); + break; } jsonUtf8.WriteEndObject(); @@ -2552,7 +3055,7 @@ public void InvalidUTF8(bool formatted, bool skipValidation) var invalidUtf8 = new byte[2] { 0xc3, 0x28 }; jsonUtf8.WriteStartObject(); - for (int i = 0; i < 4; i++) + for (int i = 0; i < 6; i++) { switch (i) { @@ -2568,6 +3071,18 @@ public void InvalidUTF8(bool formatted, bool skipValidation) case 3: jsonUtf8.WriteString(validUtf8, validUtf8); break; + case 4: + Assert.Throws(() => jsonUtf8.WritePropertyName(invalidUtf8)); + break; + case 5: + jsonUtf8.WritePropertyName(validUtf8); + Assert.Throws(() => jsonUtf8.WriteStringValue(invalidUtf8)); + jsonUtf8.WriteStringValue(validUtf8); + break; + case 6: + jsonUtf8.WritePropertyName(validUtf8); + jsonUtf8.WriteStringValue(validUtf8); + break; } } jsonUtf8.WriteEndObject(); @@ -2590,7 +3105,7 @@ public void InvalidUTF16(bool formatted, bool skipValidation) var invalidUtf16 = new char[2] { (char)0xD801, 'a' }; jsonUtf8.WriteStartObject(); - for (int i = 0; i < 4; i++) + for (int i = 0; i < 7; i++) { switch (i) { @@ -2606,6 +3121,18 @@ public void InvalidUTF16(bool formatted, bool skipValidation) case 3: jsonUtf8.WriteString(validUtf16, validUtf16); break; + case 4: + Assert.Throws(() => jsonUtf8.WritePropertyName(invalidUtf16)); + break; + case 5: + jsonUtf8.WritePropertyName(validUtf16); + Assert.Throws(() => jsonUtf8.WriteStringValue(invalidUtf16)); + jsonUtf8.WriteStringValue(validUtf16); + break; + case 6: + jsonUtf8.WritePropertyName(validUtf16); + jsonUtf8.WriteStringValue(validUtf16); + break; } } jsonUtf8.WriteEndObject(); @@ -2943,7 +3470,7 @@ public void WriteBooleanValue(bool formatted, bool skipValidation, bool value, s var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation }; - for (int i = 0; i < 3; i++) + for (int i = 0; i < 4; i++) { var output = new ArrayBufferWriter(1024); var jsonUtf8 = new Utf8JsonWriter(output, options); @@ -2961,6 +3488,10 @@ public void WriteBooleanValue(bool formatted, bool skipValidation, bool value, s case 2: jsonUtf8.WriteBoolean(Encoding.UTF8.GetBytes(keyString), value); break; + case 3: + jsonUtf8.WritePropertyName(keyString); + jsonUtf8.WriteBooleanValue(value); + break; } jsonUtf8.WriteStartArray("temp"); @@ -2996,7 +3527,7 @@ public void WriteNullValue(bool formatted, bool skipValidation, string keyString var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation }; - for (int i = 0; i < 3; i++) + for (int i = 0; i < 4; i++) { var output = new ArrayBufferWriter(16); var jsonUtf8 = new Utf8JsonWriter(output, options); @@ -3017,6 +3548,12 @@ public void WriteNullValue(bool formatted, bool skipValidation, string keyString jsonUtf8.WriteNull(Encoding.UTF8.GetBytes(keyString)); jsonUtf8.WriteNull(Encoding.UTF8.GetBytes(keyString)); break; + case 3: + jsonUtf8.WritePropertyName(keyString); + jsonUtf8.WriteNullValue(); + jsonUtf8.WritePropertyName(keyString); + jsonUtf8.WriteNullValue(); + break; } jsonUtf8.WriteStartArray("temp"); @@ -3062,7 +3599,7 @@ public void WriteIntegerValue(bool formatted, bool skipValidation, int value) var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation }; - for (int i = 0; i < 3; i++) + for (int i = 0; i < 4; i++) { var output = new ArrayBufferWriter(1024); var jsonUtf8 = new Utf8JsonWriter(output, options); @@ -3080,6 +3617,10 @@ public void WriteIntegerValue(bool formatted, bool skipValidation, int value) case 2: jsonUtf8.WriteNumber(Encoding.UTF8.GetBytes("message"), value); break; + case 3: + jsonUtf8.WritePropertyName("message"); + jsonUtf8.WriteNumberValue(value); + break; } jsonUtf8.WriteEndObject(); @@ -4207,6 +4748,7 @@ public void WriteTooLargeArguments(bool formatted, bool skipValidation) Assert.Throws(() => jsonUtf8.WriteNumber(bytesTooLarge, (ulong)12345678901)); Assert.Throws(() => jsonUtf8.WriteBoolean(bytesTooLarge, true)); Assert.Throws(() => jsonUtf8.WriteNull(bytesTooLarge)); + Assert.Throws(() => jsonUtf8.WritePropertyName(bytesTooLarge)); Assert.Throws(() => jsonUtf8.WriteStartObject(charsTooLarge)); Assert.Throws(() => jsonUtf8.WriteString(charsTooLarge, chars)); @@ -4225,6 +4767,7 @@ public void WriteTooLargeArguments(bool formatted, bool skipValidation) Assert.Throws(() => jsonUtf8.WriteNumber(charsTooLarge, (ulong)12345678901)); Assert.Throws(() => jsonUtf8.WriteBoolean(charsTooLarge, true)); Assert.Throws(() => jsonUtf8.WriteNull(charsTooLarge)); + Assert.Throws(() => jsonUtf8.WritePropertyName(charsTooLarge)); jsonUtf8.Flush(); Assert.Equal(1, jsonUtf8.BytesCommitted);