diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Immutable.Write.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Immutable.Write.cs index d9e2ae4cb089d1..448170ad6b68af 100644 --- a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Immutable.Write.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Immutable.Write.cs @@ -507,5 +507,42 @@ public async Task WriteImmutableCollectionWrappers() Assert.Equal(SimpleTestClassWithImmutableSetWrapper.s_json.StripWhitespace(), await Serializer.SerializeWrapper(obj5)); Assert.Equal(SimpleTestClassWithImmutableSetWrapper.s_json.StripWhitespace(), await Serializer.SerializeWrapper(obj5)); } + + [Fact] + public async Task WriteImmutableStack() + { + ImmutableStack input = ImmutableStack.Create(1, 2, 3); + string json = await Serializer.SerializeWrapper(input); + Assert.Equal("[3,2,1]", json); + } + + [Fact] + public async Task WriteImmutableQueue() + { + ImmutableQueue input = ImmutableQueue.Create(1, 2, 3); + string json = await Serializer.SerializeWrapper(input); + Assert.Equal("[1,2,3]", json); + } + + [Fact] + public async Task WriteImmutableHashSet() + { + ImmutableHashSet input = ImmutableHashSet.Create(1, 2, 3); + string json = await Serializer.SerializeWrapper(input); + Assert.Contains("1", json); + Assert.Contains("2", json); + Assert.Contains("3", json); + } + + [Fact] + public async Task WriteImmutableDictionaryStringKey() + { + ImmutableDictionary input = ImmutableDictionary.Create() + .Add("a", 1) + .Add("b", 2); + string json = await Serializer.SerializeWrapper(input); + Assert.Contains("\"a\":1", json); + Assert.Contains("\"b\":2", json); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs index 73babaa84b4ade..07d629c6f49278 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs @@ -3996,6 +3996,72 @@ public static void ParseJsonDuplicatePropertiesErrorMessageEscaped() () => JsonDocument.Parse(json, s_noDuplicateParamsOptions), "'0'"); } + + [Fact] + public static void JsonElement_GetDouble_EdgeCases() + { + using JsonDocument doc = JsonDocument.Parse("0.0"); + double value = doc.RootElement.GetDouble(); + Assert.Equal(0.0, value); + + using JsonDocument doc2 = JsonDocument.Parse("1.7976931348623157E+308"); + double value2 = doc2.RootElement.GetDouble(); + Assert.True(value2 > 1E+307); + + using JsonDocument doc3 = JsonDocument.Parse("-1.7976931348623157E+308"); + double value3 = doc3.RootElement.GetDouble(); + Assert.True(value3 < -1E+307); + } + + [Fact] + public static void JsonElement_GetSingle_EdgeCases() + { + using JsonDocument doc = JsonDocument.Parse("0.0"); + float value = doc.RootElement.GetSingle(); + Assert.Equal(0.0f, value); + + using JsonDocument doc2 = JsonDocument.Parse("3.4028235E+38"); + float value2 = doc2.RootElement.GetSingle(); + Assert.True(value2 > 1E+37f); + + using JsonDocument doc3 = JsonDocument.Parse("-3.4028235E+38"); + float value3 = doc3.RootElement.GetSingle(); + Assert.True(value3 < -1E+37f); + } + + [Fact] + public static void ParseWithMaxDepthOption() + { + string json = """{"a":{"b":{"c":{"d":1}}}}"""; + var options = new JsonDocumentOptions { MaxDepth = 10 }; + using var doc = JsonDocument.Parse(json, options); + Assert.Equal(1, doc.RootElement.GetProperty("a").GetProperty("b").GetProperty("c").GetProperty("d").GetInt32()); + } + + [Fact] + public static void ParseWithAllowTrailingCommas() + { + string json = """{"a":1,}"""; + var options = new JsonDocumentOptions { AllowTrailingCommas = true }; + using var doc = JsonDocument.Parse(json, options); + Assert.Equal(1, doc.RootElement.GetProperty("a").GetInt32()); + } + + [Fact] + public static void JsonElement_TryGetDouble() + { + using JsonDocument doc = JsonDocument.Parse("3.14159"); + Assert.True(doc.RootElement.TryGetDouble(out double value)); + Assert.Equal(3.14159, value, precision: 5); + } + + [Fact] + public static void JsonElement_TryGetSingle() + { + using JsonDocument doc = JsonDocument.Parse("3.14"); + Assert.True(doc.RootElement.TryGetSingle(out float value)); + Assert.Equal(3.14f, value, precision: 2); + } } public class ThrowOnReadStream : MemoryStream diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNamingPolicyTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNamingPolicyTests.cs new file mode 100644 index 00000000000000..9d0a66ce90152f --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNamingPolicyTests.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Text.Json.Tests +{ + public static class JsonNamingPolicyTests + { + [Theory] + [InlineData("MyProperty", "myProperty")] + [InlineData("PropertyName", "propertyName")] + [InlineData("ABC", "aBC")] + [InlineData("A", "a")] + [InlineData("", "")] + public static void CamelCase_ConvertName(string input, string expected) + { + string result = JsonNamingPolicy.CamelCase.ConvertName(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("MyProperty", "my_property")] + [InlineData("PropertyName", "property_name")] + [InlineData("ABC", "a_b_c")] + [InlineData("HTMLParser", "h_t_m_l_parser")] + [InlineData("A", "a")] + [InlineData("", "")] + public static void SnakeCaseLower_ConvertName(string input, string expected) + { + string result = JsonNamingPolicy.SnakeCaseLower.ConvertName(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("MyProperty", "MY_PROPERTY")] + [InlineData("PropertyName", "PROPERTY_NAME")] + [InlineData("ABC", "A_B_C")] + [InlineData("HTMLParser", "H_T_M_L_PARSER")] + [InlineData("A", "A")] + [InlineData("", "")] + public static void SnakeCaseUpper_ConvertName(string input, string expected) + { + string result = JsonNamingPolicy.SnakeCaseUpper.ConvertName(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("MyProperty", "my-property")] + [InlineData("PropertyName", "property-name")] + [InlineData("ABC", "a-b-c")] + [InlineData("HTMLParser", "h-t-m-l-parser")] + [InlineData("A", "a")] + [InlineData("", "")] + public static void KebabCaseLower_ConvertName(string input, string expected) + { + string result = JsonNamingPolicy.KebabCaseLower.ConvertName(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("MyProperty", "MY-PROPERTY")] + [InlineData("PropertyName", "PROPERTY-NAME")] + [InlineData("ABC", "A-B-C")] + [InlineData("HTMLParser", "H-T-M-L-PARSER")] + [InlineData("A", "A")] + [InlineData("", "")] + public static void KebabCaseUpper_ConvertName(string input, string expected) + { + string result = JsonNamingPolicy.KebabCaseUpper.ConvertName(input); + Assert.Equal(expected, result); + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs index a3a40e4f1d3228..368e05c478dd39 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs @@ -897,5 +897,41 @@ public static void Deserialize_WrongType(string json) { Assert.Throws(() => JsonSerializer.Deserialize(json)); } + + [Fact] + public static void DeepEquals_WithNestedArrays() + { + JsonArray array1 = new JsonArray { 1, 2, new JsonArray { 3, 4 } }; + JsonArray array2 = new JsonArray { 1, 2, new JsonArray { 3, 4 } }; + JsonArray array3 = new JsonArray { 1, 2, new JsonArray { 3, 5 } }; + + Assert.True(JsonNode.DeepEquals(array1, array2)); + Assert.False(JsonNode.DeepEquals(array1, array3)); + Assert.False(JsonNode.DeepEquals(array1, null)); + } + + [Fact] + public static void DeepEquals_DifferentLengths() + { + JsonArray array1 = new JsonArray { 1, 2, 3 }; + JsonArray array2 = new JsonArray { 1, 2 }; + + Assert.False(JsonNode.DeepEquals(array1, array2)); + } + + [Fact] + public static void Clear_RemovesAllItemsAndDetachesParent() + { + JsonArray array = new JsonArray { 1, 2, 3 }; + JsonNode item = array[0]; + + Assert.Equal(3, array.Count); + Assert.Same(array, item.Parent); + + array.Clear(); + + Assert.Equal(0, array.Count); + Assert.Null(item.Parent); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonNodeTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonNodeTests.cs index 537f69c7696103..0246369a1056eb 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonNodeTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonNodeTests.cs @@ -303,5 +303,41 @@ public static void DeepEquals_NotEqualValuesReturnFalse(string value1, string va AssertNotDeepEqual(obj1, obj2); } + + [Fact] + public static void JsonNode_ReplaceWith() + { + JsonObject obj = new JsonObject(); + obj["name"] = "test"; + JsonNode node = obj["name"]; + + node.ReplaceWith(JsonValue.Create("replaced")); + Assert.Equal("replaced", obj["name"].GetValue()); + } + + [Fact] + public static void JsonArray_Replace() + { + JsonArray arr = new JsonArray(1, 2, 3); + arr[1] = JsonValue.Create(99); + Assert.Equal(99, arr[1].GetValue()); + } + + [Fact] + public static void JsonObject_SetProperty() + { + JsonObject obj = new JsonObject(); + obj["key"] = "value"; + obj["key"] = "newValue"; + Assert.Equal("newValue", obj["key"].GetValue()); + } + + [Fact] + public static void JsonValue_AsValue() + { + JsonNode node = JsonValue.Create(42); + JsonValue value = node.AsValue(); + Assert.Equal(42, value.GetValue()); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs index 6787c1a6f95b28..14fdc680ab37a6 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs @@ -1810,5 +1810,46 @@ public static void GetPath_IsThreadSafe() }); } } + + [Fact] + public static void DeepEquals_WithNestedObjects() + { + JsonObject obj1 = new JsonObject { ["a"] = 1, ["b"] = new JsonObject { ["c"] = 2 } }; + JsonObject obj2 = new JsonObject { ["a"] = 1, ["b"] = new JsonObject { ["c"] = 2 } }; + JsonObject obj3 = new JsonObject { ["a"] = 1, ["b"] = new JsonObject { ["c"] = 3 } }; + + Assert.True(JsonNode.DeepEquals(obj1, obj2)); + Assert.False(JsonNode.DeepEquals(obj1, obj3)); + Assert.False(JsonNode.DeepEquals(obj1, null)); + } + + [Fact] + public static void DeepEquals_DifferentKeys() + { + JsonObject obj1 = new JsonObject { ["a"] = 1, ["b"] = 2 }; + JsonObject obj2 = new JsonObject { ["a"] = 1, ["c"] = 2 }; + + Assert.False(JsonNode.DeepEquals(obj1, obj2)); + } + + [Fact] + public static void TryGetPropertyValue_NonExistentProperty() + { + JsonObject obj = new JsonObject { ["a"] = 1 }; + + bool result = obj.TryGetPropertyValue("b", out JsonNode value); + + Assert.False(result); + Assert.Null(value); + } + + [Fact] + public static void ContainsKey_ChecksForProperty() + { + JsonObject obj = new JsonObject { ["a"] = 1, ["b"] = 2 }; + + Assert.True(obj.ContainsKey("a")); + Assert.False(obj.ContainsKey("c")); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs index 83a51a3b3a7aaa..52327863a672d3 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs @@ -909,5 +909,60 @@ private class ExcludeType_TypeInfoResolver(Type excludeType) : IJsonTypeInfoReso } private record DummyClass; + + [Fact] + public static void JsonValue_CreateFromInt() + { + JsonValue value = JsonValue.Create(42); + Assert.Equal(42, value.GetValue()); + Assert.True(value.TryGetValue(out int result)); + Assert.Equal(42, result); + } + + [Fact] + public static void JsonValue_CreateFromString() + { + JsonValue value = JsonValue.Create("test"); + Assert.Equal("test", value.GetValue()); + Assert.True(value.TryGetValue(out string result)); + Assert.Equal("test", result); + } + + [Fact] + public static void JsonValue_CreateFromBool() + { + JsonValue value = JsonValue.Create(true); + Assert.True(value.GetValue()); + Assert.True(value.TryGetValue(out bool result)); + Assert.True(result); + } + + [Fact] + public static void JsonValue_CreateFromDouble() + { + JsonValue value = JsonValue.Create(3.14); + Assert.Equal(3.14, value.GetValue()); + Assert.True(value.TryGetValue(out double result)); + Assert.Equal(3.14, result); + } + + [Fact] + public static void JsonValue_CreateWithJsonTypeInfo() + { + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(int)); + + JsonValue value = JsonValue.Create(42, typeInfo); + Assert.Equal(42, value.GetValue()); + } + + [Fact] + public static void JsonValue_CreateWithJsonTypeInfoAndOptions() + { + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(string)); + var nodeOptions = new JsonNodeOptions { PropertyNameCaseInsensitive = true }; + + JsonValue value = JsonValue.Create("test", typeInfo, nodeOptions); + Assert.Equal("test", value.GetValue()); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Array.ReadTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Array.ReadTests.cs index d06465de159395..2ad742a7f819d1 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Array.ReadTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Array.ReadTests.cs @@ -689,5 +689,29 @@ public IEnumerable NetworkCodeList set => Networks = (value != null) ? string.Join(",", value) : string.Empty; } } + + [Fact] + public static void ReadLargeArray() + { + int[] largeArray = Enumerable.Range(0, 10000).ToArray(); + string json = JsonSerializer.Serialize(largeArray); + int[] deserialized = JsonSerializer.Deserialize(json); + + Assert.Equal(largeArray.Length, deserialized.Length); + Assert.Equal(largeArray[0], deserialized[0]); + Assert.Equal(largeArray[9999], deserialized[9999]); + } + + [Fact] + public static void ReadNestedArrays() + { + int[][] nested = new int[][] { new[] { 1, 2 }, new[] { 3, 4, 5 } }; + string json = JsonSerializer.Serialize(nested); + int[][] deserialized = JsonSerializer.Deserialize(json); + + Assert.Equal(2, deserialized.Length); + Assert.Equal(2, deserialized[0].Length); + Assert.Equal(3, deserialized[1].Length); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DomTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DomTests.cs index d107b32a51838c..bf11f1cbf8b875 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DomTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DomTests.cs @@ -1,9 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.IO; +using System.IO.Pipelines; using System.Linq; using System.Runtime.InteropServices; +using System.Text; using System.Text.Json.Nodes; +using System.Text.Json.Serialization.Metadata; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -13,10 +18,15 @@ namespace System.Text.Json.Serialization.Tests /// /// The test class provides tests for the JsonTypeInfo and JsonContext permutations. /// The test class provides tests for input validation. - public static class DomTests + public static partial class DomTests { private const string Escaped_PlusSign = "\"\\u002B\""; // A '+' sign is escaped as hex. + [JsonSerializable(typeof(MyPoco))] + private partial class MyPocoContext : JsonSerializerContext + { + } + private class MyPoco { public static MyPoco Create() @@ -239,5 +249,520 @@ public static void SerializeToNode_WithEscaping() string json = dom.Deserialize(); Assert.Equal("+", json); } + + [Fact] + public static void SerializeToDocument_WithJsonTypeInfo() + { + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = MyPoco.Create(); + using JsonDocument dom = JsonSerializer.SerializeToDocument(obj, typeInfo); + + JsonElement stringProp = dom.RootElement.GetProperty("StringProp"); + Assert.Equal(JsonValueKind.String, stringProp.ValueKind); + Assert.Equal("Hello", stringProp.ToString()); + } + + [Fact] + public static void SerializeToDocument_WithJsonTypeInfo_NonGeneric() + { + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = MyPoco.Create(); + using JsonDocument dom = JsonSerializer.SerializeToDocument(obj, typeInfo); + + JsonElement stringProp = dom.RootElement.GetProperty("StringProp"); + Assert.Equal(JsonValueKind.String, stringProp.ValueKind); + Assert.Equal("Hello", stringProp.ToString()); + } + + [Fact] + public static void SerializeToElement_WithJsonTypeInfo() + { + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = MyPoco.Create(); + JsonElement element = JsonSerializer.SerializeToElement(obj, typeInfo); + + JsonElement stringProp = element.GetProperty("StringProp"); + Assert.Equal(JsonValueKind.String, stringProp.ValueKind); + Assert.Equal("Hello", stringProp.ToString()); + } + + [Fact] + public static void SerializeToElement_WithJsonTypeInfo_NonGeneric() + { + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = MyPoco.Create(); + JsonElement element = JsonSerializer.SerializeToElement(obj, typeInfo); + + JsonElement stringProp = element.GetProperty("StringProp"); + Assert.Equal(JsonValueKind.String, stringProp.ValueKind); + Assert.Equal("Hello", stringProp.ToString()); + } + + [Fact] + public static void SerializeToNode_WithJsonTypeInfo() + { + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = MyPoco.Create(); + JsonNode node = JsonSerializer.SerializeToNode(obj, typeInfo); + + Assert.NotNull(node); + Assert.Equal("Hello", node["StringProp"]?.GetValue()); + } + + [Fact] + public static void SerializeToNode_WithJsonTypeInfo_NonGeneric() + { + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = MyPoco.Create(); + JsonNode node = JsonSerializer.SerializeToNode(obj, typeInfo); + + Assert.NotNull(node); + Assert.Equal("Hello", node["StringProp"]?.GetValue()); + } + + [Fact] + public static void DeserializeFromSpan_WithJsonTypeInfo() + { + ReadOnlySpan utf8Json = """{"StringProp":"Hello","IntArrayProp":[1,2]}"""u8; + + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = JsonSerializer.Deserialize(utf8Json, typeInfo); + obj.Verify(); + } + + [Fact] + public static void DeserializeFromSpan_WithJsonTypeInfo_NonGeneric() + { + ReadOnlySpan utf8Json = """{"StringProp":"Hello","IntArrayProp":[1,2]}"""u8; + + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + object obj = JsonSerializer.Deserialize(utf8Json, typeInfo); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void SerializeToDocument_NullValue_WithJsonTypeInfo_NonGeneric() + { + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + using JsonDocument dom = JsonSerializer.SerializeToDocument((object)null, typeInfo); + Assert.Equal(JsonValueKind.Null, dom.RootElement.ValueKind); + } + + [Fact] + public static void SerializeToElement_NullValue_WithJsonTypeInfo_NonGeneric() + { + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + JsonElement element = JsonSerializer.SerializeToElement((object)null, typeInfo); + Assert.Equal(JsonValueKind.Null, element.ValueKind); + } + + [Fact] + public static void SerializeToNode_NullValue_WithJsonTypeInfo_NonGeneric() + { + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + JsonNode node = JsonSerializer.SerializeToNode((object)null, typeInfo); + Assert.Null(node); + } + + [Fact] + public static void DeserializeFromSpan_NullValue() + { + ReadOnlySpan utf8Json = "null"u8; + + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = JsonSerializer.Deserialize(utf8Json, typeInfo); + Assert.Null(obj); + } + + [Fact] + public static void DeserializeFromJsonDocument_WithJsonTypeInfo() + { + using JsonDocument doc = JsonDocument.Parse(Json); + + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = doc.Deserialize(typeInfo); + obj.Verify(); + } + + [Fact] + public static void DeserializeFromJsonDocument_WithJsonTypeInfo_NonGeneric() + { + using JsonDocument doc = JsonDocument.Parse(Json); + + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + object obj = doc.Deserialize(typeInfo); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void DeserializeFromJsonElement_WithJsonTypeInfo() + { + using JsonDocument doc = JsonDocument.Parse(Json); + JsonElement element = doc.RootElement; + + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = element.Deserialize(typeInfo); + obj.Verify(); + } + + [Fact] + public static void DeserializeFromJsonElement_WithJsonTypeInfo_NonGeneric() + { + using JsonDocument doc = JsonDocument.Parse(Json); + JsonElement element = doc.RootElement; + + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + object obj = element.Deserialize(typeInfo); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void DeserializeFromJsonNode_WithJsonTypeInfo() + { + JsonNode node = JsonNode.Parse(Json); + + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = node.Deserialize(typeInfo); + obj.Verify(); + } + + [Fact] + public static void DeserializeFromJsonNode_WithJsonTypeInfo_NonGeneric() + { + JsonNode node = JsonNode.Parse(Json); + + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + object obj = node.Deserialize(typeInfo); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void DeserializeFromCharSpan_WithJsonTypeInfo() + { + ReadOnlySpan jsonChars = Json.AsSpan(); + + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = JsonSerializer.Deserialize(jsonChars, typeInfo); + obj.Verify(); + } + + [Fact] + public static void DeserializeFromCharSpan_WithJsonTypeInfo_NonGeneric() + { + ReadOnlySpan jsonChars = Json.AsSpan(); + + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + object obj = JsonSerializer.Deserialize(jsonChars, typeInfo); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void SerializeToUtf8Bytes_WithJsonTypeInfo_NonGeneric() + { + MyPoco obj = MyPoco.Create(); + + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(obj, typeInfo); + string json = Encoding.UTF8.GetString(bytes); + Assert.Contains("Hello", json); + } + + [Fact] + public static void SerializeToStream_WithJsonTypeInfo_NonGeneric() + { + MyPoco obj = MyPoco.Create(); + + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + using MemoryStream stream = new(); + JsonSerializer.Serialize(stream, obj, typeInfo); + + stream.Position = 0; + string json = new StreamReader(stream).ReadToEnd(); + Assert.Contains("Hello", json); + } + + [Fact] + public static void SerializeToUtf8JsonWriter_WithJsonTypeInfo_NonGeneric() + { + MyPoco obj = MyPoco.Create(); + + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + using MemoryStream stream = new(); + using (Utf8JsonWriter writer = new(stream)) + { + JsonSerializer.Serialize(writer, obj, typeInfo); + } + + string json = Encoding.UTF8.GetString(stream.ToArray()); + Assert.Contains("Hello", json); + } + + [Fact] + public static void DeserializeFromUtf8JsonReader_WithJsonTypeInfo() + { + byte[] utf8Json = Encoding.UTF8.GetBytes(Json); + Utf8JsonReader reader = new(utf8Json); + + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = JsonSerializer.Deserialize(ref reader, typeInfo); + obj.Verify(); + } + + [Fact] + public static void DeserializeFromUtf8JsonReader_WithJsonTypeInfo_NonGeneric() + { + byte[] utf8Json = Encoding.UTF8.GetBytes(Json); + Utf8JsonReader reader = new(utf8Json); + + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + object obj = JsonSerializer.Deserialize(ref reader, typeInfo); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void DeserializeFromStream_WithJsonTypeInfo() + { + using MemoryStream stream = new(Encoding.UTF8.GetBytes(Json)); + + JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + MyPoco obj = JsonSerializer.Deserialize(stream, typeInfo); + obj.Verify(); + } + + [Fact] + public static void DeserializeFromStream_WithJsonTypeInfo_NonGeneric() + { + using MemoryStream stream = new(Encoding.UTF8.GetBytes(Json)); + + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + object obj = JsonSerializer.Deserialize(stream, typeInfo); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void SerializeToDocument_WithJsonSerializerContext() + { + MyPoco obj = MyPoco.Create(); + + using JsonDocument dom = JsonSerializer.SerializeToDocument(obj, typeof(MyPoco), MyPocoContext.Default); + + JsonElement stringProp = dom.RootElement.GetProperty("StringProp"); + Assert.Equal(JsonValueKind.String, stringProp.ValueKind); + Assert.Equal("Hello", stringProp.ToString()); + } + + [Fact] + public static void SerializeToElement_WithJsonSerializerContext() + { + MyPoco obj = MyPoco.Create(); + + JsonElement element = JsonSerializer.SerializeToElement(obj, typeof(MyPoco), MyPocoContext.Default); + + JsonElement stringProp = element.GetProperty("StringProp"); + Assert.Equal(JsonValueKind.String, stringProp.ValueKind); + Assert.Equal("Hello", stringProp.ToString()); + } + + [Fact] + public static void SerializeToNode_WithJsonSerializerContext() + { + MyPoco obj = MyPoco.Create(); + + JsonNode node = JsonSerializer.SerializeToNode(obj, typeof(MyPoco), MyPocoContext.Default); + + Assert.NotNull(node); + Assert.Equal("Hello", node["StringProp"]?.GetValue()); + } + + [Fact] + public static void DeserializeFromSpan_WithJsonSerializerContext() + { + ReadOnlySpan utf8Json = """{"StringProp":"Hello","IntArrayProp":[1,2]}"""u8; + + object obj = JsonSerializer.Deserialize(utf8Json, typeof(MyPoco), MyPocoContext.Default); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void DeserializeFromCharSpan_WithJsonSerializerContext() + { + ReadOnlySpan jsonChars = Json.AsSpan(); + + object obj = JsonSerializer.Deserialize(jsonChars, typeof(MyPoco), MyPocoContext.Default); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void DeserializeFromJsonDocument_WithJsonSerializerContext() + { + using JsonDocument doc = JsonDocument.Parse(Json); + + object obj = doc.Deserialize(typeof(MyPoco), MyPocoContext.Default); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void DeserializeFromJsonElement_WithJsonSerializerContext() + { + using JsonDocument doc = JsonDocument.Parse(Json); + JsonElement element = doc.RootElement; + + object obj = element.Deserialize(typeof(MyPoco), MyPocoContext.Default); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void DeserializeFromJsonNode_WithJsonSerializerContext() + { + JsonNode node = JsonNode.Parse(Json); + + object obj = node.Deserialize(typeof(MyPoco), MyPocoContext.Default); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void DeserializeFromUtf8JsonReader_WithJsonSerializerContext() + { + byte[] utf8Json = Encoding.UTF8.GetBytes(Json); + Utf8JsonReader reader = new(utf8Json); + + object obj = JsonSerializer.Deserialize(ref reader, typeof(MyPoco), MyPocoContext.Default); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void DeserializeFromStream_WithJsonSerializerContext() + { + using MemoryStream stream = new(Encoding.UTF8.GetBytes(Json)); + + object obj = JsonSerializer.Deserialize(stream, typeof(MyPoco), MyPocoContext.Default); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } + + [Fact] + public static void SerializeToUtf8Bytes_WithJsonSerializerContext() + { + MyPoco obj = MyPoco.Create(); + + byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(obj, typeof(MyPoco), MyPocoContext.Default); + string json = Encoding.UTF8.GetString(bytes); + Assert.Contains("Hello", json); + } + + [Fact] + public static void SerializeToStream_WithJsonSerializerContext() + { + MyPoco obj = MyPoco.Create(); + + using MemoryStream stream = new(); + JsonSerializer.Serialize(stream, obj, typeof(MyPoco), MyPocoContext.Default); + + stream.Position = 0; + string json = new StreamReader(stream).ReadToEnd(); + Assert.Contains("Hello", json); + } + + [Fact] + public static void SerializeToUtf8JsonWriter_WithJsonSerializerContext() + { + MyPoco obj = MyPoco.Create(); + + using MemoryStream stream = new(); + using (Utf8JsonWriter writer = new(stream)) + { + JsonSerializer.Serialize(writer, obj, typeof(MyPoco), MyPocoContext.Default); + } + + string json = Encoding.UTF8.GetString(stream.ToArray()); + Assert.Contains("Hello", json); + } + + [Fact] + public static async Task SerializeAsyncToPipeWriter_WithJsonTypeInfo() + { + MyPoco obj = MyPoco.Create(); + + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + + Pipe pipe = new(); + await JsonSerializer.SerializeAsync(pipe.Writer, obj, typeInfo); + await pipe.Writer.CompleteAsync(); + + ReadResult result = await pipe.Reader.ReadAsync(); + string json = Encoding.UTF8.GetString(result.Buffer.First.Span.ToArray()); + pipe.Reader.Complete(); + + Assert.Contains("Hello", json); + } + + [Fact] + public static async Task SerializeAsyncToPipeWriter_WithJsonSerializerContext() + { + MyPoco obj = MyPoco.Create(); + + Pipe pipe = new(); + await JsonSerializer.SerializeAsync(pipe.Writer, obj, typeof(MyPoco), MyPocoContext.Default); + await pipe.Writer.CompleteAsync(); + + ReadResult result = await pipe.Reader.ReadAsync(); + string json = Encoding.UTF8.GetString(result.Buffer.First.Span.ToArray()); + pipe.Reader.Complete(); + + Assert.Contains("Hello", json); + } + + [Fact] + public static async Task DeserializeAsyncFromPipeReader_WithJsonSerializerContext() + { + Pipe pipe = new(); + byte[] utf8Json = Encoding.UTF8.GetBytes(Json); + await pipe.Writer.WriteAsync(utf8Json); + await pipe.Writer.CompleteAsync(); + + object obj = await JsonSerializer.DeserializeAsync(pipe.Reader, typeof(MyPoco), MyPocoContext.Default); + Assert.IsType(obj); + ((MyPoco)obj).Verify(); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonElementTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonElementTests.cs index 4064301320eaef..19d8487caa6ad0 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonElementTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonElementTests.cs @@ -234,6 +234,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() { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs index 402ae6c8117d96..7fa0c4920cc103 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs @@ -1952,5 +1952,72 @@ public static void AllowDuplicateProperties_RespectsSetting() Assert.Single(result); Assert.Equal(2, result["a"]); } + + [Fact] + public static void PreferredObjectCreationHandling_CanBeSet() + { + var options = new JsonSerializerOptions(); + Assert.Equal(JsonObjectCreationHandling.Replace, options.PreferredObjectCreationHandling); + + options.PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate; + Assert.Equal(JsonObjectCreationHandling.Populate, options.PreferredObjectCreationHandling); + + options.TypeInfoResolver = new DefaultJsonTypeInfoResolver(); + options.MakeReadOnly(); + Assert.Throws(() => options.PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace); + } + + [Fact] + public static void PreferredObjectCreationHandling_InvalidValue_ThrowsArgumentOutOfRangeException() + { + var options = new JsonSerializerOptions(); + Assert.Throws(() => options.PreferredObjectCreationHandling = (JsonObjectCreationHandling)999); + } + + [Fact] + public static void TypeInfoResolverChain_CanAddMultipleResolvers() + { + var options = new JsonSerializerOptions(); + var resolver1 = new DefaultJsonTypeInfoResolver(); + var resolver2 = new DefaultJsonTypeInfoResolver(); + + options.TypeInfoResolverChain.Add(resolver1); + options.TypeInfoResolverChain.Add(resolver2); + + Assert.Equal(2, options.TypeInfoResolverChain.Count); + Assert.Same(resolver1, options.TypeInfoResolverChain[0]); + Assert.Same(resolver2, options.TypeInfoResolverChain[1]); + } + + [Fact] + public static void TypeInfoResolverChain_ToString_ReturnsChainDescription() + { + var options = new JsonSerializerOptions(); + options.TypeInfoResolverChain.Add(new DefaultJsonTypeInfoResolver()); + + string result = options.TypeInfoResolver.ToString(); + Assert.NotNull(result); + Assert.NotEmpty(result); + } + + [Fact] + public static void JsonSerializerOptions_WriteIndented() + { + var options = new JsonSerializerOptions { WriteIndented = true }; + Assert.True(options.WriteIndented); + + string json = JsonSerializer.Serialize(new { name = "test" }, options); + Assert.Contains("\n", json); + } + + [Fact] + public static void JsonSerializerOptions_DefaultBufferSize() + { + var options = new JsonSerializerOptions(); + Assert.Equal(16384, options.DefaultBufferSize); + + options.DefaultBufferSize = 8192; + Assert.Equal(8192, options.DefaultBufferSize); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs index cf65451ec16a8c..0eb2dc95a9ce66 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs @@ -265,6 +265,251 @@ public static void ValidStringWithinInvalidJsonVariousSegmentSizes(string input, } } + [Fact] + public static void ReadComplexNestedJsonMultiSegment() + { + string jsonString = """{"array":[1,2,{"nested":"value","number":42.5e10}],"bool":true,"null":null}"""; + byte[] utf8 = Encoding.UTF8.GetBytes(jsonString); + + for (int splitLocation = 1; splitLocation < utf8.Length; splitLocation++) + { + ReadOnlySequence sequence = JsonTestHelper.CreateSegments(utf8, splitLocation); + var jsonReader = new Utf8JsonReader(sequence); + + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.StartObject, jsonReader.TokenType); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.PropertyName, jsonReader.TokenType); + Assert.Equal("array", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.StartArray, jsonReader.TokenType); + Assert.True(jsonReader.Read()); + Assert.Equal(1, jsonReader.GetInt32()); + Assert.True(jsonReader.Read()); + Assert.Equal(2, jsonReader.GetInt32()); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.StartObject, jsonReader.TokenType); + Assert.True(jsonReader.Read()); + Assert.Equal("nested", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.Equal("value", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.Equal("number", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.Equal(42.5e10, jsonReader.GetDouble()); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.EndObject, jsonReader.TokenType); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.EndArray, jsonReader.TokenType); + Assert.True(jsonReader.Read()); + Assert.Equal("bool", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.True(jsonReader.GetBoolean()); + Assert.True(jsonReader.Read()); + Assert.Equal("null", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.Null, jsonReader.TokenType); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.EndObject, jsonReader.TokenType); + Assert.False(jsonReader.Read()); + } + } + + [Fact] + public static void ReadLongStringAcrossSegments() + { + string longString = new string('a', 1000); + string jsonString = $$"""{"key":"{{longString}}"}"""; + byte[] utf8 = Encoding.UTF8.GetBytes(jsonString); + + for (int segmentSize = 10; segmentSize < 100; segmentSize += 10) + { + ReadOnlySequence sequence = JsonTestHelper.GetSequence(utf8, segmentSize); + var jsonReader = new Utf8JsonReader(sequence); + + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.StartObject, jsonReader.TokenType); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.PropertyName, jsonReader.TokenType); + Assert.Equal("key", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.String, jsonReader.TokenType); + Assert.Equal(longString, jsonReader.GetString()); + Assert.True(jsonReader.HasValueSequence); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.EndObject, jsonReader.TokenType); + Assert.False(jsonReader.Read()); + } + } + + [Fact] + public static void ReadEscapedStringAcrossSegmentBoundary() + { + string jsonString = """{"key":"value\twith\nescape\u0041sequences"}"""; + byte[] utf8 = Encoding.UTF8.GetBytes(jsonString); + + for (int splitLocation = 1; splitLocation < utf8.Length; splitLocation++) + { + ReadOnlySequence sequence = JsonTestHelper.CreateSegments(utf8, splitLocation); + var jsonReader = new Utf8JsonReader(sequence); + + Assert.True(jsonReader.Read()); + Assert.True(jsonReader.Read()); + Assert.Equal("key", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.Equal("value\twith\nescape\u0041sequences", jsonReader.GetString()); + Assert.True(jsonReader.ValueIsEscaped); + } + } + + [Fact] + public static void ReadNumberWithExponentAcrossSegments() + { + string jsonString = """[1.23456789e+100,-9.87654321e-50,1e308]"""; + byte[] utf8 = Encoding.UTF8.GetBytes(jsonString); + + for (int splitLocation = 1; splitLocation < utf8.Length; splitLocation++) + { + ReadOnlySequence sequence = JsonTestHelper.CreateSegments(utf8, splitLocation); + var jsonReader = new Utf8JsonReader(sequence); + + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.StartArray, jsonReader.TokenType); + Assert.True(jsonReader.Read()); + Assert.Equal(1.23456789e+100, jsonReader.GetDouble()); + Assert.True(jsonReader.Read()); + Assert.Equal(-9.87654321e-50, jsonReader.GetDouble()); + Assert.True(jsonReader.Read()); + Assert.Equal(1e308, jsonReader.GetDouble()); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.EndArray, jsonReader.TokenType); + Assert.False(jsonReader.Read()); + } + } + + [Fact] + public static void ReadLiteralsAcrossSegmentBoundaries() + { + string jsonString = """{"t":true,"f":false,"n":null}"""; + byte[] utf8 = Encoding.UTF8.GetBytes(jsonString); + + for (int splitLocation = 1; splitLocation < utf8.Length; splitLocation++) + { + ReadOnlySequence sequence = JsonTestHelper.CreateSegments(utf8, splitLocation); + var jsonReader = new Utf8JsonReader(sequence); + + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.StartObject, jsonReader.TokenType); + Assert.True(jsonReader.Read()); + Assert.Equal("t", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.True(jsonReader.GetBoolean()); + Assert.True(jsonReader.Read()); + Assert.Equal("f", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.False(jsonReader.GetBoolean()); + Assert.True(jsonReader.Read()); + Assert.Equal("n", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.Null, jsonReader.TokenType); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.EndObject, jsonReader.TokenType); + Assert.False(jsonReader.Read()); + } + } + + [Theory] + [InlineData(2)] + [InlineData(5)] + [InlineData(10)] + [InlineData(20)] + public static void ReadDeeplyNestedArraysMultiSegment(int depth) + { + string json = new string('[', depth) + "1" + new string(']', depth); + byte[] utf8 = Encoding.UTF8.GetBytes(json); + + ReadOnlySequence sequence = JsonTestHelper.GetSequence(utf8, 3); + var jsonReader = new Utf8JsonReader(sequence); + + for (int i = 0; i < depth; i++) + { + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.StartArray, jsonReader.TokenType); + Assert.Equal(i, jsonReader.CurrentDepth); + } + + Assert.True(jsonReader.Read()); + Assert.Equal(1, jsonReader.GetInt32()); + Assert.Equal(depth, jsonReader.CurrentDepth); + + for (int i = depth - 1; i >= 0; i--) + { + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.EndArray, jsonReader.TokenType); + Assert.Equal(i, jsonReader.CurrentDepth); + } + + Assert.False(jsonReader.Read()); + } + + [Fact] + public static void ReadCommentsAcrossSegments() + { + string jsonString = """ + { + // single line comment + "key": /* multi + line + comment */ "value" + } + """; + byte[] utf8 = Encoding.UTF8.GetBytes(jsonString); + var options = new JsonReaderOptions { CommentHandling = JsonCommentHandling.Skip }; + + for (int splitLocation = 1; splitLocation < utf8.Length; splitLocation++) + { + ReadOnlySequence sequence = JsonTestHelper.CreateSegments(utf8, splitLocation); + var jsonReader = new Utf8JsonReader(sequence, options); + + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.StartObject, jsonReader.TokenType); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.PropertyName, jsonReader.TokenType); + Assert.Equal("key", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.String, jsonReader.TokenType); + Assert.Equal("value", jsonReader.GetString()); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.EndObject, jsonReader.TokenType); + Assert.False(jsonReader.Read()); + } + } + + [Fact] + public static void ReadPropertyNameAcrossSegmentWithEscaping() + { + string jsonString = """{"prop\u0065rty":123}"""; + byte[] utf8 = Encoding.UTF8.GetBytes(jsonString); + + for (int splitLocation = 1; splitLocation < utf8.Length; splitLocation++) + { + ReadOnlySequence sequence = JsonTestHelper.CreateSegments(utf8, splitLocation); + var jsonReader = new Utf8JsonReader(sequence); + + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.StartObject, jsonReader.TokenType); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.PropertyName, jsonReader.TokenType); + Assert.Equal("property", jsonReader.GetString()); + Assert.True(jsonReader.ValueIsEscaped); + Assert.True(jsonReader.Read()); + Assert.Equal(123, jsonReader.GetInt32()); + Assert.True(jsonReader.Read()); + Assert.Equal(JsonTokenType.EndObject, jsonReader.TokenType); + Assert.False(jsonReader.Read()); + } + } + private static void ValidReadStringHelper(ref Utf8JsonReader jsonReader, int expectedBytePositionInLine, int expectedConsumed, string expectedStr, int expectedTokenLength) { Assert.True(jsonReader.Read()); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.cs index 6195032db8082b..6007da596755d4 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.cs @@ -1527,5 +1527,132 @@ static void Test(string testString, bool isFinalBlock) Test(testString, isFinalBlock: true); Test(testString, isFinalBlock: false); } + + [Fact] + public static void TestNumericEdgeCases_Get_Methods() + { + byte[] data1 = Encoding.UTF8.GetBytes("0"); + var reader1 = new Utf8JsonReader(data1, isFinalBlock: true, state: default); + Assert.True(reader1.Read()); + Assert.Equal(0, reader1.GetByte()); + + byte[] data2 = Encoding.UTF8.GetBytes("255"); + var reader2 = new Utf8JsonReader(data2, isFinalBlock: true, state: default); + Assert.True(reader2.Read()); + Assert.Equal(255, reader2.GetByte()); + + byte[] data3 = Encoding.UTF8.GetBytes("-128"); + var reader3 = new Utf8JsonReader(data3, isFinalBlock: true, state: default); + Assert.True(reader3.Read()); + Assert.Equal(-128, reader3.GetSByte()); + + byte[] data4 = Encoding.UTF8.GetBytes("127"); + var reader4 = new Utf8JsonReader(data4, isFinalBlock: true, state: default); + Assert.True(reader4.Read()); + Assert.Equal(127, reader4.GetSByte()); + + byte[] data5 = Encoding.UTF8.GetBytes("-32768"); + var reader5 = new Utf8JsonReader(data5, isFinalBlock: true, state: default); + Assert.True(reader5.Read()); + Assert.Equal(-32768, reader5.GetInt16()); + + byte[] data6 = Encoding.UTF8.GetBytes("32767"); + var reader6 = new Utf8JsonReader(data6, isFinalBlock: true, state: default); + Assert.True(reader6.Read()); + Assert.Equal(32767, reader6.GetInt16()); + + byte[] data7 = Encoding.UTF8.GetBytes("-2147483648"); + var reader7 = new Utf8JsonReader(data7, isFinalBlock: true, state: default); + Assert.True(reader7.Read()); + Assert.Equal(-2147483648, reader7.GetInt32()); + + byte[] data8 = Encoding.UTF8.GetBytes("2147483647"); + var reader8 = new Utf8JsonReader(data8, isFinalBlock: true, state: default); + Assert.True(reader8.Read()); + Assert.Equal(2147483647, reader8.GetInt32()); + + byte[] data9 = Encoding.UTF8.GetBytes("-9223372036854775808"); + var reader9 = new Utf8JsonReader(data9, isFinalBlock: true, state: default); + Assert.True(reader9.Read()); + Assert.Equal(-9223372036854775808, reader9.GetInt64()); + + byte[] data10 = Encoding.UTF8.GetBytes("9223372036854775807"); + var reader10 = new Utf8JsonReader(data10, isFinalBlock: true, state: default); + Assert.True(reader10.Read()); + Assert.Equal(9223372036854775807, reader10.GetInt64()); + + byte[] data11 = Encoding.UTF8.GetBytes("0"); + var reader11 = new Utf8JsonReader(data11, isFinalBlock: true, state: default); + Assert.True(reader11.Read()); + Assert.Equal(0, reader11.GetUInt16()); + + byte[] data12 = Encoding.UTF8.GetBytes("65535"); + var reader12 = new Utf8JsonReader(data12, isFinalBlock: true, state: default); + Assert.True(reader12.Read()); + Assert.Equal(65535, reader12.GetUInt16()); + + byte[] data13 = Encoding.UTF8.GetBytes("0"); + var reader13 = new Utf8JsonReader(data13, isFinalBlock: true, state: default); + Assert.True(reader13.Read()); + Assert.Equal(0u, reader13.GetUInt32()); + + byte[] data14 = Encoding.UTF8.GetBytes("4294967295"); + var reader14 = new Utf8JsonReader(data14, isFinalBlock: true, state: default); + Assert.True(reader14.Read()); + Assert.Equal(4294967295u, reader14.GetUInt32()); + + byte[] data15 = Encoding.UTF8.GetBytes("0"); + var reader15 = new Utf8JsonReader(data15, isFinalBlock: true, state: default); + Assert.True(reader15.Read()); + Assert.Equal(0ul, reader15.GetUInt64()); + + byte[] data16 = Encoding.UTF8.GetBytes("18446744073709551615"); + var reader16 = new Utf8JsonReader(data16, isFinalBlock: true, state: default); + Assert.True(reader16.Read()); + Assert.Equal(18446744073709551615ul, reader16.GetUInt64()); + + byte[] data17 = Encoding.UTF8.GetBytes("0"); + var reader17 = new Utf8JsonReader(data17, isFinalBlock: true, state: default); + Assert.True(reader17.Read()); + Assert.Equal(0m, reader17.GetDecimal()); + } + + [Fact] + public static void TestFloatingPointEdgeCases_Get_Methods() + { + byte[] data1 = Encoding.UTF8.GetBytes("0.0"); + var reader1 = new Utf8JsonReader(data1, isFinalBlock: true, state: default); + Assert.True(reader1.Read()); + Assert.Equal(0.0f, reader1.GetSingle()); + + byte[] data2 = Encoding.UTF8.GetBytes("3.4028235E+38"); + var reader2 = new Utf8JsonReader(data2, isFinalBlock: true, state: default); + Assert.True(reader2.Read()); + float result2 = reader2.GetSingle(); + Assert.True(Math.Abs(result2 - 3.4028235E+38f) < 1E+31f); + + byte[] data3 = Encoding.UTF8.GetBytes("-3.4028235E+38"); + var reader3 = new Utf8JsonReader(data3, isFinalBlock: true, state: default); + Assert.True(reader3.Read()); + float result3 = reader3.GetSingle(); + Assert.True(Math.Abs(result3 - (-3.4028235E+38f)) < 1E+31f); + + byte[] data4 = Encoding.UTF8.GetBytes("0.0"); + var reader4 = new Utf8JsonReader(data4, isFinalBlock: true, state: default); + Assert.True(reader4.Read()); + Assert.Equal(0.0, reader4.GetDouble()); + + byte[] data5 = Encoding.UTF8.GetBytes("1.7976931348623157E+308"); + var reader5 = new Utf8JsonReader(data5, isFinalBlock: true, state: default); + Assert.True(reader5.Read()); + double result5 = reader5.GetDouble(); + Assert.True(Math.Abs(result5 - 1.7976931348623157E+308) < 1E+300); + + byte[] data6 = Encoding.UTF8.GetBytes("-1.7976931348623157E+308"); + var reader6 = new Utf8JsonReader(data6, isFinalBlock: true, state: default); + Assert.True(reader6.Read()); + double result6 = reader6.GetDouble(); + Assert.True(Math.Abs(result6 - (-1.7976931348623157E+308)) < 1E+300); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.cs index c49c65ef291d71..8e3f68618e3eb9 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.cs @@ -4840,5 +4840,39 @@ public static IEnumerable GetCommentUnescapeData return dataList; } } + + [Fact] + public static void SkipComment_SingleLineComment() + { + byte[] data = "// This is a comment\n{}"u8.ToArray(); + var reader = new Utf8JsonReader(data, new JsonReaderOptions { CommentHandling = JsonCommentHandling.Skip }); + + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.StartObject, reader.TokenType); + } + + [Fact] + public static void SkipComment_MultiLineComment() + { + byte[] data = "/* This is a\nmultiline comment */\n{}"u8.ToArray(); + var reader = new Utf8JsonReader(data, new JsonReaderOptions { CommentHandling = JsonCommentHandling.Skip }); + + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.StartObject, reader.TokenType); + } + + [Fact] + public static void AllowComment_ReadsCommentToken() + { + byte[] data = "// comment\n{}"u8.ToArray(); + var reader = new Utf8JsonReader(data, new JsonReaderOptions { CommentHandling = JsonCommentHandling.Allow }); + + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.Comment, reader.TokenType); + Assert.Equal(" comment", reader.GetComment()); + + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.StartObject, reader.TokenType); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs index 5abafb3914fbf1..0707327d171d4e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs @@ -8067,6 +8067,209 @@ from inputValue in others from anotherValue in anothers select new object[] { options, inputValue, anotherValue }; + [Fact] + public static void WritePropertyWithExtremelyLongName_ThrowsArgumentException() + { + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output); + + writer.WriteStartObject(); + + string longName = new string('a', 170_000_000); + + Assert.Throws(() => writer.WritePropertyName(longName.AsSpan())); + } + + [Fact] + public static void WriteValueWithExtremelyLongValue_ThrowsArgumentException() + { + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output); + + writer.WriteStartArray(); + + string longValue = new string('a', 170_000_000); + + Assert.Throws(() => writer.WriteStringValue(longValue.AsSpan())); + } + + [Fact] + public static void WriteRawValueWithInvalidJson_ValidationDisabled() + { + var options = new JsonWriterOptions { SkipValidation = true }; + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output, options); + + writer.WriteStartArray(); + writer.WriteRawValue("invalid json content"u8, skipInputValidation: true); + writer.WriteEndArray(); + writer.Flush(); + + string json = Encoding.UTF8.GetString(output.WrittenSpan.ToArray()); + Assert.Contains("invalid json content", json); + } + + [Fact] + public static void JsonElement_ToString_EdgeCase() + { + using JsonDocument doc = JsonDocument.Parse("null"); + JsonElement element = doc.RootElement; + string result = element.ToString(); + Assert.Equal("", result); + } + + [Fact] + public static void WriteWithMinimizedFormatting() + { + var options = new JsonWriterOptions { Indented = false }; + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output, options); + + writer.WriteStartObject(); + writer.WritePropertyName("name"); + writer.WriteStringValue("test"); + writer.WriteEndObject(); + writer.Flush(); + + string json = Encoding.UTF8.GetString(output.WrittenSpan.ToArray()); + Assert.Equal("""{"name":"test"}""", json); + } + + [Fact] + public static void WriteRawValue_WithValidation() + { + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output); + + writer.WriteStartArray(); + writer.WriteRawValue("42"u8, skipInputValidation: false); + writer.WriteEndArray(); + writer.Flush(); + + string json = Encoding.UTF8.GetString(output.WrittenSpan.ToArray()); + Assert.Equal("[42]", json); + } + + [Fact] + public static void WriteDateTime_MinimizedFormat() + { + var options = new JsonWriterOptions { Indented = false }; + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output, options); + + DateTime dt = new DateTime(2024, 1, 1, 12, 0, 0, DateTimeKind.Utc); + writer.WriteStartObject(); + writer.WriteString("date", dt); + writer.WriteEndObject(); + writer.Flush(); + + string json = Encoding.UTF8.GetString(output.WrittenSpan.ToArray()); + Assert.Contains("2024-01-01", json); + } + + [Fact] + public static void WriteGuid_MinimizedFormat() + { + var options = new JsonWriterOptions { Indented = false }; + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output, options); + + Guid guid = Guid.Parse("12345678-1234-1234-1234-123456789012"); + writer.WriteStartObject(); + writer.WriteString("id", guid); + writer.WriteEndObject(); + writer.Flush(); + + string json = Encoding.UTF8.GetString(output.WrittenSpan.ToArray()); + Assert.Contains("12345678-1234-1234-1234-123456789012", json); + } + + [Fact] + public static void WriteDecimal_MinimizedFormat() + { + var options = new JsonWriterOptions { Indented = false }; + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output, options); + + writer.WriteStartObject(); + writer.WriteNumber("value", 123.456m); + writer.WriteEndObject(); + writer.Flush(); + + string json = Encoding.UTF8.GetString(output.WrittenSpan.ToArray()); + Assert.Contains("123.456", json); + } + + [Fact] + public static void WriteLargeInteger_MinimizedFormat() + { + var options = new JsonWriterOptions { Indented = false }; + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output, options); + + writer.WriteStartObject(); + writer.WriteNumber("value", long.MaxValue); + writer.WriteEndObject(); + writer.Flush(); + + string json = Encoding.UTF8.GetString(output.WrittenSpan.ToArray()); + Assert.Contains("9223372036854775807", json); + } + + [Fact] + public static void WriteBase64String_IndentedFormat() + { + var options = new JsonWriterOptions { Indented = true }; + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output, options); + + byte[] bytes = new byte[] { 1, 2, 3, 4, 5 }; + writer.WriteStartObject(); + writer.WriteBase64String("data", bytes); + writer.WriteEndObject(); + writer.Flush(); + + string json = Encoding.UTF8.GetString(output.WrittenSpan.ToArray()); + Assert.Contains("\"data\": \"AQIDBAU=\"", json); + } + + [Fact] + public static void WriteStartArray_WithPropertyName_Indented() + { + var options = new JsonWriterOptions { Indented = true }; + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output, options); + + writer.WriteStartObject(); + writer.WriteStartArray("items"); + writer.WriteNumberValue(1); + writer.WriteNumberValue(2); + writer.WriteEndArray(); + writer.WriteEndObject(); + writer.Flush(); + + string json = Encoding.UTF8.GetString(output.WrittenSpan.ToArray()); + Assert.Contains("\"items\":", json); + } + + [Fact] + public static void WriteStartObject_WithPropertyName_Indented() + { + var options = new JsonWriterOptions { Indented = true }; + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output, options); + + writer.WriteStartObject(); + writer.WriteStartObject("nested"); + writer.WriteString("key", "value"); + writer.WriteEndObject(); + writer.WriteEndObject(); + writer.Flush(); + + string json = Encoding.UTF8.GetString(output.WrittenSpan.ToArray()); + Assert.Contains("\"nested\":", json); + Assert.Contains("\"key\": \"value\"", json); + } } public static class WriterHelpers