diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.Dynamic.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.Dynamic.cs index c1ff197fcf8..dda66667d85 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.Dynamic.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.Dynamic.cs @@ -25,14 +25,68 @@ private MethodBodyStatement CreateDictionarySerializationWithPatch( SerializationFormat serializationFormat, ScopedApi patchSnippet, string serializedName, - List? parentIndices = null) + List? parentIndices = null, + ValueExpression? parentHasPatch = null, + bool suppressPatchLogic = false) { parentIndices ??= []; + MethodBodyStatement CreateDictionaryItemSerialization(KeyValuePairExpression item, ValueExpression? itemParentHasPatch, bool itemSuppressPatchLogic) + { + List itemChildIndices = item.ValueType.IsCollection + ? [.. parentIndices, item.Key] + : parentIndices; + + return new MethodBodyStatement[] + { + _utf8JsonWriterSnippet.WritePropertyName(item.Key), + CreateElementSerializationWithPatch( + item.Value, + item.ValueType, + patchSnippet, + serializationFormat, + serializedName, + itemChildIndices, + itemParentHasPatch, + itemSuppressPatchLogic) + }; + } + + // The collection is known to have no relevant patch at or below this level, so skip building + // any path-dependent patch checks entirely instead of materializing a dead patched branch. + if (suppressPatchLogic) + { + var noPatchForeachStatement = new ForEachStatement("item", dictionary, out KeyValuePairExpression noPatchKeyValuePair); + noPatchForeachStatement.Add(CreateDictionaryItemSerialization(noPatchKeyValuePair, null, itemSuppressPatchLogic: true)); + + return new MethodBodyStatement[] + { + _utf8JsonWriterSnippet.WriteStartObject(), + noPatchForeachStatement, + _utf8JsonWriterSnippet.WriteEndObject() + }; + } + var jsonPathTemplate = BuildJsonPathForElement(serializedName, parentIndices); + MethodBodyStatement? hasPatchDeclaration = null; + ValueExpression hasPatch; + if (parentHasPatch == null) + { + hasPatchDeclaration = Declare( + "hasPatch", + typeof(bool), + patchSnippet.Contains(LiteralU8("$"), LiteralU8(serializedName)), + out var localHasPatch); + hasPatch = localHasPatch; + } + else + { + hasPatch = parentHasPatch; + } + ValueExpression jsonPath = parentIndices.Count > 0 ? Utf8Snippets.GetBytes(new FormattableStringExpression(jsonPathTemplate, [.. parentIndices]).As()) - : LiteralU8($"$.{serializedName}"); + : LiteralU8(jsonPathTemplate); var foreachStatement = new ForEachStatement("item", dictionary, out KeyValuePairExpression keyValuePair); @@ -59,21 +113,10 @@ private MethodBodyStatement CreateDictionarySerializationWithPatch( ReadOnlySpanSnippets.Slice(bufferVar, Int(0), bytesWrittenVar))), out var patchContainsNet8Var); - List childIndices = keyValuePair.ValueType.IsCollection - ? [.. parentIndices, keyValuePair.Key] - : parentIndices; - // Process key-value pair if patch doesn't contain it var ifPatchDoesNotContainStatement = new IfStatement(Not(patchContainsNet8Var)) { - _utf8JsonWriterSnippet.WritePropertyName(keyValuePair.Key), - CreateElementSerializationWithPatch( - keyValuePair.Value, - keyValuePair.ValueType, - patchSnippet, - serializationFormat, - serializedName, - childIndices) + CreateDictionaryItemSerialization(keyValuePair, hasPatch, itemSuppressPatchLogic: false) }; var innerIfElseProcessorStatement = new IfElsePreprocessorStatement( @@ -86,15 +129,30 @@ private MethodBodyStatement CreateDictionarySerializationWithPatch( foreachStatement.Add(innerIfElseProcessorStatement); foreachStatement.Add(ifPatchDoesNotContainStatement); - return new[] + var patchedStatements = new MethodBodyStatement[] { - _utf8JsonWriterSnippet.WriteStartObject(), new IfElsePreprocessorStatement("NET8_0_OR_GREATER", bufferDeclaration), foreachStatement, MethodBodyStatement.EmptyLine, - patchSnippet.WriteTo(_utf8JsonWriterSnippet, jsonPath).Terminate(), - _utf8JsonWriterSnippet.WriteEndObject(), + patchSnippet.WriteTo(_utf8JsonWriterSnippet, jsonPath).Terminate() }; + + var unpatchedForeachStatement = new ForEachStatement("item", dictionary, out KeyValuePairExpression unpatchedKeyValuePair); + // This branch only runs when the collection has no relevant patch, so serializers can skip path-dependent patch work. + unpatchedForeachStatement.Add(CreateDictionaryItemSerialization(unpatchedKeyValuePair, null, itemSuppressPatchLogic: true)); + + var dictionaryStatements = new List + { + _utf8JsonWriterSnippet.WriteStartObject(), + }; + if (hasPatchDeclaration != null) + { + dictionaryStatements.Add(hasPatchDeclaration); + } + + dictionaryStatements.Add(new IfElseStatement(hasPatch.As(), patchedStatements, unpatchedForeachStatement)); + dictionaryStatements.Add(_utf8JsonWriterSnippet.WriteEndObject()); + return dictionaryStatements.ToArray(); } private MethodBodyStatement CreateListSerializationWithPatch( @@ -104,18 +162,17 @@ private MethodBodyStatement CreateListSerializationWithPatch( ScopedApi patchSnippet, SerializationFormat serializationFormat, string serializedName, - List? parentIndices = null) + List? parentIndices = null, + ValueExpression? parentHasPatch = null, + bool suppressPatchLogic = false) { parentIndices ??= []; var indexDeclaration = Declare("i", out var indexVar); var allIndices = new List(parentIndices) { indexVar }; - var jsonPathTemplate = BuildJsonPathForElement(serializedName, parentIndices); - var patchIsRemovedCondition = patchSnippet.IsRemoved( - Utf8Snippets.GetBytes( - new FormattableStringExpression(jsonPathTemplate + $"[{{{parentIndices.Count}}}]", allIndices) - .As())); - // Handle model types with their own patch property + // Handle model types with their own patch property. This is independent of whether this + // collection has a relevant patch, so it applies in both the patched and no-patch paths below. + ScopedApi? childIsRemovedCondition = null; if (ScmCodeModelGenerator.Instance.TypeFactory.CSharpTypeMap.TryGetValue(type, out var provider) && provider is ScmModelProvider scmModelProvider && scmModelProvider.JsonPatchProperty != null) { @@ -127,12 +184,72 @@ private MethodBodyStatement CreateListSerializationWithPatch( { childIsRemoved = new IndexerExpression(collection, indexVar).NotEqual(Null).And(childIsRemoved); } - patchIsRemovedCondition = patchIsRemovedCondition.Or(childIsRemoved); + childIsRemovedCondition = childIsRemoved; } string lengthProperty = isReadOnlySpan || type.IsArray ? "Length" : "Count"; + + // The collection is known to have no relevant patch at or below this level, so skip building any + // path-dependent patch checks entirely instead of materializing a dead patched branch. + if (suppressPatchLogic) + { + var noPatchForStatement = new ForStatement( + indexDeclaration.Assign(Literal(0)), + indexVar.LessThan(collection.Property(lengthProperty)), + indexVar.Increment()); + if (childIsRemovedCondition != null) + { + noPatchForStatement.Add(new IfStatement(childIsRemovedCondition) { Continue }); + } + noPatchForStatement.Add(CreateElementSerializationWithPatch( + new IndexerExpression(collection, indexVar), + type, + patchSnippet, + serializationFormat, + serializedName, + allIndices, + parentHasPatch, + suppressPatchLogic: true)); + + return new MethodBodyStatement[] + { + _utf8JsonWriterSnippet.WriteStartArray(), + noPatchForStatement, + _utf8JsonWriterSnippet.WriteEndArray() + }; + } + + var jsonPathTemplate = BuildJsonPathForElement(serializedName, parentIndices); + // The prefix overload includes indexed descendants, unlike an exact-path Contains check. + // Nested collections under the same serialized property can reuse the parent guard. + MethodBodyStatement? hasPatchDeclaration = null; + ValueExpression hasPatch; + if (parentHasPatch == null) + { + hasPatchDeclaration = Declare( + "hasPatch", + typeof(bool), + patchSnippet.Contains(LiteralU8("$"), LiteralU8(serializedName)), + out var localHasPatch); + hasPatch = localHasPatch; + } + else + { + hasPatch = parentHasPatch; + } + + var patchIsRemovedCondition = hasPatch.As().And(patchSnippet.IsRemoved( + Utf8Snippets.GetBytes( + new FormattableStringExpression(jsonPathTemplate + $"[{{{parentIndices.Count}}}]", allIndices) + .As()))); + + if (childIsRemovedCondition != null) + { + patchIsRemovedCondition = patchIsRemovedCondition.Or(childIsRemovedCondition); + } + var forStatement = new ForStatement( indexDeclaration.Assign(Literal(0)), indexVar.LessThan(collection.Property(lengthProperty)), @@ -147,21 +264,32 @@ private MethodBodyStatement CreateListSerializationWithPatch( patchSnippet, serializationFormat, serializedName, - allIndices) + allIndices, + hasPatch) } }; - var writeToPatchStatement = parentIndices.Count == 0 + MethodBodyStatement writeToPatchStatement = parentIndices.Count == 0 ? patchSnippet.WriteTo(_utf8JsonWriterSnippet, LiteralU8(jsonPathTemplate)).Terminate() : patchSnippet.WriteTo(_utf8JsonWriterSnippet, Utf8Snippets.GetBytes(new FormattableStringExpression(jsonPathTemplate, parentIndices).As())).Terminate(); + if (parentIndices.Count > 0) + { + writeToPatchStatement = new IfStatement(hasPatch.As()) { writeToPatchStatement }; + } - return new[] + var listStatements = new List { - _utf8JsonWriterSnippet.WriteStartArray(), - forStatement, - writeToPatchStatement, - _utf8JsonWriterSnippet.WriteEndArray() + _utf8JsonWriterSnippet.WriteStartArray() }; + if (hasPatchDeclaration != null) + { + listStatements.Add(hasPatchDeclaration); + } + + listStatements.Add(forStatement); + listStatements.Add(writeToPatchStatement); + listStatements.Add(_utf8JsonWriterSnippet.WriteEndArray()); + return listStatements.ToArray(); } private MethodBodyStatement CreateElementSerializationWithPatch( @@ -170,7 +298,9 @@ private MethodBodyStatement CreateElementSerializationWithPatch( ScopedApi patchSnippet, SerializationFormat serializationFormat, string serializedName, - List currentIndices) + List currentIndices, + ValueExpression? parentHasPatch = null, + bool suppressPatchLogic = false) { var nestedSerialization = elementType switch { @@ -181,13 +311,17 @@ private MethodBodyStatement CreateElementSerializationWithPatch( patchSnippet, serializationFormat, serializedName, - currentIndices), + currentIndices, + parentHasPatch, + suppressPatchLogic), { IsDictionary: true } => CreateDictionarySerializationWithPatch( new DictionaryExpression(elementType, element), serializationFormat, patchSnippet, serializedName, - currentIndices), + currentIndices, + parentHasPatch, + suppressPatchLogic), _ => null }; @@ -215,7 +349,7 @@ private IfElseStatement CreateConditionalPatchSerializationStatement( MethodBodyStatement writePropertySerializationStatement, MethodBodyStatement? elseStatementBody) { - string jsonPath = $"$.{serializedName}"; + string jsonPath = BuildJsonPathForElement(serializedName, []); var ifPatchIsNotRemoved = new IfStatement(Not(_jsonPatchProperty!.As().IsRemoved(LiteralU8(jsonPath)))) { _utf8JsonWriterSnippet.WritePropertyName(serializedName), @@ -605,10 +739,16 @@ private MethodProvider BuildActiveItemsMethod(PropertyProvider property) { isActive = item.Equal(Null).Or(isActive); } + var serializedName = GetJsonSerializedName(property.WireInfo!); + var hasPatchDeclaration = Declare( + "hasPatch", + typeof(bool), + _jsonPatchProperty!.As().Contains(LiteralU8("$"), LiteralU8(serializedName)), + out var hasPatch); var itemPath = Utf8Snippets.GetBytes(new FormattableStringExpression( - BuildJsonPathForElement(GetJsonSerializedName(property.WireInfo!), [indexVar]), + BuildJsonPathForElement(serializedName, [indexVar]), [indexVar]).As()); - isActive = Not(_jsonPatchProperty!.As().IsRemoved(itemPath)).And(isActive); + isActive = Not(hasPatch).Or(Not(_jsonPatchProperty!.As().IsRemoved(itemPath))).And(isActive); var forStatement = new ForStatement( indexDeclaration.Assign(Literal(0)), indexVar.LessThan(((ValueExpression)property).Property(lengthPropertyName)), @@ -626,6 +766,7 @@ private MethodProvider BuildActiveItemsMethod(PropertyProvider property) { YieldBreak() }, + hasPatchDeclaration, forStatement }; @@ -659,11 +800,6 @@ private List GetQualifyingDynamicListProperties() private static string BuildJsonPathForElement(string propertySerializedName, List indices) { var count = indices.Count; - if (count == 0) - { - return $"$.{propertySerializedName}"; - } - var result = $"$.{propertySerializedName}"; for (int i = 0; i < count; i++) { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ModelReaderWriterValidation/TestProjects/Sample_TypeSpec/DynamicModelTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ModelReaderWriterValidation/TestProjects/Sample_TypeSpec/DynamicModelTests.cs index 8021aa4c547..457a065bb83 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ModelReaderWriterValidation/TestProjects/Sample_TypeSpec/DynamicModelTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ModelReaderWriterValidation/TestProjects/Sample_TypeSpec/DynamicModelTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Buffers; using System.ClientModel.Primitives; using System.Collections.Generic; using System.Linq; @@ -369,6 +370,292 @@ public void JsonPatchRemove_NullDynamicListElementSnapshot(bool onlyNull) Assert.That(Encoding.UTF8.GetString(json), Is.EqualTo(onlyNull ? "[]" : """[{"bar":"present"},null]""")); } + [TestCase(false)] + [TestCase(true)] + public void JsonModelWrite_UnpatchedCollectionDoesNotAllocatePerElement(bool unrelatedPatch) + { + var model = new NullableDynamicModel + { + Children = new AnotherDynamicModel[256] + }; +#pragma warning disable SCME0001 + if (unrelatedPatch) + { + model.Patch.Set("$.unrelated"u8, 1); + } +#pragma warning restore SCME0001 + + var buffer = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(buffer); + var jsonModel = (IJsonModel)model; + jsonModel.Write(writer, ModelReaderWriterOptions.Json); + writer.Flush(); + buffer.Clear(); + writer.Reset(buffer); + + long before = GC.GetAllocatedBytesForCurrentThread(); + jsonModel.Write(writer, ModelReaderWriterOptions.Json); + writer.Flush(); + long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + + Assert.That(allocated, Is.LessThan(1024), "Indexed patch paths must not allocate for each unpatched element."); + using var document = JsonDocument.Parse(buffer.WrittenMemory); + Assert.That(document.RootElement.GetProperty("children").GetArrayLength(), Is.EqualTo(256)); + } + + [TestCase("nestedChildren", false)] + [TestCase("nestedChildren", true)] + [TestCase("nestedChildDictionary", false)] + [TestCase("nestedChildDictionary", true)] + [TestCase("dictionaryChildren", false)] + [TestCase("dictionaryChildren", true)] + [TestCase("listOfDictionaries", false)] + [TestCase("listOfDictionaries", true)] + public void JsonModelWrite_UnpatchedNestedCollectionDoesNotAllocatePerElement(string propertyName, bool unrelatedPatch) + { + const int Count = 256; + var model = new NullableDynamicModel(); + switch (propertyName) + { + case "nestedChildren": + model.NestedChildren = Enumerable.Range(0, Count) + .Select(_ => (IList)[new AnotherDynamicModel("value")]) + .ToList(); + break; + case "nestedChildDictionary": + model.NestedChildDictionary = Enumerable.Range(0, Count) + .ToDictionary( + index => index.ToString(), + _ => (IDictionary)new Dictionary + { + ["value"] = new AnotherDynamicModel("value") + }); + break; + case "dictionaryChildren": + model.DictionaryChildren = Enumerable.Range(0, Count) + .ToDictionary( + index => index.ToString(), + _ => (IList)[new AnotherDynamicModel("value")]); + break; + case "listOfDictionaries": + model.ListOfDictionaries = Enumerable.Range(0, Count) + .Select(_ => (IDictionary)new Dictionary + { + ["value"] = new AnotherDynamicModel("value") + }) + .ToList(); + break; + default: + throw new ArgumentOutOfRangeException(nameof(propertyName), propertyName, null); + } + +#pragma warning disable SCME0001 + if (unrelatedPatch) + { + model.Patch.Set("$.unrelated"u8, 1); + } +#pragma warning restore SCME0001 + + var buffer = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(buffer); + var jsonModel = (IJsonModel)model; + jsonModel.Write(writer, ModelReaderWriterOptions.Json); + writer.Flush(); + buffer.Clear(); + writer.Reset(buffer); + + long before = GC.GetAllocatedBytesForCurrentThread(); + jsonModel.Write(writer, ModelReaderWriterOptions.Json); + writer.Flush(); + long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + + // Shapes whose innermost collection element is an IDictionary<,> (nestedChildDictionary, + // listOfDictionaries) enumerate that dictionary via its interface once per outer element, + // which boxes a struct enumerator regardless of any patch guard. That is unrelated overhead + // this PR does not address, so those shapes use a higher bound; all shapes must stay far + // below what unconditional per-element interpolated-path formatting would cost (tens of KB). + long maxAllocated = propertyName is "nestedChildDictionary" or "listOfDictionaries" ? 20 * 1024 : 4096; + Assert.That(allocated, Is.LessThan(maxAllocated), "Indexed patch paths must not allocate for each unpatched nested element."); + using var document = JsonDocument.Parse(buffer.WrittenMemory); + var collection = document.RootElement.GetProperty(propertyName); + Assert.That( + collection.ValueKind == JsonValueKind.Array ? collection.GetArrayLength() : collection.EnumerateObject().Count(), + Is.EqualTo(Count)); + } + + [TestCase("nestedChildren")] + [TestCase("nestedChildDictionary")] + [TestCase("dictionaryChildren")] + [TestCase("listOfDictionaries")] + public void JsonModelWrite_UnpatchedNestedCollectionSerializes(string propertyName) + { + const int Count = 256; + var model = new NullableDynamicModel(); + switch (propertyName) + { + case "nestedChildren": + model.NestedChildren = Enumerable.Range(0, Count) + .Select(_ => (IList)[new AnotherDynamicModel("value")]) + .ToList(); + break; + case "nestedChildDictionary": + model.NestedChildDictionary = Enumerable.Range(0, Count) + .ToDictionary( + index => index.ToString(), + _ => (IDictionary)new Dictionary + { + ["value"] = new AnotherDynamicModel("value") + }); + break; + case "dictionaryChildren": + model.DictionaryChildren = Enumerable.Range(0, Count) + .ToDictionary( + index => index.ToString(), + _ => (IList)[new AnotherDynamicModel("value")]); + break; + case "listOfDictionaries": + model.ListOfDictionaries = Enumerable.Range(0, Count) + .Select(_ => (IDictionary)new Dictionary + { + ["value"] = new AnotherDynamicModel("value") + }) + .ToList(); + break; + default: + throw new ArgumentOutOfRangeException(nameof(propertyName), propertyName, null); + } + + var buffer = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(buffer); + var jsonModel = (IJsonModel)model; + jsonModel.Write(writer, ModelReaderWriterOptions.Json); + writer.Flush(); + buffer.Clear(); + writer.Reset(buffer); + + jsonModel.Write(writer, ModelReaderWriterOptions.Json); + writer.Flush(); + + using var document = JsonDocument.Parse(buffer.WrittenMemory); + var collection = document.RootElement.GetProperty(propertyName); + Assert.That( + collection.ValueKind == JsonValueKind.Array ? collection.GetArrayLength() : collection.EnumerateObject().Count(), + Is.EqualTo(Count)); + } + + [TestCase("nestedChildren")] + [TestCase("nestedChildDictionary")] + [TestCase("dictionaryChildren")] + [TestCase("listOfDictionaries")] + public void JsonModelWrite_PatchedNestedCollectionSerializesParentPatch(string propertyName) + { + var model = new NullableDynamicModel(); + var patchPath = propertyName switch + { + "nestedChildren" => SetNestedChildren(model), + "nestedChildDictionary" => SetNestedChildDictionary(model), + "dictionaryChildren" => SetDictionaryChildren(model), + "listOfDictionaries" => SetListOfDictionaries(model), + _ => throw new ArgumentOutOfRangeException(nameof(propertyName), propertyName, null) + }; + +#pragma warning disable SCME0001 + model.Patch.Set(Encoding.UTF8.GetBytes(patchPath), "patched"); +#pragma warning restore SCME0001 + + var data = ModelReaderWriter.Write(model, ModelReaderWriterOptions.Json, SampleTypeSpecContext.Default); + using var document = JsonDocument.Parse(data); + JsonElement patchedElement; + switch (propertyName) + { + case "nestedChildren": + var nestedChildren = document.RootElement.GetProperty("nestedChildren")[0]; + patchedElement = nestedChildren[0]; + break; + case "nestedChildDictionary": + var nestedChildDictionary = document.RootElement.GetProperty("nestedChildDictionary").GetProperty("outer"); + patchedElement = nestedChildDictionary.GetProperty("patched"); + break; + case "dictionaryChildren": + var dictionaryChildren = document.RootElement.GetProperty("dictionaryChildren").GetProperty("outer"); + patchedElement = dictionaryChildren[0]; + break; + case "listOfDictionaries": + var listOfDictionaries = document.RootElement.GetProperty("listOfDictionaries")[0]; + patchedElement = listOfDictionaries.GetProperty("patched"); + break; + default: + throw new ArgumentOutOfRangeException(nameof(propertyName), propertyName, null); + } + + Assert.That(patchedElement.GetProperty("extra").GetString(), Is.EqualTo("patched")); + + static string SetNestedChildren(NullableDynamicModel model) + { + model.NestedChildren = [[null!]]; + return "$.nestedChildren[0][0].extra"; + } + + static string SetNestedChildDictionary(NullableDynamicModel model) + { + model.NestedChildDictionary = new Dictionary> + { + ["outer"] = new Dictionary + { + ["patched"] = null! + } + }; + return "$.nestedChildDictionary.outer.patched.extra"; + } + + static string SetDictionaryChildren(NullableDynamicModel model) + { + model.DictionaryChildren = new Dictionary> + { + ["outer"] = [null!] + }; + return "$.dictionaryChildren.outer[0].extra"; + } + + static string SetListOfDictionaries(NullableDynamicModel model) + { + model.ListOfDictionaries = + [ + new Dictionary + { + ["patched"] = null! + } + ]; + return "$.listOfDictionaries[0].patched.extra"; + } + } + + [TestCase(false)] + [TestCase(true)] + public void JsonPatchRemove_ChildRootWithUnpatchedParentCollection(bool unrelatedPatch) + { + var removed = new AnotherDynamicModel("removed"); + var model = new NullableDynamicModel + { + Children = [null, removed, new AnotherDynamicModel("present")] + }; + +#pragma warning disable SCME0001 + removed.Patch.Remove("$"u8); + if (unrelatedPatch) + { + model.Patch.Set("$.unrelated"u8, 1); + } + Assert.That(model.Patch.Contains("$"u8, "children"u8), Is.False); + var snapshot = model.Patch.GetJson("$.children"u8); +#pragma warning restore SCME0001 + + Assert.That(Encoding.UTF8.GetString(snapshot), Is.EqualTo("""[null,{"bar":"present"}]""")); + var data = ModelReaderWriter.Write(model, ModelReaderWriterOptions.Json, SampleTypeSpecContext.Default); + using var document = JsonDocument.Parse(data); + Assert.That(document.RootElement.GetProperty("children").GetRawText(), Is.EqualTo("""[null,{"bar":"present"}]""")); + } + private static NullableDynamicModel CreateModelWithRemovedDynamicListElements(string propertyName, bool onlyNull) { var items = onlyNull ? "[null]" : """[null,{"bar":"present"},null]"""; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/PropagateModelListPropertyHelperMethods.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/PropagateModelListPropertyHelperMethods.cs index dc9e907c4a9..ba367387537 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/PropagateModelListPropertyHelperMethods.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/PropagateModelListPropertyHelperMethods.cs @@ -30,9 +30,10 @@ private bool TryResolveP1Array(out global::System.ClientModel.Primitives.JsonPat { yield break; } + bool hasPatch = Patch.Contains("$"u8, "p1"u8); for (int i = 0; (i < P1.Count); i++) { - if ((!Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.p1[{i}]")) && ((P1[i] == null) || !P1[i].Patch.IsRemoved("$"u8)))) + if (((!hasPatch || !Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.p1[{i}]"))) && ((P1[i] == null) || !P1[i].Patch.IsRemoved("$"u8)))) { yield return P1[i]; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteArrayProperties.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteArrayProperties.cs index 567364334e1..207d475d1c0 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteArrayProperties.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteArrayProperties.cs @@ -49,9 +49,10 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite { writer.WritePropertyName("cats"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "cats"u8); for (int i = 0; (i < Cats.Count); i++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.cats[{i}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.cats[{i}]")))) { continue; } @@ -72,9 +73,10 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite { writer.WritePropertyName("names"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "names"u8); for (int i = 0; (i < Names.Count); i++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.names[{i}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.names[{i}]")))) { continue; } @@ -100,9 +102,10 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite { writer.WritePropertyName("optionalNames"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "optionalNames"u8); for (int i = 0; (i < OptionalNames.Count); i++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.optionalNames[{i}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.optionalNames[{i}]")))) { continue; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteDictionaryProperties.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteDictionaryProperties.cs index 3e786fd1bea..1d31320c51f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteDictionaryProperties.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteDictionaryProperties.cs @@ -41,43 +41,74 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite { writer.WritePropertyName("cats"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "cats"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in Cats) - { + foreach (var item in Cats) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.cats"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.cats"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.cats"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.cats"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.cats"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.cats"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + writer.WriteObjectValue(item.Value, options); + } + } + + Patch.WriteTo(writer, "$.cats"u8); + } + else + { + foreach (var item in Cats) { writer.WritePropertyName(item.Key); writer.WriteObjectValue(item.Value, options); } } - - Patch.WriteTo(writer, "$.cats"u8); writer.WriteEndObject(); } if (!Patch.Contains("$.names"u8)) { writer.WritePropertyName("names"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "names"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in Names) - { + foreach (var item in Names) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.names"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.names"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.names"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.names"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.names"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.names"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + if ((item.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStringValue(item.Value); + } + } + + Patch.WriteTo(writer, "$.names"u8); + } + else + { + foreach (var item in Names) { writer.WritePropertyName(item.Key); if ((item.Value == null)) @@ -88,26 +119,43 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite writer.WriteStringValue(item.Value); } } - - Patch.WriteTo(writer, "$.names"u8); writer.WriteEndObject(); } if ((global::Sample.Optional.IsCollectionDefined(OptionalNames) && !Patch.Contains("$.optionalNames"u8))) { writer.WritePropertyName("optionalNames"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "optionalNames"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in OptionalNames) - { + foreach (var item in OptionalNames) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.optionalNames"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.optionalNames"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.optionalNames"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.optionalNames"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.optionalNames"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.optionalNames"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + if ((item.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStringValue(item.Value); + } + } + + Patch.WriteTo(writer, "$.optionalNames"u8); + } + else + { + foreach (var item in OptionalNames) { writer.WritePropertyName(item.Key); if ((item.Value == null)) @@ -118,8 +166,6 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite writer.WriteStringValue(item.Value); } } - - Patch.WriteTo(writer, "$.optionalNames"u8); writer.WriteEndObject(); } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedArrayDictionaryProperties.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedArrayDictionaryProperties.cs index 3ffca3dc201..d0044bbe1e2 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedArrayDictionaryProperties.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedArrayDictionaryProperties.cs @@ -49,9 +49,10 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite { writer.WritePropertyName("propertyWithNestedArray"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "propertyWithNestedArray"u8); for (int i = 0; (i < PropertyWithNestedArray.Count); i++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}]")))) { continue; } @@ -63,7 +64,7 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite writer.WriteStartArray(); for (int i0 = 0; (i0 < PropertyWithNestedArray[i].Count); i0++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}]")))) { continue; } @@ -75,7 +76,7 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite writer.WriteStartArray(); for (int i1 = 0; (i1 < PropertyWithNestedArray[i][i0].Count); i1++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]")))) { continue; } @@ -85,18 +86,36 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite continue; } writer.WriteStartObject(); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in PropertyWithNestedArray[i][i0][i1]) - { + foreach (var item in PropertyWithNestedArray[i][i0][i1]) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]"), global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]"), buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]"), global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]"), buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]"), global::System.Text.Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]"), global::System.Text.Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + if ((item.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStringValue(item.Value); + } + } + + Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]")); + } + else + { + foreach (var item in PropertyWithNestedArray[i][i0][i1]) { writer.WritePropertyName(item.Key); if ((item.Value == null)) @@ -107,14 +126,18 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite writer.WriteStringValue(item.Value); } } - - Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]")); writer.WriteEndObject(); } - Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}]")); + if (hasPatch) + { + Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}]")); + } writer.WriteEndArray(); } - Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}]")); + if (hasPatch) + { + Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}]")); + } writer.WriteEndArray(); } Patch.WriteTo(writer, "$.propertyWithNestedArray"u8); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedArrayDynamicModelProperties.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedArrayDynamicModelProperties.cs index 58f44014717..3c0976f2e80 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedArrayDynamicModelProperties.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedArrayDynamicModelProperties.cs @@ -49,9 +49,10 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite { writer.WritePropertyName("propertyWithNestedArray"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "propertyWithNestedArray"u8); for (int i = 0; (i < PropertyWithNestedArray.Count); i++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}]")))) { continue; } @@ -63,7 +64,7 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite writer.WriteStartArray(); for (int i0 = 0; (i0 < PropertyWithNestedArray[i].Count); i0++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}]")))) { continue; } @@ -75,16 +76,22 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite writer.WriteStartArray(); for (int i1 = 0; (i1 < PropertyWithNestedArray[i][i0].Count); i1++) { - if ((Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]")) || ((PropertyWithNestedArray[i][i0][i1] != null) && PropertyWithNestedArray[i][i0][i1].Patch.IsRemoved("$"u8)))) + if (((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]"))) || ((PropertyWithNestedArray[i][i0][i1] != null) && PropertyWithNestedArray[i][i0][i1].Patch.IsRemoved("$"u8)))) { continue; } writer.WriteObjectValue(PropertyWithNestedArray[i][i0][i1], options); } - Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}]")); + if (hasPatch) + { + Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}]")); + } writer.WriteEndArray(); } - Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}]")); + if (hasPatch) + { + Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}]")); + } writer.WriteEndArray(); } Patch.WriteTo(writer, "$.propertyWithNestedArray"u8); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedArrayPrimitiveProperties.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedArrayPrimitiveProperties.cs index 0db787e69fd..964bfffd55f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedArrayPrimitiveProperties.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedArrayPrimitiveProperties.cs @@ -49,9 +49,10 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite { writer.WritePropertyName("propertyWithNestedArray"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "propertyWithNestedArray"u8); for (int i = 0; (i < PropertyWithNestedArray.Count); i++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}]")))) { continue; } @@ -63,7 +64,7 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite writer.WriteStartArray(); for (int i0 = 0; (i0 < PropertyWithNestedArray[i].Count); i0++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}]")))) { continue; } @@ -75,7 +76,7 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite writer.WriteStartArray(); for (int i1 = 0; (i1 < PropertyWithNestedArray[i][i0].Count); i1++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}][{i1}]")))) { continue; } @@ -86,10 +87,16 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite } writer.WriteStringValue(PropertyWithNestedArray[i][i0][i1]); } - Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}]")); + if (hasPatch) + { + Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}][{i0}]")); + } writer.WriteEndArray(); } - Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}]")); + if (hasPatch) + { + Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedArray[{i}]")); + } writer.WriteEndArray(); } Patch.WriteTo(writer, "$.propertyWithNestedArray"u8); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedDictDynamicModelProperties.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedDictDynamicModelProperties.cs index e2997e91593..9e7dfdafb1e 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedDictDynamicModelProperties.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedDictDynamicModelProperties.cs @@ -41,75 +41,142 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite { writer.WritePropertyName("propertyWithNestedDictionary"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "propertyWithNestedDictionary"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in PropertyWithNestedDictionary) - { + foreach (var item in PropertyWithNestedDictionary) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.propertyWithNestedDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.propertyWithNestedDictionary"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.propertyWithNestedDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.propertyWithNestedDictionary"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.propertyWithNestedDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.propertyWithNestedDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) - { - writer.WritePropertyName(item.Key); - if ((item.Value == null)) + if (!patchContains) { - writer.WriteNullValue(); - continue; - } - writer.WriteStartObject(); + writer.WritePropertyName(item.Key); + if ((item.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer0 = stackalloc byte[256]; + global::System.Span buffer0 = stackalloc byte[256]; #endif - foreach (var item0 in item.Value) - { + foreach (var item0 in item.Value) + { #if NET8_0_OR_GREATER - int bytesWritten0 = global::System.Text.Encoding.UTF8.GetBytes(item0.Key.AsSpan(), buffer0); - bool patchContains0 = (bytesWritten0 == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item0.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]"), buffer0.Slice(0, bytesWritten0)); + int bytesWritten0 = global::System.Text.Encoding.UTF8.GetBytes(item0.Key.AsSpan(), buffer0); + bool patchContains0 = (bytesWritten0 == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item0.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]"), buffer0.Slice(0, bytesWritten0)); #else - bool patchContains0 = Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item0.Key)); + bool patchContains0 = Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item0.Key)); #endif - if (!patchContains0) - { - writer.WritePropertyName(item0.Key); - if ((item0.Value == null)) - { - writer.WriteNullValue(); - continue; - } - writer.WriteStartObject(); + if (!patchContains0) + { + writer.WritePropertyName(item0.Key); + if ((item0.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer1 = stackalloc byte[256]; + global::System.Span buffer1 = stackalloc byte[256]; #endif - foreach (var item1 in item0.Value) - { + foreach (var item1 in item0.Value) + { #if NET8_0_OR_GREATER - int bytesWritten1 = global::System.Text.Encoding.UTF8.GetBytes(item1.Key.AsSpan(), buffer1); - bool patchContains1 = (bytesWritten1 == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item1.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]"), buffer1.Slice(0, bytesWritten1)); + int bytesWritten1 = global::System.Text.Encoding.UTF8.GetBytes(item1.Key.AsSpan(), buffer1); + bool patchContains1 = (bytesWritten1 == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item1.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]"), buffer1.Slice(0, bytesWritten1)); #else - bool patchContains1 = Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item1.Key)); + bool patchContains1 = Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item1.Key)); #endif - if (!patchContains1) + if (!patchContains1) + { + writer.WritePropertyName(item1.Key); + writer.WriteObjectValue(item1.Value, options); + } + } + + Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]")); + } + else + { + foreach (var item1 in item0.Value) + { + writer.WritePropertyName(item1.Key); + writer.WriteObjectValue(item1.Value, options); + } + } + writer.WriteEndObject(); + } + } + + Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]")); + } + else + { + foreach (var item0 in item.Value) + { + writer.WritePropertyName(item0.Key); + if ((item0.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); + foreach (var item1 in item0.Value) { writer.WritePropertyName(item1.Key); writer.WriteObjectValue(item1.Value, options); } + writer.WriteEndObject(); } - - Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]")); - writer.WriteEndObject(); } + writer.WriteEndObject(); } + } - Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]")); + Patch.WriteTo(writer, "$.propertyWithNestedDictionary"u8); + } + else + { + foreach (var item in PropertyWithNestedDictionary) + { + writer.WritePropertyName(item.Key); + if ((item.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); + foreach (var item0 in item.Value) + { + writer.WritePropertyName(item0.Key); + if ((item0.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); + foreach (var item1 in item0.Value) + { + writer.WritePropertyName(item1.Key); + writer.WriteObjectValue(item1.Value, options); + } + writer.WriteEndObject(); + } writer.WriteEndObject(); } } - - Patch.WriteTo(writer, "$.propertyWithNestedDictionary"u8); writer.WriteEndObject(); } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedDictPrimitiveProperties.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedDictPrimitiveProperties.cs index 0dc4862a482..0b79e811907 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedDictPrimitiveProperties.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteNestedDictPrimitiveProperties.cs @@ -41,58 +41,109 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite { writer.WritePropertyName("propertyWithNestedDictionary"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "propertyWithNestedDictionary"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in PropertyWithNestedDictionary) - { + foreach (var item in PropertyWithNestedDictionary) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.propertyWithNestedDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.propertyWithNestedDictionary"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.propertyWithNestedDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.propertyWithNestedDictionary"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.propertyWithNestedDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.propertyWithNestedDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) - { - writer.WritePropertyName(item.Key); - if ((item.Value == null)) + if (!patchContains) { - writer.WriteNullValue(); - continue; - } - writer.WriteStartObject(); + writer.WritePropertyName(item.Key); + if ((item.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer0 = stackalloc byte[256]; + global::System.Span buffer0 = stackalloc byte[256]; #endif - foreach (var item0 in item.Value) - { + foreach (var item0 in item.Value) + { #if NET8_0_OR_GREATER - int bytesWritten0 = global::System.Text.Encoding.UTF8.GetBytes(item0.Key.AsSpan(), buffer0); - bool patchContains0 = (bytesWritten0 == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item0.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]"), buffer0.Slice(0, bytesWritten0)); + int bytesWritten0 = global::System.Text.Encoding.UTF8.GetBytes(item0.Key.AsSpan(), buffer0); + bool patchContains0 = (bytesWritten0 == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item0.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]"), buffer0.Slice(0, bytesWritten0)); #else - bool patchContains0 = Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item0.Key)); + bool patchContains0 = Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item0.Key)); #endif - if (!patchContains0) - { - writer.WritePropertyName(item0.Key); - if ((item0.Value == null)) - { - writer.WriteNullValue(); - continue; - } - writer.WriteStartObject(); + if (!patchContains0) + { + writer.WritePropertyName(item0.Key); + if ((item0.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer1 = stackalloc byte[256]; + global::System.Span buffer1 = stackalloc byte[256]; #endif - foreach (var item1 in item0.Value) - { + foreach (var item1 in item0.Value) + { #if NET8_0_OR_GREATER - int bytesWritten1 = global::System.Text.Encoding.UTF8.GetBytes(item1.Key.AsSpan(), buffer1); - bool patchContains1 = (bytesWritten1 == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item1.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]"), buffer1.Slice(0, bytesWritten1)); + int bytesWritten1 = global::System.Text.Encoding.UTF8.GetBytes(item1.Key.AsSpan(), buffer1); + bool patchContains1 = (bytesWritten1 == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item1.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]"), buffer1.Slice(0, bytesWritten1)); #else - bool patchContains1 = Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item1.Key)); + bool patchContains1 = Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item1.Key)); #endif - if (!patchContains1) + if (!patchContains1) + { + writer.WritePropertyName(item1.Key); + if ((item1.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStringValue(item1.Value); + } + } + + Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]")); + } + else + { + foreach (var item1 in item0.Value) + { + writer.WritePropertyName(item1.Key); + if ((item1.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStringValue(item1.Value); + } + } + writer.WriteEndObject(); + } + } + + Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]")); + } + else + { + foreach (var item0 in item.Value) + { + writer.WritePropertyName(item0.Key); + if ((item0.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); + foreach (var item1 in item0.Value) { writer.WritePropertyName(item1.Key); if ((item1.Value == null)) @@ -102,19 +153,50 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite } writer.WriteStringValue(item1.Value); } + writer.WriteEndObject(); } - - Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"][\"{item0.Key}\"]")); - writer.WriteEndObject(); } + writer.WriteEndObject(); } + } - Patch.WriteTo(writer, global::System.Text.Encoding.UTF8.GetBytes($"$.propertyWithNestedDictionary[\"{item.Key}\"]")); + Patch.WriteTo(writer, "$.propertyWithNestedDictionary"u8); + } + else + { + foreach (var item in PropertyWithNestedDictionary) + { + writer.WritePropertyName(item.Key); + if ((item.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); + foreach (var item0 in item.Value) + { + writer.WritePropertyName(item0.Key); + if ((item0.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); + foreach (var item1 in item0.Value) + { + writer.WritePropertyName(item1.Key); + if ((item1.Value == null)) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStringValue(item1.Value); + } + writer.WriteEndObject(); + } writer.WriteEndObject(); } } - - Patch.WriteTo(writer, "$.propertyWithNestedDictionary"u8); writer.WriteEndObject(); } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteReadOnlySpanProperty.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteReadOnlySpanProperty.cs index 0b53a13e9e4..7443ddf7dd5 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteReadOnlySpanProperty.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteReadOnlySpanProperty.cs @@ -49,9 +49,10 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite { writer.WritePropertyName("someSpan"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "someSpan"u8); for (int i = 0; (i < SomeSpan.Span.Length); i++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.someSpan[{i}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.someSpan[{i}]")))) { continue; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteRequiredCollectionDoesNotDuplicatePatchedKey.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteRequiredCollectionDoesNotDuplicatePatchedKey.cs index 4e6fcc67213..9ee1bdb8742 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteRequiredCollectionDoesNotDuplicatePatchedKey.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/DynamicModelSerializationTests/WriteRequiredCollectionDoesNotDuplicatePatchedKey.cs @@ -10,9 +10,10 @@ { writer.WritePropertyName("tools"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "tools"u8); for (int i = 0; (i < Tools.Count); i++) { - if (Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.tools[{i}]"))) + if ((hasPatch && Patch.IsRemoved(global::System.Text.Encoding.UTF8.GetBytes($"$.tools[{i}]")))) { continue; } diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/DynamicModel.Serialization.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/DynamicModel.Serialization.cs index b8ae775f896..7fac448642e 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/DynamicModel.Serialization.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/DynamicModel.Serialization.cs @@ -133,9 +133,10 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("optionalNullableList"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "optionalNullableList"u8); for (int i = 0; i < OptionalNullableList.Count; i++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.optionalNullableList[{i}]"))) + if (hasPatch && Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.optionalNullableList[{i}]"))) { continue; } @@ -156,9 +157,10 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("requiredNullableList"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "requiredNullableList"u8); for (int i = 0; i < RequiredNullableList.Count; i++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.requiredNullableList[{i}]"))) + if (hasPatch && Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.requiredNullableList[{i}]"))) { continue; } @@ -175,50 +177,74 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("optionalNullableDictionary"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "optionalNullableDictionary"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in OptionalNullableDictionary) - { + foreach (var item in OptionalNullableDictionary) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.optionalNullableDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.optionalNullableDictionary"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.optionalNullableDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.optionalNullableDictionary"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.optionalNullableDictionary"u8, Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.optionalNullableDictionary"u8, Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + writer.WriteNumberValue(item.Value); + } + } + + Patch.WriteTo(writer, "$.optionalNullableDictionary"u8); + } + else + { + foreach (var item in OptionalNullableDictionary) { writer.WritePropertyName(item.Key); writer.WriteNumberValue(item.Value); } } - - Patch.WriteTo(writer, "$.optionalNullableDictionary"u8); writer.WriteEndObject(); } if (Optional.IsCollectionDefined(RequiredNullableDictionary) && !Patch.Contains("$.requiredNullableDictionary"u8)) { writer.WritePropertyName("requiredNullableDictionary"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "requiredNullableDictionary"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in RequiredNullableDictionary) - { + foreach (var item in RequiredNullableDictionary) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.requiredNullableDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.requiredNullableDictionary"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.requiredNullableDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.requiredNullableDictionary"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.requiredNullableDictionary"u8, Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.requiredNullableDictionary"u8, Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + writer.WriteNumberValue(item.Value); + } + } + + Patch.WriteTo(writer, "$.requiredNullableDictionary"u8); + } + else + { + foreach (var item in RequiredNullableDictionary) { writer.WritePropertyName(item.Key); writer.WriteNumberValue(item.Value); } } - - Patch.WriteTo(writer, "$.requiredNullableDictionary"u8); writer.WriteEndObject(); } else if (!Patch.Contains("$.requiredNullableDictionary"u8)) @@ -229,25 +255,37 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("primitiveDictionary"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "primitiveDictionary"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in PrimitiveDictionary) - { + foreach (var item in PrimitiveDictionary) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.primitiveDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.primitiveDictionary"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.primitiveDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.primitiveDictionary"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.primitiveDictionary"u8, Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.primitiveDictionary"u8, Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + writer.WriteNumberValue(item.Value); + } + } + + Patch.WriteTo(writer, "$.primitiveDictionary"u8); + } + else + { + foreach (var item in PrimitiveDictionary) { writer.WritePropertyName(item.Key); writer.WriteNumberValue(item.Value); } } - - Patch.WriteTo(writer, "$.primitiveDictionary"u8); writer.WriteEndObject(); } if (!Patch.Contains("$.foo"u8)) @@ -267,9 +305,10 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("listFoo"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "listFoo"u8); for (int i = 0; i < ListFoo.Count; i++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listFoo[{i}]")) || ListFoo[i] != null && ListFoo[i].Patch.IsRemoved("$"u8)) + if (hasPatch && Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listFoo[{i}]")) || ListFoo[i] != null && ListFoo[i].Patch.IsRemoved("$"u8)) { continue; } @@ -290,9 +329,10 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("listOfListFoo"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "listOfListFoo"u8); for (int i = 0; i < ListOfListFoo.Count; i++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listOfListFoo[{i}]"))) + if (hasPatch && Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listOfListFoo[{i}]"))) { continue; } @@ -304,13 +344,16 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit writer.WriteStartArray(); for (int i0 = 0; i0 < ListOfListFoo[i].Count; i0++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listOfListFoo[{i}][{i0}]")) || ListOfListFoo[i][i0] != null && ListOfListFoo[i][i0].Patch.IsRemoved("$"u8)) + if (hasPatch && Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listOfListFoo[{i}][{i0}]")) || ListOfListFoo[i][i0] != null && ListOfListFoo[i][i0].Patch.IsRemoved("$"u8)) { continue; } writer.WriteObjectValue(ListOfListFoo[i][i0], options); } - Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.listOfListFoo[{i}]")); + if (hasPatch) + { + Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.listOfListFoo[{i}]")); + } writer.WriteEndArray(); } Patch.WriteTo(writer, "$.listOfListFoo"u8); @@ -320,43 +363,105 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("dictionaryFoo"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "dictionaryFoo"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in DictionaryFoo) - { + foreach (var item in DictionaryFoo) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.dictionaryFoo"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.dictionaryFoo"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.dictionaryFoo"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.dictionaryFoo"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.dictionaryFoo"u8, Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.dictionaryFoo"u8, Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + writer.WriteObjectValue(item.Value, options); + } + } + + Patch.WriteTo(writer, "$.dictionaryFoo"u8); + } + else + { + foreach (var item in DictionaryFoo) { writer.WritePropertyName(item.Key); writer.WriteObjectValue(item.Value, options); } } - - Patch.WriteTo(writer, "$.dictionaryFoo"u8); writer.WriteEndObject(); } if (!Patch.Contains("$.dictionaryOfDictionaryFoo"u8)) { writer.WritePropertyName("dictionaryOfDictionaryFoo"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "dictionaryOfDictionaryFoo"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in DictionaryOfDictionaryFoo) - { + foreach (var item in DictionaryOfDictionaryFoo) + { +#if NET8_0_OR_GREATER + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.dictionaryOfDictionaryFoo"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.dictionaryOfDictionaryFoo"u8, buffer.Slice(0, bytesWritten)); +#else + bool patchContains = Patch.Contains("$.dictionaryOfDictionaryFoo"u8, Encoding.UTF8.GetBytes(item.Key)); +#endif + if (!patchContains) + { + writer.WritePropertyName(item.Key); + if (item.Value == null) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); + if (hasPatch) + { +#if NET8_0_OR_GREATER + global::System.Span buffer0 = stackalloc byte[256]; +#endif + foreach (var item0 in item.Value) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.dictionaryOfDictionaryFoo"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.dictionaryOfDictionaryFoo"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten0 = global::System.Text.Encoding.UTF8.GetBytes(item0.Key.AsSpan(), buffer0); + bool patchContains0 = (bytesWritten0 == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.dictionaryOfDictionaryFoo[\"{item.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item0.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.dictionaryOfDictionaryFoo[\"{item.Key}\"]"), buffer0.Slice(0, bytesWritten0)); #else - bool patchContains = Patch.Contains("$.dictionaryOfDictionaryFoo"u8, Encoding.UTF8.GetBytes(item.Key)); + bool patchContains0 = Patch.Contains(Encoding.UTF8.GetBytes($"$.dictionaryOfDictionaryFoo[\"{item.Key}\"]"), Encoding.UTF8.GetBytes(item0.Key)); #endif - if (!patchContains) + if (!patchContains0) + { + writer.WritePropertyName(item0.Key); + writer.WriteObjectValue(item0.Value, options); + } + } + + Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.dictionaryOfDictionaryFoo[\"{item.Key}\"]")); + } + else + { + foreach (var item0 in item.Value) + { + writer.WritePropertyName(item0.Key); + writer.WriteObjectValue(item0.Value, options); + } + } + writer.WriteEndObject(); + } + } + + Patch.WriteTo(writer, "$.dictionaryOfDictionaryFoo"u8); + } + else + { + foreach (var item in DictionaryOfDictionaryFoo) { writer.WritePropertyName(item.Key); if (item.Value == null) @@ -365,48 +470,64 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit continue; } writer.WriteStartObject(); -#if NET8_0_OR_GREATER - global::System.Span buffer0 = stackalloc byte[256]; -#endif foreach (var item0 in item.Value) { -#if NET8_0_OR_GREATER - int bytesWritten0 = global::System.Text.Encoding.UTF8.GetBytes(item0.Key.AsSpan(), buffer0); - bool patchContains0 = (bytesWritten0 == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.dictionaryOfDictionaryFoo[\"{item.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item0.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.dictionaryOfDictionaryFoo[\"{item.Key}\"]"), buffer0.Slice(0, bytesWritten0)); -#else - bool patchContains0 = Patch.Contains(Encoding.UTF8.GetBytes($"$.dictionaryOfDictionaryFoo[\"{item.Key}\"]"), Encoding.UTF8.GetBytes(item0.Key)); -#endif - if (!patchContains0) - { - writer.WritePropertyName(item0.Key); - writer.WriteObjectValue(item0.Value, options); - } + writer.WritePropertyName(item0.Key); + writer.WriteObjectValue(item0.Value, options); } - - Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.dictionaryOfDictionaryFoo[\"{item.Key}\"]")); writer.WriteEndObject(); } } - - Patch.WriteTo(writer, "$.dictionaryOfDictionaryFoo"u8); writer.WriteEndObject(); } if (!Patch.Contains("$.dictionaryListFoo"u8)) { writer.WritePropertyName("dictionaryListFoo"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "dictionaryListFoo"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in DictionaryListFoo) - { + foreach (var item in DictionaryListFoo) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.dictionaryListFoo"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.dictionaryListFoo"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.dictionaryListFoo"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.dictionaryListFoo"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.dictionaryListFoo"u8, Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.dictionaryListFoo"u8, Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + if (item.Value == null) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartArray(); + for (int i = 0; i < item.Value.Count; i++) + { + if (hasPatch && Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.dictionaryListFoo[\"{item.Key}\"][{i}]")) || item.Value[i] != null && item.Value[i].Patch.IsRemoved("$"u8)) + { + continue; + } + writer.WriteObjectValue(item.Value[i], options); + } + if (hasPatch) + { + Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.dictionaryListFoo[\"{item.Key}\"]")); + } + writer.WriteEndArray(); + } + } + + Patch.WriteTo(writer, "$.dictionaryListFoo"u8); + } + else + { + foreach (var item in DictionaryListFoo) { writer.WritePropertyName(item.Key); if (item.Value == null) @@ -417,18 +538,15 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit writer.WriteStartArray(); for (int i = 0; i < item.Value.Count; i++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.dictionaryListFoo[\"{item.Key}\"][{i}]")) || item.Value[i] != null && item.Value[i].Patch.IsRemoved("$"u8)) + if (item.Value[i] != null && item.Value[i].Patch.IsRemoved("$"u8)) { continue; } writer.WriteObjectValue(item.Value[i], options); } - Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.dictionaryListFoo[\"{item.Key}\"]")); writer.WriteEndArray(); } } - - Patch.WriteTo(writer, "$.dictionaryListFoo"u8); writer.WriteEndObject(); } if (Patch.Contains("$.listOfDictionaryFoo"u8)) @@ -443,9 +561,10 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("listOfDictionaryFoo"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "listOfDictionaryFoo"u8); for (int i = 0; i < ListOfDictionaryFoo.Count; i++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listOfDictionaryFoo[{i}]"))) + if (hasPatch && Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listOfDictionaryFoo[{i}]"))) { continue; } @@ -455,25 +574,36 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit continue; } writer.WriteStartObject(); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in ListOfDictionaryFoo[i]) - { + foreach (var item in ListOfDictionaryFoo[i]) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.listOfDictionaryFoo[{i}]"), global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.listOfDictionaryFoo[{i}]"), buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.listOfDictionaryFoo[{i}]"), global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.listOfDictionaryFoo[{i}]"), buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains(Encoding.UTF8.GetBytes($"$.listOfDictionaryFoo[{i}]"), Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains(Encoding.UTF8.GetBytes($"$.listOfDictionaryFoo[{i}]"), Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + writer.WriteObjectValue(item.Value, options); + } + } + + Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.listOfDictionaryFoo[{i}]")); + } + else + { + foreach (var item in ListOfDictionaryFoo[i]) { writer.WritePropertyName(item.Key); writer.WriteObjectValue(item.Value, options); } } - - Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.listOfDictionaryFoo[{i}]")); writer.WriteEndObject(); } Patch.WriteTo(writer, "$.listOfDictionaryFoo"u8); @@ -1130,9 +1260,10 @@ private IEnumerable ActiveListFoo() { yield break; } + bool hasPatch = Patch.Contains("$"u8, "listFoo"u8); for (int i = 0; i < ListFoo.Count; i++) { - if (!Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listFoo[{i}]")) && (ListFoo[i] == null || !ListFoo[i].Patch.IsRemoved("$"u8))) + if ((!hasPatch || !Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listFoo[{i}]"))) && (ListFoo[i] == null || !ListFoo[i].Patch.IsRemoved("$"u8))) { yield return ListFoo[i]; } diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/NullableDynamicModel.Serialization.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/NullableDynamicModel.Serialization.cs index c4c251836ac..60ec553e5b7 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/NullableDynamicModel.Serialization.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/NullableDynamicModel.Serialization.cs @@ -100,9 +100,10 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("children"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "children"u8); for (int i = 0; i < Children.Count; i++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.children[{i}]")) || Children[i] != null && Children[i].Patch.IsRemoved("$"u8)) + if (hasPatch && Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.children[{i}]")) || Children[i] != null && Children[i].Patch.IsRemoved("$"u8)) { continue; } @@ -115,25 +116,37 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("childDictionary"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "childDictionary"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in ChildDictionary) - { + foreach (var item in ChildDictionary) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.childDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.childDictionary"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.childDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.childDictionary"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.childDictionary"u8, Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.childDictionary"u8, Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + writer.WriteObjectValue(item.Value, options); + } + } + + Patch.WriteTo(writer, "$.childDictionary"u8); + } + else + { + foreach (var item in ChildDictionary) { writer.WritePropertyName(item.Key); writer.WriteObjectValue(item.Value, options); } } - - Patch.WriteTo(writer, "$.childDictionary"u8); writer.WriteEndObject(); } if (Patch.Contains("$.nestedChildren"u8)) @@ -148,9 +161,10 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("nestedChildren"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "nestedChildren"u8); for (int i = 0; i < NestedChildren.Count; i++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.nestedChildren[{i}]"))) + if (hasPatch && Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.nestedChildren[{i}]"))) { continue; } @@ -162,13 +176,16 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit writer.WriteStartArray(); for (int i0 = 0; i0 < NestedChildren[i].Count; i0++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.nestedChildren[{i}][{i0}]")) || NestedChildren[i][i0] != null && NestedChildren[i][i0].Patch.IsRemoved("$"u8)) + if (hasPatch && Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.nestedChildren[{i}][{i0}]")) || NestedChildren[i][i0] != null && NestedChildren[i][i0].Patch.IsRemoved("$"u8)) { continue; } writer.WriteObjectValue(NestedChildren[i][i0], options); } - Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.nestedChildren[{i}]")); + if (hasPatch) + { + Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.nestedChildren[{i}]")); + } writer.WriteEndArray(); } Patch.WriteTo(writer, "$.nestedChildren"u8); @@ -178,18 +195,68 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("nestedChildDictionary"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "nestedChildDictionary"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in NestedChildDictionary) - { + foreach (var item in NestedChildDictionary) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.nestedChildDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.nestedChildDictionary"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.nestedChildDictionary"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.nestedChildDictionary"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.nestedChildDictionary"u8, Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.nestedChildDictionary"u8, Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + if (item.Value == null) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); + if (hasPatch) + { +#if NET8_0_OR_GREATER + global::System.Span buffer0 = stackalloc byte[256]; +#endif + foreach (var item0 in item.Value) + { +#if NET8_0_OR_GREATER + int bytesWritten0 = global::System.Text.Encoding.UTF8.GetBytes(item0.Key.AsSpan(), buffer0); + bool patchContains0 = (bytesWritten0 == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.nestedChildDictionary[\"{item.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item0.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.nestedChildDictionary[\"{item.Key}\"]"), buffer0.Slice(0, bytesWritten0)); +#else + bool patchContains0 = Patch.Contains(Encoding.UTF8.GetBytes($"$.nestedChildDictionary[\"{item.Key}\"]"), Encoding.UTF8.GetBytes(item0.Key)); +#endif + if (!patchContains0) + { + writer.WritePropertyName(item0.Key); + writer.WriteObjectValue(item0.Value, options); + } + } + + Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.nestedChildDictionary[\"{item.Key}\"]")); + } + else + { + foreach (var item0 in item.Value) + { + writer.WritePropertyName(item0.Key); + writer.WriteObjectValue(item0.Value, options); + } + } + writer.WriteEndObject(); + } + } + + Patch.WriteTo(writer, "$.nestedChildDictionary"u8); + } + else + { + foreach (var item in NestedChildDictionary) { writer.WritePropertyName(item.Key); if (item.Value == null) @@ -198,48 +265,64 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit continue; } writer.WriteStartObject(); -#if NET8_0_OR_GREATER - global::System.Span buffer0 = stackalloc byte[256]; -#endif foreach (var item0 in item.Value) { -#if NET8_0_OR_GREATER - int bytesWritten0 = global::System.Text.Encoding.UTF8.GetBytes(item0.Key.AsSpan(), buffer0); - bool patchContains0 = (bytesWritten0 == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.nestedChildDictionary[\"{item.Key}\"]"), global::System.Text.Encoding.UTF8.GetBytes(item0.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.nestedChildDictionary[\"{item.Key}\"]"), buffer0.Slice(0, bytesWritten0)); -#else - bool patchContains0 = Patch.Contains(Encoding.UTF8.GetBytes($"$.nestedChildDictionary[\"{item.Key}\"]"), Encoding.UTF8.GetBytes(item0.Key)); -#endif - if (!patchContains0) - { - writer.WritePropertyName(item0.Key); - writer.WriteObjectValue(item0.Value, options); - } + writer.WritePropertyName(item0.Key); + writer.WriteObjectValue(item0.Value, options); } - - Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.nestedChildDictionary[\"{item.Key}\"]")); writer.WriteEndObject(); } } - - Patch.WriteTo(writer, "$.nestedChildDictionary"u8); writer.WriteEndObject(); } if (Optional.IsCollectionDefined(DictionaryChildren) && !Patch.Contains("$.dictionaryChildren"u8)) { writer.WritePropertyName("dictionaryChildren"u8); writer.WriteStartObject(); + bool hasPatch = Patch.Contains("$"u8, "dictionaryChildren"u8); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in DictionaryChildren) - { + foreach (var item in DictionaryChildren) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.dictionaryChildren"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.dictionaryChildren"u8, buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains("$.dictionaryChildren"u8, global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains("$.dictionaryChildren"u8, buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains("$.dictionaryChildren"u8, Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains("$.dictionaryChildren"u8, Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + if (item.Value == null) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartArray(); + for (int i = 0; i < item.Value.Count; i++) + { + if (hasPatch && Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.dictionaryChildren[\"{item.Key}\"][{i}]")) || item.Value[i] != null && item.Value[i].Patch.IsRemoved("$"u8)) + { + continue; + } + writer.WriteObjectValue(item.Value[i], options); + } + if (hasPatch) + { + Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.dictionaryChildren[\"{item.Key}\"]")); + } + writer.WriteEndArray(); + } + } + + Patch.WriteTo(writer, "$.dictionaryChildren"u8); + } + else + { + foreach (var item in DictionaryChildren) { writer.WritePropertyName(item.Key); if (item.Value == null) @@ -250,18 +333,15 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit writer.WriteStartArray(); for (int i = 0; i < item.Value.Count; i++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.dictionaryChildren[\"{item.Key}\"][{i}]")) || item.Value[i] != null && item.Value[i].Patch.IsRemoved("$"u8)) + if (item.Value[i] != null && item.Value[i].Patch.IsRemoved("$"u8)) { continue; } writer.WriteObjectValue(item.Value[i], options); } - Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.dictionaryChildren[\"{item.Key}\"]")); writer.WriteEndArray(); } } - - Patch.WriteTo(writer, "$.dictionaryChildren"u8); writer.WriteEndObject(); } if (Patch.Contains("$.listOfDictionaries"u8)) @@ -276,9 +356,10 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("listOfDictionaries"u8); writer.WriteStartArray(); + bool hasPatch = Patch.Contains("$"u8, "listOfDictionaries"u8); for (int i = 0; i < ListOfDictionaries.Count; i++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]"))) + if (hasPatch && Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]"))) { continue; } @@ -288,25 +369,36 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit continue; } writer.WriteStartObject(); + if (hasPatch) + { #if NET8_0_OR_GREATER - global::System.Span buffer = stackalloc byte[256]; + global::System.Span buffer = stackalloc byte[256]; #endif - foreach (var item in ListOfDictionaries[i]) - { + foreach (var item in ListOfDictionaries[i]) + { #if NET8_0_OR_GREATER - int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); - bool patchContains = (bytesWritten == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]"), global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]"), buffer.Slice(0, bytesWritten)); + int bytesWritten = global::System.Text.Encoding.UTF8.GetBytes(item.Key.AsSpan(), buffer); + bool patchContains = (bytesWritten == 256) ? Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]"), global::System.Text.Encoding.UTF8.GetBytes(item.Key)) : Patch.Contains(global::System.Text.Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]"), buffer.Slice(0, bytesWritten)); #else - bool patchContains = Patch.Contains(Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]"), Encoding.UTF8.GetBytes(item.Key)); + bool patchContains = Patch.Contains(Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]"), Encoding.UTF8.GetBytes(item.Key)); #endif - if (!patchContains) + if (!patchContains) + { + writer.WritePropertyName(item.Key); + writer.WriteObjectValue(item.Value, options); + } + } + + Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]")); + } + else + { + foreach (var item in ListOfDictionaries[i]) { writer.WritePropertyName(item.Key); writer.WriteObjectValue(item.Value, options); } } - - Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]")); writer.WriteEndObject(); } Patch.WriteTo(writer, "$.listOfDictionaries"u8); @@ -885,9 +977,10 @@ private IEnumerable ActiveChildren() { yield break; } + bool hasPatch = Patch.Contains("$"u8, "children"u8); for (int i = 0; i < Children.Count; i++) { - if (!Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.children[{i}]")) && (Children[i] == null || !Children[i].Patch.IsRemoved("$"u8))) + if ((!hasPatch || !Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.children[{i}]"))) && (Children[i] == null || !Children[i].Patch.IsRemoved("$"u8))) { yield return Children[i]; }