Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2269,6 +2269,140 @@ public static void TestDepthInvalid(int depth)
});
}

[Fact]
public static void TestJsonDocumentOptionsProperties()
{
var options = new JsonDocumentOptions();
Assert.True(options.AllowDuplicateProperties);
Assert.False(options.AllowTrailingCommas);
Assert.Equal(JsonCommentHandling.Disallow, options.CommentHandling);
Assert.Equal(0, options.MaxDepth);

options.AllowDuplicateProperties = false;
Assert.False(options.AllowDuplicateProperties);

options.AllowTrailingCommas = true;
Assert.True(options.AllowTrailingCommas);

options.CommentHandling = JsonCommentHandling.Skip;
Assert.Equal(JsonCommentHandling.Skip, options.CommentHandling);

options.MaxDepth = 128;
Assert.Equal(128, options.MaxDepth);
}

[Fact]
public static void TestJsonDocumentOptionsWithTrailingCommasAndComments()
{
var options = new JsonDocumentOptions
{
AllowTrailingCommas = true,
CommentHandling = JsonCommentHandling.Skip,
MaxDepth = 32
};

string json = @"
{
""name"": ""test"", // comment
""values"": [1, 2, 3,], /* trailing comma */
}";

using JsonDocument doc = JsonDocument.Parse(json, options);
Assert.Equal(JsonValueKind.Object, doc.RootElement.ValueKind);
Assert.Equal("test", doc.RootElement.GetProperty("name").GetString());
Assert.Equal(3, doc.RootElement.GetProperty("values").GetArrayLength());
}

[Fact]
public static void TestJsonDocumentWithEscapedPropertyNames()
{
string json = @"{""prop\u0041"":""value1"",""prop\u0042"":""value2"",""prop\u0043"":123}";

using JsonDocument doc = JsonDocument.Parse(json);
Assert.Equal(JsonValueKind.Object, doc.RootElement.ValueKind);
Assert.Equal("value1", doc.RootElement.GetProperty("propA").GetString());
Assert.Equal("value2", doc.RootElement.GetProperty("propB").GetString());
Assert.Equal(123, doc.RootElement.GetProperty("propC").GetInt32());
}

[Fact]
public static void TestJsonDocumentWithMultiSegmentEscapedPropertyNames()
{
string json = @"{""name\u0041"":""test"",""value\u0042"":42,""flag\u0043"":true}";
byte[] utf8 = Encoding.UTF8.GetBytes(json);

// Create multi-segment buffer to exercise HasValueSequence path
ReadOnlySequence<byte> sequence = JsonTestHelper.GetSequence(utf8, 1);

using JsonDocument doc = JsonDocument.Parse(sequence);
Assert.Equal(JsonValueKind.Object, doc.RootElement.ValueKind);
Assert.Equal("test", doc.RootElement.GetProperty("nameA").GetString());
Assert.Equal(42, doc.RootElement.GetProperty("valueB").GetInt32());
Assert.True(doc.RootElement.GetProperty("flagC").GetBoolean());
}

[Fact]
public static void TestAllowDuplicatePropertiesFalseThrowsException()
{
string jsonWithDuplicates = @"{""name"":""first"",""name"":""second""}";
var options = new JsonDocumentOptions
{
AllowDuplicateProperties = false
};

Assert.Throws<JsonException>(() => JsonDocument.Parse(jsonWithDuplicates, options));
}

[Fact]
public static void TestAllowDuplicatePropertiesTrueAllowsDuplicates()
{
string jsonWithDuplicates = @"{""name"":""first"",""name"":""second""}";
var options = new JsonDocumentOptions
{
AllowDuplicateProperties = true
};

using JsonDocument doc = JsonDocument.Parse(jsonWithDuplicates, options);
Assert.Equal(JsonValueKind.Object, doc.RootElement.ValueKind);
// The last value wins
Assert.Equal("second", doc.RootElement.GetProperty("name").GetString());
}

[Fact]
public static void TestJsonDocumentWithMaxDepth()
{
var options = new JsonDocumentOptions { MaxDepth = 3 };

// Should succeed with depth of 3
string validJson = @"{""level1"":{""level2"":{}}}";
using JsonDocument doc = JsonDocument.Parse(validJson, options);
Assert.Equal(JsonValueKind.Object, doc.RootElement.ValueKind);

// Should fail with depth of 4
string tooDeepJson = @"{""level1"":{""level2"":{""level3"":{}}}}";
Assert.ThrowsAny<JsonException>(() => JsonDocument.Parse(tooDeepJson, options));
}

[Fact]
public static void TestJsonDocumentWithDefaultMaxDepth()
{
var options = new JsonDocumentOptions { MaxDepth = 0 }; // Default is 64

// Build a JSON with depth of 65 (should fail with default)
var sb = new System.Text.StringBuilder();
for (int i = 0; i < 65; i++)
{
sb.Append("{\"a\":");
}
sb.Append("1");
for (int i = 0; i < 65; i++)
{
sb.Append("}");
}

Assert.ThrowsAny<JsonException>(() => JsonDocument.Parse(sb.ToString(), options));
}

[Theory]
[InlineData("{ \"object\": { \"1-1\": null, \"1-2\": \"12\", }, \"array\": [ 4, 8, 1, 9, 2 ] }")]
[InlineData("[ 5, 4, 3, 2, 1, ]")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ public void ReadJsonElementFromStream(int defaultBufferSize)
[InlineData("0.000000000000000000000000000000000000000001", "1e-42")]
[InlineData("1000000000000000000000000000000000000000000", "1e42")]
[InlineData("-1.1e3", "-1100")]
[InlineData("1000", "1e3")] // Trailing zeros in integral part
[InlineData("100", "1e2")] // Trailing zeros in integral part
[InlineData("10000", "1e4")] // More trailing zeros in integral part
[InlineData("0.0001", "1e-4")] // Leading zeros in fractional part
[InlineData("0.00100", "0.001")] // Leading and trailing zeros
[InlineData("0.00000", "0")] // Zero with fractional zeros
[InlineData("0e10", "0e-5")] // Zero with different exponents
[InlineData("5000", "5e3")] // Non-1 with trailing zeros
[InlineData("79228162514264337593543950336", "792281625142643375935439503360e-1")] // decimal.MaxValue + 1
[InlineData("79228162514.264337593543950336", "792281625142643375935439503360e-19")]
[InlineData("1.75e+300", "1.75E+300")] // Variations in exponent casing
Expand Down Expand Up @@ -234,6 +242,7 @@ public static void DeepEquals_DeepJsonDocument(int depth)
Assert.True(JsonElement.DeepEquals(element, element));
}

[OuterLoop]
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported), nameof(PlatformDetection.IsNotMonoInterpreter))]
public static async Task DeepEquals_TooDeepJsonDocument_ThrowsInsufficientExecutionStackException()
{
Expand Down