diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs
index bb27d8b1a563b..c1568c8f2233f 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs
@@ -67,6 +67,8 @@ internal static class JsonConstants
// When transcoding from UTF8 -> UTF16, the byte count threshold where we rent from the array pool before performing a normal alloc.
public const long ArrayPoolMaxSizeBeforeUsingNormalAlloc = 1024 * 1024;
+ public const int MaxRawValueLength = int.MaxValue / MaxExpansionFactorWhileTranscoding;
+
public const int MaxEscapedTokenSize = 1_000_000_000; // Max size for already escaped value.
public const int MaxUnescapedTokenSize = MaxEscapedTokenSize / MaxExpansionFactorWhileEscaping; // 166_666_666 bytes
public const int MaxBase64ValueTokenSize = (MaxEscapedTokenSize >> 2) * 3 / MaxExpansionFactorWhileEscaping; // 125_000_000 bytes
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Raw.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Raw.cs
index 3ea7e1f26c8aa..051f9e2d16965 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Raw.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Raw.cs
@@ -8,26 +8,93 @@ namespace System.Text.Json
public sealed partial class Utf8JsonWriter
{
///
- /// Writes the input as JSON content.
+ /// Writes the input as JSON content. It is expected that the input content is a single complete JSON value.
///
/// The raw JSON content to write.
- /// Whether to skip validation of the input JSON content.
+ /// Whether to validate if the input is an RFC 8259-compliant JSON payload.
+ /// Thrown if is .
+ /// Thrown if the length of the input is zero or greater than 715,827,882.
+ /// Thrown if is , and the input is not RFC 8259-compliant.
+ ///
+ /// When writing untrused JSON values, do not set to as this can result in invalid JSON
+ /// being written, and/or the overall payload being written to the writer instance being invalid.
+ ///
+ /// When using this method, the input content will be written to the writer destination as-is, unless validation fails.
+ ///
+ /// The value for the writer instance is honored when using this method.
+ ///
+ /// The and values for the writer instance are not applied when using this method.
+ ///
public void WriteRawValue(string json, bool skipInputValidation = false)
{
+ if (!_options.SkipValidation)
+ {
+ ValidateWritingValue();
+ }
+
if (json == null)
{
throw new ArgumentNullException(nameof(json));
}
- WriteRawValue(json.AsSpan(), skipInputValidation);
+ TranscodeAndWriteRawValue(json.AsSpan(), skipInputValidation);
}
///
- /// Writes the input as JSON content.
+ /// Writes the input as JSON content. It is expected that the input content is a single complete JSON value.
///
/// The raw JSON content to write.
- /// Whether to skip validation of the input JSON content.
+ /// Whether to validate if the input is an RFC 8259-compliant JSON payload.
+ /// Thrown if the length of the input is zero or greater than 715,827,882.
+ /// Thrown if is , and the input is not RFC 8259-compliant.
+ ///
+ /// When writing untrused JSON values, do not set to as this can result in invalid JSON
+ /// being written, and/or the overall payload being written to the writer instance being invalid.
+ ///
+ /// When using this method, the input content will be written to the writer destination as-is, unless validation fails.
+ ///
+ /// The value for the writer instance is honored when using this method.
+ ///
+ /// The and values for the writer instance are not applied when using this method.
+ ///
public void WriteRawValue(ReadOnlySpan json, bool skipInputValidation = false)
+ {
+ if (!_options.SkipValidation)
+ {
+ ValidateWritingValue();
+ }
+
+ TranscodeAndWriteRawValue(json, skipInputValidation);
+ }
+
+ ///
+ /// Writes the input as JSON content. It is expected that the input content is a single complete JSON value.
+ ///
+ /// The raw JSON content to write.
+ /// Whether to validate if the input is an RFC 8259-compliant JSON payload.
+ /// Thrown if the length of the input is zero or greater than 715,827,882.
+ /// Thrown if is , and the input is not RFC 8259-compliant.
+ ///
+ /// When writing untrused JSON values, do not set to as this can result in invalid JSON
+ /// being written, and/or the overall payload being written to the writer instance being invalid.
+ ///
+ /// When using this method, the input content will be written to the writer destination as-is, unless validation fails.
+ ///
+ /// The value for the writer instance is honored when using this method.
+ ///
+ /// The and values for the writer instance are not applied when using this method.
+ ///
+ public void WriteRawValue(ReadOnlySpan utf8Json, bool skipInputValidation = false)
+ {
+ if (!_options.SkipValidation)
+ {
+ ValidateWritingValue();
+ }
+
+ WriteRawValueInternal(utf8Json, skipInputValidation);
+ }
+
+ private void TranscodeAndWriteRawValue(ReadOnlySpan json, bool skipInputValidation)
{
byte[]? tempArray = null;
@@ -55,33 +122,40 @@ public void WriteRawValue(ReadOnlySpan json, bool skipInputValidation = fa
}
}
- ///
- /// Writes the input as JSON content.
- ///
- /// The raw JSON content to write.
- /// Whether to skip validation of the input JSON content.
- public void WriteRawValue(ReadOnlySpan utf8Json, bool skipInputValidation = false)
+ private void WriteRawValueInternal(ReadOnlySpan utf8Json, bool skipInputValidation)
{
- if (utf8Json.Length == 0)
+ int len = utf8Json.Length;
+
+ if (len == 0)
{
ThrowHelper.ThrowArgumentException(SR.ExpectedJsonTokens);
}
-
- if (!skipInputValidation)
+ else if (len > JsonConstants.MaxRawValueLength)
{
- Utf8JsonReader reader = new Utf8JsonReader(utf8Json);
+ ThrowHelper.ThrowArgumentException_ValueTooLarge(len);
+ }
- try
- {
- while (reader.Read());
- }
- catch (JsonReaderException ex)
+ if (skipInputValidation)
+ {
+ // Treat all unvalidated raw JSON value writes as string. If the payload is valid, this approach does
+ // not affect structural validation since a string token is equivalent to a complete object, array,
+ // or other complete JSON tokens when considering structural validation on subsequent writer calls.
+ // If the payload is not valid, then we make no guarantees about the structural validation of the final payload.
+ _tokenType = JsonTokenType.String;
+ }
+ else
+ {
+ // Utilize reader validation.
+ Utf8JsonReader reader = new(utf8Json);
+ while (reader.Read())
{
- ThrowHelper.ThrowArgumentException(ex.Message);
+ _tokenType = reader.TokenType;
}
}
- int maxRequired = utf8Json.Length + 1; // Optionally, 1 list separator
+ // TODO (https://github.com/dotnet/runtime/issues/29293):
+ // investigate writing this in chunks, rather than requesting one potentially long, contiguous buffer.
+ int maxRequired = len + 1; // Optionally, 1 list separator
if (_memory.Length - BytesPending < maxRequired)
{
@@ -96,12 +170,10 @@ public void WriteRawValue(ReadOnlySpan utf8Json, bool skipInputValidation
}
utf8Json.CopyTo(output.Slice(BytesPending));
- BytesPending += utf8Json.Length;
+ BytesPending += len;
- SetFlagToAddListSeparatorBeforeNextItem();
- // Treat all raw JSON value writes as string.
- _tokenType = JsonTokenType.String;
+ SetFlagToAddListSeparatorBeforeNextItem();
}
}
}
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj
index d3ba50335a1d6..964f2c6d33a79 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj
@@ -186,10 +186,10 @@
+
-
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.WriteRaw.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.WriteRaw.cs
index 219d49393416f..27a342e43a5ec 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.WriteRaw.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.WriteRaw.cs
@@ -12,7 +12,6 @@ public partial class Utf8JsonWriterTests
{
private const string TestGuidAsStr = "eb97fadd-3ebf-4781-8722-f4773989160e";
private readonly static Guid s_guid = Guid.Parse(TestGuidAsStr);
- private static byte[] s_guidAsJson = WrapInQuotes(TestGuidAsStr);
private static byte[] s_oneAsJson = new byte[] { (byte)'1' };
@@ -67,7 +66,8 @@ public static IEnumerable