From dc50db0e67679d551d5de7a597625612b53cd1b2 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Wed, 12 Apr 2023 14:14:10 +0400 Subject: [PATCH 01/12] feat(83547): Adding escaping of property name for GetPath. --- .../System/Text/Json/JsonHelpers.Escaping.cs | 37 ++++++++++++ .../src/System/Text/Json/Nodes/JsonObject.cs | 3 +- .../tests/Common/PropertyNameTests.cs | 37 ++++++++++++ .../Serialization/PropertyNameTests.cs | 2 + .../JsonNode/ParentPathRootTests.cs | 56 +++++++++++++++++++ 5 files changed, 134 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs index c4cfe678a5c8e3..f2fc3655609074 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs @@ -53,6 +53,43 @@ public static byte[] EscapeValue( return escapedString; } + public static string GetPropertyName( + string rawPropertyName, + JavaScriptEncoder? encoder) + { + int indexOfFirstCharacterToEncode; + string returnValue; + + indexOfFirstCharacterToEncode = JsonWriterHelper.NeedsEscaping(rawPropertyName.AsSpan(), encoder); + + if (indexOfFirstCharacterToEncode != -1) + { + returnValue = GetEscapedPropertyName(rawPropertyName); + } + else + { + returnValue = rawPropertyName; + } + + return returnValue; + } + + private static string GetEscapedPropertyName(string rawPropertyName) + { + const string Quote = "\""; + const string EscapedQuote = "\\\""; + const string SingleQuote = "'"; + const string EscapedSingleQuote = "\\'"; + + string returnValue; + + returnValue = rawPropertyName + .Replace(Quote, EscapedQuote) + .Replace(SingleQuote, EscapedSingleQuote); + + return returnValue; + } + private static byte[] GetEscapedPropertyNameSection( ReadOnlySpan utf8Value, int firstEscapeIndexVal, diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs index 749f5d8d07d886..9435301b4a8965 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Text.Encodings.Web; namespace System.Text.Json.Nodes { @@ -206,7 +207,7 @@ internal override void GetPath(ref ValueStringBuilder path, JsonNode? child) if (propertyName.AsSpan().ContainsSpecialCharacters()) { path.Append("['"); - path.Append(propertyName); + path.Append(JsonHelpers.GetPropertyName(propertyName, JavaScriptEncoder.Default)); path.Append("']"); } else diff --git a/src/libraries/System.Text.Json/tests/Common/PropertyNameTests.cs b/src/libraries/System.Text.Json/tests/Common/PropertyNameTests.cs index 4295359c6f0380..1dc2f0fbf06544 100644 --- a/src/libraries/System.Text.Json/tests/Common/PropertyNameTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/PropertyNameTests.cs @@ -36,6 +36,17 @@ public async Task BuiltInPolicyDeserializeMatch() await DeserializeAndAssert(JsonNamingPolicy.KebabCaseUpper, @"{""MY-INT16"":1}", 1); } + [Theory] + [MemberData(nameof(SuccessDeserialize_TestData))] + public async Task SuccessDeserialize(string valueForDeserialize, ClassWithEscapablePropertyName expectedValue) + { + ClassWithEscapablePropertyName actualValue; + + actualValue = await Serializer.DeserializeWrapper(valueForDeserialize); + + Assert.Equal(expectedValue.MyFunnyProperty, actualValue.MyFunnyProperty); + } + private async Task DeserializeAndAssert(JsonNamingPolicy policy, string json, short expected) { var options = new JsonSerializerOptions { PropertyNamingPolicy = policy }; @@ -494,5 +505,31 @@ public class ClassWithSpecialCharacters [JsonPropertyName("\uA000_2")] // Valid C# property name: \uA000_2 public int YiIt_2 { get; set; } } + + public static IEnumerable SuccessDeserialize_TestData() + { + yield return new object[] + { + "{\"abc[!@#№$;%:^&?*()-+~`|]'def'\":\"valueFromMyFunnyPropertyTestCase1\"}", + new ClassWithEscapablePropertyName + { + MyFunnyProperty = "valueFromMyFunnyPropertyTestCase1" + }, + }; + yield return new object[] + { + "{\"abc[!@#\\u2116$;%:^\\u0026?*()-\\u002B~\\u0060|]\\u0027def\\u0027\":\"valueFromMyFunnyPropertyTestCase2\"}", + new ClassWithEscapablePropertyName + { + MyFunnyProperty = "valueFromMyFunnyPropertyTestCase2" + }, + }; + } + + public class ClassWithEscapablePropertyName + { + [JsonPropertyName("abc[!@#№$;%:^&?*()-+~`|]'def'")] + public string MyFunnyProperty { get; set; } + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyNameTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyNameTests.cs index 82566bf7123ce7..2a3aa67b4ef0c2 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyNameTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyNameTests.cs @@ -18,6 +18,7 @@ public PropertyNameTests_Metadata() [JsonSerializable(typeof(Dictionary))] [JsonSerializable(typeof(Dictionary))] [JsonSerializable(typeof(int))] + [JsonSerializable(typeof(ClassWithEscapablePropertyName))] [JsonSerializable(typeof(ClassWithSpecialCharacters))] [JsonSerializable(typeof(ClassWithPropertyNamePermutations))] [JsonSerializable(typeof(ClassWithUnicodeProperty))] @@ -43,6 +44,7 @@ public PropertyNameTests_Default() [JsonSerializable(typeof(Dictionary))] [JsonSerializable(typeof(Dictionary))] [JsonSerializable(typeof(int))] + [JsonSerializable(typeof(ClassWithEscapablePropertyName))] [JsonSerializable(typeof(ClassWithSpecialCharacters))] [JsonSerializable(typeof(ClassWithPropertyNamePermutations))] [JsonSerializable(typeof(ClassWithUnicodeProperty))] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/ParentPathRootTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/ParentPathRootTests.cs index d9c1a80fa95388..9effe89005915f 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/ParentPathRootTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/ParentPathRootTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using Xunit; namespace System.Text.Json.Nodes.Tests @@ -76,6 +77,17 @@ public static void GetPathAndRoot() Assert.Equal("$[0].Child", node[0]["Child"].GetPath()); } + [Theory] + [MemberData(nameof(GetPath_ShouldReturnExpectedValue_TestData))] + public static void GetPath_ShouldReturnExpectedValue(JsonNode jsonNode, string expectedValue) + { + string actualValue; + + actualValue = jsonNode.GetPath(); + + Assert.Equal(expectedValue, actualValue); + } + [Fact] public static void GetPath_SpecialCharacters() { @@ -180,5 +192,49 @@ public static void Parent_Object() parent.Add("MyProp", child); Assert.True(child.Options.Value.PropertyNameCaseInsensitive); } + + public static IEnumerable GetPath_ShouldReturnExpectedValue_TestData() + { + yield return new object[] + { + JsonNode.Parse("""{"$myRoot":{"foo['bar":"baz"}}""")["$myRoot"]["foo['bar"], + "$.$myRoot['foo[\\'bar']" + }; + yield return new object[] + { + JsonNode.Parse("""{"$myRoot":{"foo[\"bar":"baz"}}""")["$myRoot"]["foo[\"bar"], + "$.$myRoot['foo[\\\"bar']" + }; + yield return new object[] + { + JsonNode.Parse("""{"$myRoot":{"foo['b\"ar":"baz"}}""")["$myRoot"]["foo['b\"ar"], + "$.$myRoot['foo[\\'b\\\"ar']" + }; + yield return new object[] + { + JsonNode.Parse("""{"$myRoot": {"myRoot'child": {"myRoot'child'secondLevelChild": "value1"}}}""")["$myRoot"]["myRoot'child"]["myRoot'child'secondLevelChild"], + "$.$myRoot['myRoot\\'child']['myRoot\\'child\\'secondLevelChild']" + }; + yield return new object[] + { + JsonNode.Parse("""{"$myRoot": {"myRoot\"child": {"myRoot\"child\"secondLevelChild": "value1"}}}""")["$myRoot"]["myRoot\"child"]["myRoot\"child\"secondLevelChild"], + "$.$myRoot['myRoot\\\"child']['myRoot\\\"child\\\"secondLevelChild']" + }; + yield return new object[] + { + JsonNode.Parse("""{"$myRoot": {"myRoot\"child": {"myRoot'child\"secondLevelChild": "value1"}}}""")["$myRoot"]["myRoot\"child"]["myRoot'child\"secondLevelChild"], + "$.$myRoot['myRoot\\\"child']['myRoot\\'child\\\"secondLevelChild']" + }; + yield return new object[] + { + JsonNode.Parse("""{"$myRoot": {"myRoot'child": {"secondLevelChildWithoutEscaping": "value2"}}}""")["$myRoot"]["myRoot'child"]["secondLevelChildWithoutEscaping"], + "$.$myRoot['myRoot\\'child'].secondLevelChildWithoutEscaping" + }; + yield return new object[] + { + JsonNode.Parse("""{"$myRoot": {"myRoot\"child": {"secondLevelChildWithoutEscaping": "value2"}}}""")["$myRoot"]["myRoot\"child"]["secondLevelChildWithoutEscaping"], + "$.$myRoot['myRoot\\\"child'].secondLevelChildWithoutEscaping" + }; + } } } From a6fb3538f5abd9837398e6dc9dc30bf8b85744a5 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Tue, 16 May 2023 16:34:10 +0400 Subject: [PATCH 02/12] feat(83547): Adding escaping of property name for source generator. --- .../gen/JsonSourceGenerator.Parser.cs | 16 +++++++++++++++- .../tests/Common/PropertyNameTests.cs | 13 +++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index e0dac6a9ad82ce..10f1cb96d19cfd 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -226,6 +226,20 @@ private TypeRef EnqueueType(ITypeSymbol type, JsonSourceGenerationMode? generati return new TypeRef(type); } + private static string EscapeString(string representation) + { + const string Quote = "\""; + const string EscapedQuote = "\\\""; + + if (string.IsNullOrEmpty(representation)) + { + return representation; + } + + return representation + .Replace(Quote, EscapedQuote); + } + private void ParseJsonSerializerContextAttributes( INamedTypeSymbol contextClassSymbol, out List? rootSerializableTypes, @@ -1236,7 +1250,7 @@ private void ProcessMemberCustomAttributes( case JsonPropertyNameAttributeFullName: { ImmutableArray ctorArgs = attributeData.ConstructorArguments; - jsonPropertyName = (string)ctorArgs[0].Value!; + jsonPropertyName = EscapeString((string)ctorArgs[0].Value!); // Null check here is done at runtime within JsonSerializer. } break; diff --git a/src/libraries/System.Text.Json/tests/Common/PropertyNameTests.cs b/src/libraries/System.Text.Json/tests/Common/PropertyNameTests.cs index 1dc2f0fbf06544..06a31d71b97732 100644 --- a/src/libraries/System.Text.Json/tests/Common/PropertyNameTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/PropertyNameTests.cs @@ -45,6 +45,7 @@ public async Task SuccessDeserialize(string valueForDeserialize, ClassWithEscapa actualValue = await Serializer.DeserializeWrapper(valueForDeserialize); Assert.Equal(expectedValue.MyFunnyProperty, actualValue.MyFunnyProperty); + Assert.Equal(expectedValue.MyFunnyProperty2, actualValue.MyFunnyProperty2); } private async Task DeserializeAndAssert(JsonNamingPolicy policy, string json, short expected) @@ -517,6 +518,15 @@ public static IEnumerable SuccessDeserialize_TestData() }, }; yield return new object[] + { + "{\"abc[!@#№$;%:^&?*()-+~`|]'def'\":\"valueFromMyFunnyPropertyTestCase1\", \"withQuote\\\"\": \"valueFromMyFunnyProperty2\"}", + new ClassWithEscapablePropertyName + { + MyFunnyProperty = "valueFromMyFunnyPropertyTestCase1", + MyFunnyProperty2 = "valueFromMyFunnyProperty2" + }, + }; + yield return new object[] { "{\"abc[!@#\\u2116$;%:^\\u0026?*()-\\u002B~\\u0060|]\\u0027def\\u0027\":\"valueFromMyFunnyPropertyTestCase2\"}", new ClassWithEscapablePropertyName @@ -530,6 +540,9 @@ public class ClassWithEscapablePropertyName { [JsonPropertyName("abc[!@#№$;%:^&?*()-+~`|]'def'")] public string MyFunnyProperty { get; set; } + + [JsonPropertyName("withQuote\"")] + public string MyFunnyProperty2 { get; set; } } } } From 1759a92e6d9dd949b12fbf75b857b1ab825ddaed Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Fri, 23 Jun 2023 16:55:32 +0400 Subject: [PATCH 03/12] feat(83547): Changing quote escaping to generally accepted mechanism. --- .../System/Text/Json/JsonHelpers.Escaping.cs | 19 +++++-------------- .../JsonNode/ParentPathRootTests.cs | 16 ++++++++-------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs index f2fc3655609074..3affe2fb144277 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs @@ -64,7 +64,7 @@ public static string GetPropertyName( if (indexOfFirstCharacterToEncode != -1) { - returnValue = GetEscapedPropertyName(rawPropertyName); + returnValue = GetEscapedPropertyName(rawPropertyName, encoder); } else { @@ -74,20 +74,11 @@ public static string GetPropertyName( return returnValue; } - private static string GetEscapedPropertyName(string rawPropertyName) + private static string GetEscapedPropertyName( + string rawPropertyName, + JavaScriptEncoder? encoder) { - const string Quote = "\""; - const string EscapedQuote = "\\\""; - const string SingleQuote = "'"; - const string EscapedSingleQuote = "\\'"; - - string returnValue; - - returnValue = rawPropertyName - .Replace(Quote, EscapedQuote) - .Replace(SingleQuote, EscapedSingleQuote); - - return returnValue; + return (encoder ?? JavaScriptEncoder.Default).Encode(rawPropertyName); } private static byte[] GetEscapedPropertyNameSection( diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/ParentPathRootTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/ParentPathRootTests.cs index 9effe89005915f..fc8d9e26d74570 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/ParentPathRootTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/ParentPathRootTests.cs @@ -198,42 +198,42 @@ public static IEnumerable GetPath_ShouldReturnExpectedValue_TestData() yield return new object[] { JsonNode.Parse("""{"$myRoot":{"foo['bar":"baz"}}""")["$myRoot"]["foo['bar"], - "$.$myRoot['foo[\\'bar']" + "$.$myRoot['foo[\\u0027bar']" }; yield return new object[] { JsonNode.Parse("""{"$myRoot":{"foo[\"bar":"baz"}}""")["$myRoot"]["foo[\"bar"], - "$.$myRoot['foo[\\\"bar']" + "$.$myRoot['foo[\\u0022bar']" }; yield return new object[] { JsonNode.Parse("""{"$myRoot":{"foo['b\"ar":"baz"}}""")["$myRoot"]["foo['b\"ar"], - "$.$myRoot['foo[\\'b\\\"ar']" + "$.$myRoot['foo[\\u0027b\\u0022ar']" }; yield return new object[] { JsonNode.Parse("""{"$myRoot": {"myRoot'child": {"myRoot'child'secondLevelChild": "value1"}}}""")["$myRoot"]["myRoot'child"]["myRoot'child'secondLevelChild"], - "$.$myRoot['myRoot\\'child']['myRoot\\'child\\'secondLevelChild']" + "$.$myRoot['myRoot\\u0027child']['myRoot\\u0027child\\u0027secondLevelChild']" }; yield return new object[] { JsonNode.Parse("""{"$myRoot": {"myRoot\"child": {"myRoot\"child\"secondLevelChild": "value1"}}}""")["$myRoot"]["myRoot\"child"]["myRoot\"child\"secondLevelChild"], - "$.$myRoot['myRoot\\\"child']['myRoot\\\"child\\\"secondLevelChild']" + "$.$myRoot['myRoot\\u0022child']['myRoot\\u0022child\\u0022secondLevelChild']" }; yield return new object[] { JsonNode.Parse("""{"$myRoot": {"myRoot\"child": {"myRoot'child\"secondLevelChild": "value1"}}}""")["$myRoot"]["myRoot\"child"]["myRoot'child\"secondLevelChild"], - "$.$myRoot['myRoot\\\"child']['myRoot\\'child\\\"secondLevelChild']" + "$.$myRoot['myRoot\\u0022child']['myRoot\\u0027child\\u0022secondLevelChild']" }; yield return new object[] { JsonNode.Parse("""{"$myRoot": {"myRoot'child": {"secondLevelChildWithoutEscaping": "value2"}}}""")["$myRoot"]["myRoot'child"]["secondLevelChildWithoutEscaping"], - "$.$myRoot['myRoot\\'child'].secondLevelChildWithoutEscaping" + "$.$myRoot['myRoot\\u0027child'].secondLevelChildWithoutEscaping" }; yield return new object[] { JsonNode.Parse("""{"$myRoot": {"myRoot\"child": {"secondLevelChildWithoutEscaping": "value2"}}}""")["$myRoot"]["myRoot\"child"]["secondLevelChildWithoutEscaping"], - "$.$myRoot['myRoot\\\"child'].secondLevelChildWithoutEscaping" + "$.$myRoot['myRoot\\u0022child'].secondLevelChildWithoutEscaping" }; } } From 67fb358cf79102178c981f142384891a9ce98a8a Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Sat, 24 Jun 2023 14:38:23 +0400 Subject: [PATCH 04/12] feat(83547): Changing quote escaping for source generator. --- .../gen/JsonSourceGenerator.Emitter.cs | 18 ++++++++++++++++-- .../gen/JsonSourceGenerator.Parser.cs | 16 +--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index 5109227a9b12d6..d5a25ad77fd821 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -1311,7 +1311,7 @@ private SourceText GetPropertyNameInitialization(ContextGenerationSpec contextSp foreach (KeyValuePair name_varName_pair in _propertyNames) { - writer.WriteLine($$"""private static readonly {{JsonEncodedTextTypeRef}} {{name_varName_pair.Value}} = {{JsonEncodedTextTypeRef}}.Encode("{{name_varName_pair.Key}}");"""); + writer.WriteLine($$"""private static readonly {{JsonEncodedTextTypeRef}} {{name_varName_pair.Value}} = {{JsonEncodedTextTypeRef}}.Encode({{FormatStringLiteral(name_varName_pair.Key)}});"""); } return CompleteSourceFileAndReturnText(writer); @@ -1343,7 +1343,21 @@ private static string FormatJsonSerializerDefaults(JsonSerializerDefaults defaul private static string GetCreateValueInfoMethodRef(string typeCompilableName) => $"{CreateValueInfoMethodName}<{typeCompilableName}>"; private static string FormatBool(bool value) => value ? "true" : "false"; - private static string FormatStringLiteral(string? value) => value is null ? "null" : $"\"{value}\""; + private static string FormatStringLiteral(string? value) + { + string returnValue; + + if (value is null) + { + returnValue = "null"; + } + else + { + returnValue = SyntaxFactory.Literal(value).ToFullString(); + } + + return returnValue; + } /// /// Method used to generate JsonTypeInfo given options instance diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index 10f1cb96d19cfd..e0dac6a9ad82ce 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -226,20 +226,6 @@ private TypeRef EnqueueType(ITypeSymbol type, JsonSourceGenerationMode? generati return new TypeRef(type); } - private static string EscapeString(string representation) - { - const string Quote = "\""; - const string EscapedQuote = "\\\""; - - if (string.IsNullOrEmpty(representation)) - { - return representation; - } - - return representation - .Replace(Quote, EscapedQuote); - } - private void ParseJsonSerializerContextAttributes( INamedTypeSymbol contextClassSymbol, out List? rootSerializableTypes, @@ -1250,7 +1236,7 @@ private void ProcessMemberCustomAttributes( case JsonPropertyNameAttributeFullName: { ImmutableArray ctorArgs = attributeData.ConstructorArguments; - jsonPropertyName = EscapeString((string)ctorArgs[0].Value!); + jsonPropertyName = (string)ctorArgs[0].Value!; // Null check here is done at runtime within JsonSerializer. } break; From 51fcbb09a8b9c5eee71d151817455c97bf484545 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Wed, 4 Oct 2023 10:15:38 +0400 Subject: [PATCH 05/12] feat(83547): Simplification of "System.Text.Json.SourceGeneration.JsonSourceGenerator.Emitter.FormatStringLiteral(string?)" implementation. --- .../gen/JsonSourceGenerator.Emitter.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index d5a25ad77fd821..8c97916097fca0 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -1344,20 +1344,7 @@ private static string FormatJsonSerializerDefaults(JsonSerializerDefaults defaul private static string FormatBool(bool value) => value ? "true" : "false"; private static string FormatStringLiteral(string? value) - { - string returnValue; - - if (value is null) - { - returnValue = "null"; - } - else - { - returnValue = SyntaxFactory.Literal(value).ToFullString(); - } - - return returnValue; - } + => value is null ? "null" : SyntaxFactory.Literal(value).ToFullString(); /// /// Method used to generate JsonTypeInfo given options instance From a610e38ee705bfe38420a1993af2ffc6b8101186 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Wed, 4 Oct 2023 10:18:18 +0400 Subject: [PATCH 06/12] Simplification of "System.Text.Json.JsonHelpers.GetEscapedPropertyName(string, JavaScriptEncoder?)" implementation. --- .../System/Text/Json/JsonHelpers.Escaping.cs | 24 ++----------------- .../src/System/Text/Json/Nodes/JsonObject.cs | 2 +- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs index 3affe2fb144277..cc19ee40dd8868 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs @@ -53,31 +53,11 @@ public static byte[] EscapeValue( return escapedString; } - public static string GetPropertyName( - string rawPropertyName, - JavaScriptEncoder? encoder) - { - int indexOfFirstCharacterToEncode; - string returnValue; - - indexOfFirstCharacterToEncode = JsonWriterHelper.NeedsEscaping(rawPropertyName.AsSpan(), encoder); - - if (indexOfFirstCharacterToEncode != -1) - { - returnValue = GetEscapedPropertyName(rawPropertyName, encoder); - } - else - { - returnValue = rawPropertyName; - } - - return returnValue; - } - - private static string GetEscapedPropertyName( + public static string GetEscapedPropertyName( string rawPropertyName, JavaScriptEncoder? encoder) { + Debug.Assert(JsonWriterHelper.NeedsEscaping(rawPropertyName.AsSpan(), encoder) != -1); return (encoder ?? JavaScriptEncoder.Default).Encode(rawPropertyName); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs index 9435301b4a8965..68be51355cf1e7 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs @@ -207,7 +207,7 @@ internal override void GetPath(ref ValueStringBuilder path, JsonNode? child) if (propertyName.AsSpan().ContainsSpecialCharacters()) { path.Append("['"); - path.Append(JsonHelpers.GetPropertyName(propertyName, JavaScriptEncoder.Default)); + path.Append(JsonHelpers.GetEscapedPropertyName(propertyName, JavaScriptEncoder.Default)); path.Append("']"); } else From 4a0d7b17c4ad86dbe77fce5281a41bbb175e53d9 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Wed, 4 Oct 2023 10:19:49 +0400 Subject: [PATCH 07/12] Revert "Simplification of "System.Text.Json.JsonHelpers.GetEscapedPropertyName(string, JavaScriptEncoder?)" implementation." This reverts commit d346c16f342c282a456c18d7f4f95e88f43a6cad. --- .../System/Text/Json/JsonHelpers.Escaping.cs | 24 +++++++++++++++++-- .../src/System/Text/Json/Nodes/JsonObject.cs | 2 +- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs index cc19ee40dd8868..3affe2fb144277 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs @@ -53,11 +53,31 @@ public static byte[] EscapeValue( return escapedString; } - public static string GetEscapedPropertyName( + public static string GetPropertyName( + string rawPropertyName, + JavaScriptEncoder? encoder) + { + int indexOfFirstCharacterToEncode; + string returnValue; + + indexOfFirstCharacterToEncode = JsonWriterHelper.NeedsEscaping(rawPropertyName.AsSpan(), encoder); + + if (indexOfFirstCharacterToEncode != -1) + { + returnValue = GetEscapedPropertyName(rawPropertyName, encoder); + } + else + { + returnValue = rawPropertyName; + } + + return returnValue; + } + + private static string GetEscapedPropertyName( string rawPropertyName, JavaScriptEncoder? encoder) { - Debug.Assert(JsonWriterHelper.NeedsEscaping(rawPropertyName.AsSpan(), encoder) != -1); return (encoder ?? JavaScriptEncoder.Default).Encode(rawPropertyName); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs index 68be51355cf1e7..9435301b4a8965 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs @@ -207,7 +207,7 @@ internal override void GetPath(ref ValueStringBuilder path, JsonNode? child) if (propertyName.AsSpan().ContainsSpecialCharacters()) { path.Append("['"); - path.Append(JsonHelpers.GetEscapedPropertyName(propertyName, JavaScriptEncoder.Default)); + path.Append(JsonHelpers.GetPropertyName(propertyName, JavaScriptEncoder.Default)); path.Append("']"); } else From 6cb8d88f7b4d294060ada463c48a68c577dd76a3 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 4 Oct 2023 15:28:52 +0100 Subject: [PATCH 08/12] Update src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs --- .../System/Text/Json/JsonHelpers.Escaping.cs | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs index 3affe2fb144277..ef7d1f5469c102 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs @@ -53,34 +53,15 @@ public static byte[] EscapeValue( return escapedString; } - public static string GetPropertyName( + public static string GetEscapedPropertyName( string rawPropertyName, - JavaScriptEncoder? encoder) + JavaScriptEncoder encoder) { - int indexOfFirstCharacterToEncode; - string returnValue; - - indexOfFirstCharacterToEncode = JsonWriterHelper.NeedsEscaping(rawPropertyName.AsSpan(), encoder); - - if (indexOfFirstCharacterToEncode != -1) - { - returnValue = GetEscapedPropertyName(rawPropertyName, encoder); - } - else - { - returnValue = rawPropertyName; - } - - return returnValue; + int indexOfFirstCharacterToEncode = JsonWriterHelper.NeedsEscaping(rawPropertyName.AsSpan(), encoder); + return indexOfFirstCharacterToEncode >= 0 + ? encoder.Encoder(rawPropertyName) + : rawPropertyName; } - - private static string GetEscapedPropertyName( - string rawPropertyName, - JavaScriptEncoder? encoder) - { - return (encoder ?? JavaScriptEncoder.Default).Encode(rawPropertyName); - } - private static byte[] GetEscapedPropertyNameSection( ReadOnlySpan utf8Value, int firstEscapeIndexVal, From e093c8ecc6d89fccb94e10df833393ef582517b4 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 4 Oct 2023 15:29:23 +0100 Subject: [PATCH 09/12] Update src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs --- .../src/System/Text/Json/JsonHelpers.Escaping.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs index ef7d1f5469c102..bbf2d6c5289b45 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs @@ -62,6 +62,7 @@ public static string GetEscapedPropertyName( ? encoder.Encoder(rawPropertyName) : rawPropertyName; } + private static byte[] GetEscapedPropertyNameSection( ReadOnlySpan utf8Value, int firstEscapeIndexVal, From 002503ca34f077c0a05d84bfdfcec8c563cb3665 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 4 Oct 2023 15:29:41 +0100 Subject: [PATCH 10/12] Update src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs --- .../System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs index 9435301b4a8965..68be51355cf1e7 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs @@ -207,7 +207,7 @@ internal override void GetPath(ref ValueStringBuilder path, JsonNode? child) if (propertyName.AsSpan().ContainsSpecialCharacters()) { path.Append("['"); - path.Append(JsonHelpers.GetPropertyName(propertyName, JavaScriptEncoder.Default)); + path.Append(JsonHelpers.GetEscapedPropertyName(propertyName, JavaScriptEncoder.Default)); path.Append("']"); } else From 6ac4ebaeb0fbc1083e6c01495eb4b88682867dcd Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 4 Oct 2023 16:21:38 +0100 Subject: [PATCH 11/12] Fix build --- .../src/System/Text/Json/JsonHelpers.Escaping.cs | 5 +++-- .../src/System/Text/Json/Nodes/JsonObject.cs | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs index bbf2d6c5289b45..a63464d3fedf54 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs @@ -55,11 +55,12 @@ public static byte[] EscapeValue( public static string GetEscapedPropertyName( string rawPropertyName, - JavaScriptEncoder encoder) + JavaScriptEncoder? encoder = null) { + encoder ??= JavaScriptEncoder.Default; int indexOfFirstCharacterToEncode = JsonWriterHelper.NeedsEscaping(rawPropertyName.AsSpan(), encoder); return indexOfFirstCharacterToEncode >= 0 - ? encoder.Encoder(rawPropertyName) + ? encoder.Encode(rawPropertyName) : rawPropertyName; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs index 68be51355cf1e7..ece82f0f06d60a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Text.Encodings.Web; namespace System.Text.Json.Nodes { @@ -207,7 +206,7 @@ internal override void GetPath(ref ValueStringBuilder path, JsonNode? child) if (propertyName.AsSpan().ContainsSpecialCharacters()) { path.Append("['"); - path.Append(JsonHelpers.GetEscapedPropertyName(propertyName, JavaScriptEncoder.Default)); + path.Append(JsonHelpers.GetEscapedPropertyName(propertyName)); path.Append("']"); } else From 1c5baabdc91f998a965202a14b595a96a783ebad Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Fri, 27 Oct 2023 15:06:41 +0100 Subject: [PATCH 12/12] Update src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs --- .../System.Text.Json/gen/JsonSourceGenerator.Emitter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index 8c97916097fca0..5ae666f4a90f90 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -1344,7 +1344,7 @@ private static string FormatJsonSerializerDefaults(JsonSerializerDefaults defaul private static string FormatBool(bool value) => value ? "true" : "false"; private static string FormatStringLiteral(string? value) - => value is null ? "null" : SyntaxFactory.Literal(value).ToFullString(); + => value is null ? "null" : SymbolDisplay.FormatLiteral(value, quote: true); /// /// Method used to generate JsonTypeInfo given options instance