diff --git a/packages/http-client-csharp/.tspd/docs/customization.md b/packages/http-client-csharp/.tspd/docs/customization.md index 98bb0bbbb20..c46fc90fb1f 100644 --- a/packages/http-client-csharp/.tspd/docs/customization.md +++ b/packages/http-client-csharp/.tspd/docs/customization.md @@ -6,6 +6,14 @@ Before customizing generated code, consider whether your change should be made i Use C# code customizations (partial classes) when TypeSpec cannot express the behavior you need. +## Optional nullable model properties + +For a TypeSpec property such as `feature?: Feature | null`, generated model classes distinguish an omitted property from an explicitly assigned `null` during JSON serialization. Leaving the property untouched omits it; assigning `null` writes `"feature": null`; assigning a value writes that value. Reading JSON preserves the same distinction when the model is serialized again, including nested models and wire-format serialization. + +Presence tracking does not change the public property type or constructor/model-factory signatures. Defaulted constructor and model-factory arguments retain their existing behavior: a scalar `null` argument is treated as omitted, since C# cannot distinguish an omitted argument from an explicitly supplied default value. Model factories also retain their existing collection initialization behavior, which can materialize a defaulted list argument as an empty list. To write explicit null for a writable property, assign the property after construction. + +A handwritten property or field that replaces a generated member (including a `[CodeGenMember]` replacement) retains its existing customization behavior; the generator cannot observe assignments inside a handwritten setter. There is no presence-tracking opt-in for replacement members. Models customized as readonly structs also retain their existing constructor-only behavior, because readonly structs cannot contain the mutable backing fields and presence flags used by generated classes. Keep the generated class property when its omitted/null distinction is needed. Similarly, plugins that replace generated property bodies or serialization methods must preserve the tracking behavior themselves rather than relying on a replacement auto-property. + ## Make a model internal Define a class with the same namespace and name as generated model and use the desired accessibility. diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.Xml.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.Xml.cs index 55c993eb5aa..327f3ce82a2 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.Xml.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.Xml.cs @@ -622,7 +622,7 @@ private MethodBodyStatement[] BuildXmlDeserializationMethodBody() new IfStatement(_xmlElementParameterSnippet.Equal(Null)) { valueKindEqualsNullReturn }, MethodBodyStatement.EmptyLine, GetXmlNamespaceDeclarations(categorizedProperties.Namespaces), - GetPropertyVariableDeclarations(), + GetPropertyVariableDeclarations(preserveJsonPresence: false), MethodBodyStatement.EmptyLine }; @@ -651,7 +651,7 @@ private MethodBodyStatement[] BuildXmlDeserializationMethodBody() statements.Add(MethodBodyStatement.EmptyLine); } - statements.Add(Return(New.Instance(_model.Type, GetSerializationCtorParameterValues()))); + statements.Add(Return(New.Instance(_model.Type, GetSerializationCtorParameterValues(preserveJsonPresence: false)))); return [.. statements]; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs index 5a19c692e73..b8715ba0158 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs @@ -980,11 +980,26 @@ private MethodBodyStatement[] BuildDeserializationMethodBody() new IfStatement(_jsonElementParameterSnippet.ValueKindEqualsNull()) { valueKindEqualsNullReturn }, GetPropertyVariableDeclarations(), deserializePropertiesForEachStatement, - Return(New.Instance(_model.Type, GetSerializationCtorParameterValues())) + Return(New.Instance(SerializationConstructor.Signature, GetSerializationCtorParameterValues(), GetNullablePresenceInitializer())) ]; } - private MethodBodyStatement GetPropertyVariableDeclarations() + private IReadOnlyDictionary? GetNullablePresenceInitializer() + { + Dictionary? values = null; + foreach (var parameter in SerializationConstructor.Signature.Parameters) + { + if (parameter.Property is { } property && + ScmModelProvider.GetNullablePropertyPresence(property) is { } presence) + { + values ??= []; + values[presence] = presence.AsVariableExpression; + } + } + return values; + } + + private MethodBodyStatement GetPropertyVariableDeclarations(bool preserveJsonPresence = true) { var parameters = SerializationConstructor.Signature.Parameters; var propertyDeclarationStatements = new List(parameters.Count); @@ -1022,6 +1037,10 @@ private MethodBodyStatement GetPropertyVariableDeclarations() } else { + if (preserveJsonPresence && ScmModelProvider.GetNullablePropertyPresence(property) is { } presence) + { + propertyDeclarationStatements.Add(Declare(presence.AsVariableExpression, False)); + } ValueExpression defaultValue; if (property.IsDiscriminator && _model.DiscriminatorValue != null && property.Type.IsFrameworkType) { @@ -1031,6 +1050,10 @@ private MethodBodyStatement GetPropertyVariableDeclarations() { defaultValue = New.List(property.Type.ElementType); } + else if (preserveJsonPresence && IsOptionalNullableCollection(property)) + { + defaultValue = New.Instance(property.Type.PropertyInitializationType); + } else { defaultValue = Default; @@ -1149,7 +1172,7 @@ private MethodBodyStatement CallBaseJsonModelWriteCore(bool isDynamicModelWithNo /// /// Builds the values for the serialization constructor parameters. /// - private ValueExpression[] GetSerializationCtorParameterValues() + private ValueExpression[] GetSerializationCtorParameterValues(bool preserveJsonPresence = true) { var parameters = SerializationConstructor.Signature.Parameters; ValueExpression[] serializationCtorParameters = new ValueExpression[parameters.Count]; @@ -1160,7 +1183,7 @@ private ValueExpression[] GetSerializationCtorParameterValues() var parameter = parameters[i]; if (parameter.Property is { } property) { - serializationCtorParameters[i] = GetValueForSerializationConstructor(property); + serializationCtorParameters[i] = GetValueForSerializationConstructor(property, preserveJsonPresence); continue; } else @@ -1174,10 +1197,15 @@ private ValueExpression[] GetSerializationCtorParameterValues() return serializationCtorParameters; } - private static ValueExpression GetValueForSerializationConstructor(PropertyProvider propertyProvider) + private static ValueExpression GetValueForSerializationConstructor(PropertyProvider propertyProvider, bool preserveJsonPresence) { var isRequired = propertyProvider.WireInfo?.IsRequired ?? false; + if (preserveJsonPresence && IsOptionalNullableCollection(propertyProvider)) + { + return propertyProvider.AsVariableExpression; + } + if (!propertyProvider.Type.IsFrameworkType || propertyProvider.IsAdditionalProperties) { return propertyProvider.Type.IsReadOnlyDictionary @@ -1192,6 +1220,10 @@ private static ValueExpression GetValueForSerializationConstructor(PropertyProvi return propertyProvider.AsVariableExpression; } + private static bool IsOptionalNullableCollection(PropertyProvider property) + => property.WireInfo is { IsRequired: false, IsNullable: true } && + property.Type is { IsCollection: true, IsReadOnlyMemory: false }; + private List BuildDeserializePropertiesStatements(ScopedApi jsonProperty) { List propertyDeserializationStatements = []; @@ -1226,10 +1258,14 @@ private List BuildDeserializePropertiesStatements(ScopedApi var propertyName = parameter.Property?.Name ?? parameter.Field?.Name; var propertyType = parameter.Property?.Type ?? parameter.Field?.Type; var propertyExpression = parameter.Property?.AsVariableExpression ?? parameter.Field?.AsVariableExpression; - var checkIfJsonPropEqualsName = new IfStatement(jsonProperty.NameEquals(propertySerializationName)) + var checkIfJsonPropEqualsName = new IfStatement(jsonProperty.NameEquals(propertySerializationName)); + if (parameter.Property is { } property && + ScmModelProvider.GetNullablePropertyPresence(property) is { } presence) { - DeserializeProperty(propertyName!, propertyType!, wireInfo, propertyExpression!, jsonProperty, serializationAttributes, wireInfo.SerializationFormat) - }; + checkIfJsonPropEqualsName.Add(presence.AsVariableExpression.Assign(True).Terminate()); + } + checkIfJsonPropEqualsName.Add( + DeserializeProperty(propertyName!, propertyType!, wireInfo, propertyExpression!, jsonProperty, serializationAttributes, wireInfo.SerializationFormat)); propertyDeserializationStatements.Add(checkIfJsonPropEqualsName); } else @@ -1633,7 +1669,7 @@ private static MethodBodyStatement DeserializationPropertyNullCheckStatement( if ((serializedType.IsNullable || !serializedType.IsValueType) && wireInfo.IsNullable) { - if (!serializedType.IsCollection) + if (!serializedType.IsCollection || !propertyIsRequired) { return new IfStatement(checkEmptyProperty) { @@ -1879,7 +1915,8 @@ private MethodBodyStatement[] CreateWritePropertiesStatements(bool isDynamicMode continue; } - propertyStatements.Add(CreateWritePropertyStatement(property.WireInfo, property.Type, property.Name, property, property.WireInfo?.SerializationFormat)); + propertyStatements.Add(CreateWritePropertyStatement(property.WireInfo, property.Type, property.Name, property, property.WireInfo?.SerializationFormat, + ScmModelProvider.GetNullablePropertyPresence(property))); } foreach (var field in baseModelProvider.CanonicalView.Fields) @@ -1903,7 +1940,8 @@ private MethodBodyStatement[] CreateWritePropertiesStatements(bool isDynamicMode continue; } - propertyStatements.Add(CreateWritePropertyStatement(property.WireInfo, property.Type, property.Name, property, property.WireInfo.SerializationFormat)); + propertyStatements.Add(CreateWritePropertyStatement(property.WireInfo, property.Type, property.Name, property, property.WireInfo.SerializationFormat, + ScmModelProvider.GetNullablePropertyPresence(property))); } foreach (var field in _model.CanonicalView.Fields) @@ -1924,7 +1962,8 @@ private MethodBodyStatement CreateWritePropertyStatement( CSharpType propertyType, string propertyName, MemberExpression propertyExpression, - SerializationFormat? serializationFormat) + SerializationFormat? serializationFormat, + FieldProvider? presence = null) { var propertySerializationName = GetJsonSerializedName(wireInfo); var propertySerializationFormat = wireInfo.SerializationFormat; @@ -1985,7 +2024,8 @@ private MethodBodyStatement CreateWritePropertyStatement( propertyIsRequired, propertyIsReadOnly, propertyIsNullable, - writePropertySerializationStatements); + writePropertySerializationStatements, + presence); return wrapInIsDefinedStatement; } @@ -1997,7 +2037,8 @@ private MethodBodyStatement WrapInIsDefined( bool propertyIsRequired, bool propertyIsReadOnly, bool propertyIsNullable, - MethodBodyStatement writePropertySerializationStatement) + MethodBodyStatement writePropertySerializationStatement, + FieldProvider? presence) { #pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. ScopedApi? patchCheck = _jsonPatchProperty != null @@ -2029,7 +2070,8 @@ private MethodBodyStatement WrapInIsDefined( propertyIsRequired, jsonSerializedName, patchCheck, - writePropertySerializationStatement); + writePropertySerializationStatement, + presence); } /// @@ -2598,7 +2640,8 @@ private MethodBodyStatement CreateConditionalSerializationStatement( bool isRequired, string serializedName, ValueExpression? patchCheck, - MethodBodyStatement writePropertySerializationStatement) + MethodBodyStatement writePropertySerializationStatement, + FieldProvider? presence) { ScopedApi condition; bool shouldCheckJsonPath = patchCheck != null && (propertyType.IsList || propertyType.IsArray); @@ -2629,6 +2672,31 @@ private MethodBodyStatement CreateConditionalSerializationStatement( ? OptionalSnippets.IsCollectionDefined(propertyMemberExpression) : OptionalSnippets.IsDefined(propertyMemberExpression); + if (!isRequired && isNullable) + { + if (presence != null || propertyType is { IsCollection: true, IsReadOnlyMemory: false }) + { + if (presence != null) + { + isDefinedCondition = presence.As().Or(isDefinedCondition); + } + var writeNullableProperty = new IfElseStatement( + new IfStatement(propertyMemberExpression.NotEqual(Null)) { writePropertySerializationStatement }, + _utf8JsonWriterSnippet.WriteNull(serializedName)); + condition = isReadOnly ? _isNotEqualToWireConditionSnippet.And(isDefinedCondition) : isDefinedCondition; + if (shouldCheckJsonPath) + { + return CreateConditionalPatchSerializationStatement( + serializedName, condition, writeNullableProperty, null); + } + if (patchCheck != null) + { + condition = condition.And(patchCheck); + } + return new IfStatement(condition) { writeNullableProperty }; + } + } + if (patchCheck != null && !shouldCheckJsonPath) { isDefinedCondition = isDefinedCondition.And(patchCheck); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmModelProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmModelProvider.cs index 660f0cdd3cf..f1124f60faf 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmModelProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmModelProvider.cs @@ -23,6 +23,7 @@ using Microsoft.TypeSpec.Generator.Providers; using Microsoft.TypeSpec.Generator.Snippets; using Microsoft.TypeSpec.Generator.Statements; +using Microsoft.TypeSpec.Generator.Utilities; using static Microsoft.TypeSpec.Generator.Snippets.Snippet; namespace Microsoft.TypeSpec.Generator.ClientModel.Providers @@ -30,6 +31,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers public class ScmModelProvider : ModelProvider { private readonly InputModelType _inputModel; + private readonly Dictionary _nullablePropertyPresence = []; private const string JsonPatchFieldName = "_patch"; #pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. private readonly CSharpType _jsonPatchFieldType = typeof(JsonPatch); @@ -75,13 +77,25 @@ public ScmModelProvider(InputModelType inputModel) : base(inputModel) protected override FieldProvider[] BuildFields() { + var fields = base.BuildFields().ToList(); + foreach (var property in Properties) + { + if (_nullablePropertyPresence.TryGetValue(property, out var presence) && presence.EnclosingType == this) + { + if (property.BackingField is { } backingField && !fields.Any(f => f.Name == backingField.Name)) + { + fields.Add(backingField); + } + fields.Add(presence); + } + } + if (JsonPatchField is null) { - return base.BuildFields(); + return [.. fields]; } - var fields = base.BuildFields(); - var updatedFields = new List(fields.Length + 1); + var updatedFields = new List(fields.Count + 1); foreach (var field in fields) { @@ -115,6 +129,7 @@ protected override PropertyProvider[] BuildProperties() properties = [JsonPatchProperty, .. base.BuildProperties()]; } + AddNullablePropertyPresence(properties); foreach (var prop in properties) { if (IsFileBinaryContentType(prop.Type)) @@ -126,10 +141,145 @@ protected override PropertyProvider[] BuildProperties() return properties; } + internal static FieldProvider? GetNullablePropertyPresence(PropertyProvider property) + { + return property.EnclosingType is ScmModelProvider model && + model._nullablePropertyPresence.TryGetValue(property, out var presence) ? presence : null; + } + + private IEnumerable GetPresenceBaseModels() + { + HashSet visited = [this]; + List ancestors = []; + for (var model = BaseModelProvider; model != null; model = model.BaseModelProvider) + { + if (!visited.Add(model)) + { + return []; + } + ancestors.Add(model); + } + return ancestors; + } + + private static string GetAvailableFieldName(string preferredName, HashSet reservedNames) + { + var name = preferredName; + for (var suffix = 0; !reservedNames.Add(name); suffix++) + { + name = $"{preferredName}{suffix}"; + } + return name; + } + + private void AddNullablePropertyPresence(PropertyProvider[] properties) + { + if ((_inputModel.Usage.HasFlag(InputModelTypeUsage.Xml) && !_inputModel.Usage.HasFlag(InputModelTypeUsage.Json)) || + DeclarationModifiers.HasFlag(TypeSignatureModifiers.ReadOnly)) + { + return; + } + + (Dictionary BackingFields, HashSet ReservedNames)? fields = null; + foreach (var property in properties) + { + if (property.WireInfo is not { IsRequired: false, IsNullable: true, IsHttpMetadata: false } || + property.Type is { IsCollection: true, IsReadOnlyMemory: false }) + { + continue; + } + + if (property.BaseProperty != null && property.Modifiers.HasFlag(MethodSignatureModifiers.Override)) + { + var baseProperty = GetPresenceBaseModels().SelectMany(m => m.CanonicalView.Properties) + .FirstOrDefault(p => p.Name == property.BaseProperty.Name); + if (baseProperty != null && GetNullablePropertyPresence(baseProperty) is { } basePresence) + { + _nullablePropertyPresence[property] = basePresence; + property.Update(body: new MethodPropertyBody( + Return(Base.Property(baseProperty.Name)), + property.Body.HasSetter ? Base.Property(baseProperty.Name).Assign(Value).Terminate() : null)); + } + continue; + } + + fields ??= BuildNullablePropertyFields(properties); + AddNullablePropertyPresence(property, fields.Value.BackingFields[property], fields.Value.ReservedNames); + } + } + + private (Dictionary BackingFields, HashSet ReservedNames) BuildNullablePropertyFields(PropertyProvider[] properties) + { + var baseFields = base.BuildFields(); + var reservedNames = new HashSet(properties.Select(p => p.Name)); + reservedNames.UnionWith(baseFields.Select(f => f.Name)); + reservedNames.UnionWith(CustomCodeView?.Fields.Select(f => f.Name) ?? []); + reservedNames.UnionWith(GetPresenceBaseModels().SelectMany(m => m.CanonicalView.Fields.Select(f => f.Name))); + if (JsonPatchField != null) + { + reservedNames.Add(JsonPatchField.Name); + } + var backingFields = new Dictionary(); + foreach (var inputProperty in _inputModel.Properties) + { + // ModelProvider may already own storage shared with a narrowed derived property. + var sharedField = baseFields.FirstOrDefault(f => + f.Modifiers == (FieldModifiers.Private | FieldModifiers.Protected) && + f.Name == $"_{inputProperty.Name.ToVariableName()}"); + if (sharedField != null && + ScmCodeModelGenerator.Instance.TypeFactory.CreateProperty(inputProperty, this) is { } property && + sharedField.Type.Equals(property.Type)) + { + backingFields[property] = sharedField; + } + } + foreach (var property in properties) + { + if (!backingFields.ContainsKey(property)) + { + backingFields[property] = new FieldProvider(FieldModifiers.Private, property.Type, + GetAvailableFieldName($"_{property.Name.ToVariableName()}", reservedNames), this); + } + } + return (backingFields, reservedNames); + } + + private void AddNullablePropertyPresence(PropertyProvider property, FieldProvider backingField, HashSet reservedFieldNames) + { + var presence = new FieldProvider( + FieldModifiers.Internal, typeof(bool), GetAvailableFieldName($"_{property.Name.ToVariableName()}IsDefined", reservedFieldNames), this); + _nullablePropertyPresence[property] = presence; + property.BackingField = backingField; + MethodBodyStatement? setter = null; + if (property.Body.HasSetter) + { + setter = new MethodBodyStatement[] + { + backingField.Assign(Value).Terminate(), + presence.Assign(True).Terminate() + }; + } + property.Update(body: new MethodPropertyBody(Return(backingField), setter)); + } + protected override ConstructorProvider[] BuildConstructors() { List constructors = [.. base.BuildConstructors()]; + foreach (var property in CanonicalView.Properties) + { + if (property.Modifiers.HasFlag(MethodSignatureModifiers.New) && + GetNullablePropertyPresence(property)?.EnclosingType == this && + property.BackingField is { } backingField) + { + // ModelProvider initializes redeclared properties through the base constructor, + // but these properties have their own storage. + MethodBodyStatement[] initializers = + [.. FullConstructor.BodyStatements ?? MethodBodyStatement.Empty, backingField.Assign(property.AsParameter).Terminate()]; + FullConstructor.Update(bodyStatements: initializers); + } + } + if (ShouldUpdateFullConstructor()) { // Update the full constructor to include the json patch parameter diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs index 2bf1c5a2564..7079c8f5143 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs @@ -64,6 +64,31 @@ public void TestBuildImplements() Assert.That(interfaces.Any(i => i.Equals(expectedJsonModelTInterface))); } + [TestCase(false, false, false)] + [TestCase(true, false, false)] + [TestCase(true, true, true)] + [TestCase(true, true, false)] + public void DeserializationOnlyInitializesTrackedPresence(bool hasProperty, bool isNullable, bool isRequired) + { + var inputModel = InputFactory.Model("model", properties: hasProperty + ? [InputFactory.Property("text", isNullable ? new InputNullableType(InputPrimitiveType.String) : InputPrimitiveType.String, isRequired: isRequired)] + : []); + var (model, serialization) = CreateModelAndSerialization(inputModel); + var returnStatement = (ExpressionStatement)serialization.BuildDeserializationMethod().BodyStatements!.Last(); + var newInstance = (NewInstanceExpression)((KeywordExpression)returnStatement.Expression).Expression!; + + if (hasProperty && isNullable && !isRequired) + { + var presence = ClientModel.Providers.ScmModelProvider.GetNullablePropertyPresence(model.Properties.Single()); + Assert.That(newInstance.InitExpression, Is.Not.Null); + Assert.That(newInstance.InitExpression!.Values.Keys, Is.EqualTo(new[] { presence!.AsValueExpression })); + } + else + { + Assert.That(newInstance.InitExpression, Is.Null); + } + } + // This test validates the json model serialization write method is built correctly [TestCase(true)] [TestCase(false)] diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/SerializedNameIsUsed(False).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/SerializedNameIsUsed(False).cs index 24c0cecfb69..84585fa9af3 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/SerializedNameIsUsed(False).cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/SerializedNameIsUsed(False).cs @@ -3,10 +3,17 @@ { throw new global::System.FormatException($"The model {nameof(global::Sample.Models.MockInputModel)} does not support writing '{format}' format."); } -if (global::Sample.Optional.IsDefined(MockProperty)) +if ((_mockPropertyIsDefined || global::Sample.Optional.IsDefined(MockProperty))) { - writer.WritePropertyName("mock_wire_name"u8); - writer.WriteNumberValue(MockProperty.Value); + if ((MockProperty != null)) + { + writer.WritePropertyName("mock_wire_name"u8); + writer.WriteNumberValue(MockProperty.Value); + } + else + { + writer.WriteNull("mock_wire_name"u8); + } } if (((options.Format != "W") && (_additionalBinaryDataProperties != null))) { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanCustomizeDeserializationMethodWithOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanCustomizeDeserializationMethodWithOptions.cs index 6e7de3279e2..8a11d277ef8 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanCustomizeDeserializationMethodWithOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanCustomizeDeserializationMethodWithOptions.cs @@ -19,6 +19,7 @@ public partial class MockInputModel return null; } string prop1 = default; + bool prop2IsDefined = false; string prop2 = default; global::System.Collections.Generic.IDictionary additionalBinaryDataProperties = new global::Sample.ChangeTrackingDictionary(); foreach (var prop in element.EnumerateObject()) @@ -30,6 +31,7 @@ public partial class MockInputModel } if (prop.NameEquals("prop2"u8)) { + prop2IsDefined = true; DeserializationMethod(prop, ref prop2, options); continue; } @@ -38,7 +40,10 @@ public partial class MockInputModel additionalBinaryDataProperties.Add(prop.Name, prop.Value.GetUtf8Bytes()); } } - return new global::Sample.Models.MockInputModel(prop1, prop2, additionalBinaryDataProperties); + return new global::Sample.Models.MockInputModel(prop1, prop2, additionalBinaryDataProperties) + { + _prop2IsDefined = prop2IsDefined + }; } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanCustomizeDeserializationMethodWithoutOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanCustomizeDeserializationMethodWithoutOptions.cs index b957189b6f0..ddc7c865927 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanCustomizeDeserializationMethodWithoutOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanCustomizeDeserializationMethodWithoutOptions.cs @@ -19,6 +19,7 @@ public partial class MockInputModel return null; } string prop1 = default; + bool prop2IsDefined = false; string prop2 = default; global::System.Collections.Generic.IDictionary additionalBinaryDataProperties = new global::Sample.ChangeTrackingDictionary(); foreach (var prop in element.EnumerateObject()) @@ -30,6 +31,7 @@ public partial class MockInputModel } if (prop.NameEquals("prop2"u8)) { + prop2IsDefined = true; DeserializationMethod(prop, ref prop2); continue; } @@ -38,7 +40,10 @@ public partial class MockInputModel additionalBinaryDataProperties.Add(prop.Name, prop.Value.GetUtf8Bytes()); } } - return new global::Sample.Models.MockInputModel(prop1, prop2, additionalBinaryDataProperties); + return new global::Sample.Models.MockInputModel(prop1, prop2, additionalBinaryDataProperties) + { + _prop2IsDefined = prop2IsDefined + }; } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanCustomizeSerializationMethod.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanCustomizeSerializationMethod.cs index b6c293a2924..c8916a5f9c6 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanCustomizeSerializationMethod.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanCustomizeSerializationMethod.cs @@ -64,10 +64,17 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite writer.WritePropertyName("prop1"u8); this.SerializationMethod(writer, options); } - if (global::Sample.Optional.IsDefined(Prop2)) + if ((_prop2IsDefined || global::Sample.Optional.IsDefined(Prop2))) { - writer.WritePropertyName("prop2"u8); - this.SerializationMethod(writer, options); + if ((Prop2 != null)) + { + writer.WritePropertyName("prop2"u8); + this.SerializationMethod(writer, options); + } + else + { + writer.WriteNull("prop2"u8); + } } if (((options.Format != "W") && (_additionalBinaryDataProperties != null))) { @@ -106,6 +113,7 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite return null; } string prop1 = default; + bool prop2IsDefined = false; string prop2 = default; global::System.Collections.Generic.IDictionary additionalBinaryDataProperties = new global::Sample.ChangeTrackingDictionary(); foreach (var prop in element.EnumerateObject()) @@ -117,6 +125,7 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite } if (prop.NameEquals("prop2"u8)) { + prop2IsDefined = true; DeserializationMethod(prop, ref prop2); continue; } @@ -125,7 +134,10 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite additionalBinaryDataProperties.Add(prop.Name, prop.Value.GetUtf8Bytes()); } } - return new global::Sample.Models.MockInputModel(prop1, prop2, additionalBinaryDataProperties); + return new global::Sample.Models.MockInputModel(prop1, prop2, additionalBinaryDataProperties) + { + _prop2IsDefined = prop2IsDefined + }; } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanReplaceDeserializationMethod.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanReplaceDeserializationMethod.cs index 7143c9c8325..6f9e1b42a96 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanReplaceDeserializationMethod.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanReplaceDeserializationMethod.cs @@ -63,10 +63,17 @@ protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWrite writer.WritePropertyName("prop1"u8); writer.WriteStringValue(Prop1); } - if (global::Sample.Optional.IsDefined(Prop2)) + if ((_prop2IsDefined || global::Sample.Optional.IsDefined(Prop2))) { - writer.WritePropertyName("prop2"u8); - writer.WriteStringValue(Prop2); + if ((Prop2 != null)) + { + writer.WritePropertyName("prop2"u8); + writer.WriteStringValue(Prop2); + } + else + { + writer.WriteNull("prop2"u8); + } } if (((options.Format != "W") && (_additionalBinaryDataProperties != null))) { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanReplaceSerializationMethod.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanReplaceSerializationMethod.cs index 20c13b2acf6..19f5e6ab177 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanReplaceSerializationMethod.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/CanReplaceSerializationMethod.cs @@ -72,6 +72,7 @@ public partial class MockInputModel : global::System.ClientModel.Primitives.IJso return null; } string prop1 = default; + bool prop2IsDefined = false; string prop2 = default; global::System.Collections.Generic.IDictionary additionalBinaryDataProperties = new global::Sample.ChangeTrackingDictionary(); foreach (var prop in element.EnumerateObject()) @@ -83,6 +84,7 @@ public partial class MockInputModel : global::System.ClientModel.Primitives.IJso } if (prop.NameEquals("prop2"u8)) { + prop2IsDefined = true; if ((prop.Value.ValueKind == global::System.Text.Json.JsonValueKind.Null)) { prop2 = null; @@ -96,7 +98,10 @@ public partial class MockInputModel : global::System.ClientModel.Primitives.IJso additionalBinaryDataProperties.Add(prop.Name, prop.Value.GetUtf8Bytes()); } } - return new global::Sample.Models.MockInputModel(prop1, prop2, additionalBinaryDataProperties); + return new global::Sample.Models.MockInputModel(prop1, prop2, additionalBinaryDataProperties) + { + _prop2IsDefined = prop2IsDefined + }; } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/ScmModelProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/ScmModelProviderTests.cs index d5b58f5a7ca..74128272960 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/ScmModelProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/ScmModelProviderTests.cs @@ -10,6 +10,7 @@ using Microsoft.TypeSpec.Generator.Input; using Microsoft.TypeSpec.Generator.Primitives; using Microsoft.TypeSpec.Generator.Snippets; +using Microsoft.TypeSpec.Generator.Statements; using Microsoft.TypeSpec.Generator.Tests.Common; using NUnit.Framework; using ScmModel = Microsoft.TypeSpec.Generator.ClientModel.Providers.ScmModelProvider; @@ -39,6 +40,268 @@ public void CanBeInherited() Assert.IsInstanceOf(provider); } + [TestCase(false)] + [TestCase(true)] + public void OptionalNullablePropertiesTrackPresenceWithoutChangingConstructorSignatures(bool isDynamic) + { + var inputModel = InputFactory.Model("model", isDynamicModel: isDynamic, properties: + [ + InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String)), + InputFactory.Property("number", new InputNullableType(InputPrimitiveType.Int32)), + InputFactory.Property("child", new InputNullableType(InputFactory.Model("child"))), + InputFactory.Property("optionalText", InputPrimitiveType.String), + InputFactory.Property("requiredText", new InputNullableType(InputPrimitiveType.String), isRequired: true) + ]); + var model = new ScmModel(inputModel); + + foreach (var name in new[] { "Text", "Number", "Child" }) + { + var property = model.Properties.Single(p => p.Name == name); + Assert.That(property.Body, Is.InstanceOf()); + Assert.That(property.BackingField, Is.Not.Null); + Assert.That(property.BackingField!.Type, Is.EqualTo(property.Type)); + Assert.That(property.BackingField.WireInfo, Is.Null); + Assert.That(property.Body.HasSetter, Is.True); + } + Assert.That(model.Properties.Single(p => p.Name == "OptionalText").Body, Is.InstanceOf()); + Assert.That(model.Properties.Single(p => p.Name == "RequiredText").Body, Is.InstanceOf()); + Assert.That(model.FullConstructor.Signature.Parameters.Count, Is.EqualTo(6)); + Assert.That(model.FullConstructor.Signature.Parameters.Take(5).Select(p => p.Type), + Is.EqualTo(model.Properties.Where(p => p.WireInfo != null).Select(p => p.Type))); + Assert.That(model.Constructors.Single(c => c.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public)) + .Signature.Parameters.Select(p => p.Name), Is.EqualTo(new[] { "requiredText" })); + } + + [Test] + public void OptionalNullableOutputPropertyTracksPresenceWithoutAddingSetter() + { + var inputModel = InputFactory.Model("model", usage: InputModelTypeUsage.Output | InputModelTypeUsage.Json, properties: + [ + InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String), isReadOnly: true) + ]); + var model = new ScmModel(inputModel); + var property = model.Properties.Single(); + + Assert.That(property.BackingField, Is.Not.Null); + Assert.That(property.Body.HasSetter, Is.False); + Assert.That(model.FullConstructor.Signature.Parameters.Count, Is.EqualTo(2)); + } + + [Test] + public void OptionalNullablePresenceFieldsDoNotCollideWithOtherBackingFields() + { + var model = new ScmModel(InputFactory.Model("model", properties: + [ + InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String)), + InputFactory.Property("textIsDefined", new InputNullableType(InputPrimitiveType.String)) + ])); + + Assert.That(model.Fields.Select(f => f.Name), Is.Unique); + Assert.That(model.Properties.Select(p => p.BackingField!.Type), Is.All.EqualTo(model.Properties[0].Type)); + } + + [TestCase(false)] + [TestCase(true)] + public void OptionalNullableBackingFieldsDoNotCollideWithAdditionalProperties(bool isDynamic) + { + var model = new ScmModel(InputFactory.Model("model", isDynamicModel: isDynamic, + additionalProperties: InputPrimitiveType.String, properties: + [ + InputFactory.Property("additionalStringProperties", new InputNullableType(InputPrimitiveType.String)), + InputFactory.Property("additionalStringPropertiesIsDefined", new InputNullableType(InputPrimitiveType.String)) + ])); + + var property = model.Properties.Single(p => p.Name == "AdditionalStringProperties"); + Assert.That(property.BackingField!.Name, Is.Not.EqualTo("_additionalStringProperties")); + Assert.That(model.Fields.Select(f => f.Name), Is.Unique); + Assert.That(model.Fields, Does.Contain(property.BackingField)); + Assert.That(model.Properties.Single(p => p.IsAdditionalProperties).BackingField!.Type.IsDictionary, Is.True); + } + + [Test] + public async Task OptionalNullableFieldsDoNotHideInheritedCustomFields() + { + var baseModel = InputFactory.Model("baseModel"); + var middleModel = InputFactory.Model("middleModel", baseModel: baseModel); + var derivedModel = InputFactory.Model("derivedModel", baseModel: middleModel, properties: + [InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String))]); + await MockHelpers.LoadMockGeneratorAsync( + inputModels: () => [baseModel, middleModel, derivedModel], + compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + var model = (ScmModel)ScmCodeModelGenerator.Instance.TypeFactory.CreateModel(derivedModel)!; + var customFields = model.BaseModelProvider!.BaseModelProvider!.CustomCodeView!.Fields; + + Assert.That(customFields.Select(f => f.Name), Is.EquivalentTo(new[] { "_text", "_textIsDefined" })); + Assert.That(model.Fields.Select(f => f.Name).Intersect(customFields.Select(f => f.Name)), Is.Empty); + Assert.That(ScmModel.GetNullablePropertyPresence(model.Properties.Single()), Is.Not.Null); + } + + [TestCase("URL", "_url", "_urlIsDefined")] + [TestCase("IPAddress", "_ipAddress", "_ipAddressIsDefined")] + [TestCase("class", "_class", "_classIsDefined")] + public void OptionalNullableFieldsUseVariableNames(string name, string backingName, string presenceName) + { + var model = new ScmModel(InputFactory.Model("model", properties: + [InputFactory.Property(name, new InputNullableType(InputPrimitiveType.String))])); + var property = model.Properties.Single(); + + Assert.That(property.BackingField!.Name, Is.EqualTo(backingName)); + Assert.That(ScmModel.GetNullablePropertyPresence(property)!.Name, Is.EqualTo(presenceName)); + } + + [Test] + public void OptionalNullableReadonlyStructDoesNotAddMutableFields() + { + var model = new ScmModel(InputFactory.Model("model", modelAsStruct: true, properties: + [InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String))])); + var property = model.Properties.Single(); + + Assert.That(model.DeclarationModifiers.HasFlag(TypeSignatureModifiers.ReadOnly), Is.True); + Assert.That(ScmModel.GetNullablePropertyPresence(property), Is.Null); + Assert.That(property.BackingField, Is.Null); + Assert.That(property.Body.HasSetter, Is.False); + Assert.That(model.Fields.All(f => f.Modifiers.HasFlag(FieldModifiers.ReadOnly)), Is.True); + } + + [Test] + public void OptionalNullableBackingFieldDoesNotHideInheritedPresence() + { + var baseModel = InputFactory.Model("baseModel", properties: + [InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String))]); + var middleModel = InputFactory.Model("middleModel", baseModel: baseModel); + var derivedModel = InputFactory.Model("derivedModel", baseModel: middleModel, properties: + [InputFactory.Property("textIsDefined", new InputNullableType(InputPrimitiveType.String))]); + MockHelpers.LoadMockGenerator(inputModels: () => [baseModel, middleModel, derivedModel]); + var provider = (ScmModel)ScmCodeModelGenerator.Instance.TypeFactory.CreateModel(derivedModel)!; + var baseProperty = provider.BaseModelProvider!.BaseModelProvider!.Properties.Single(); + var property = provider.Properties.Single(); + + Assert.That(property.BackingField!.Name, Is.Not.EqualTo(ScmModel.GetNullablePropertyPresence(baseProperty)!.Name)); + Assert.That(provider.Fields.Select(f => f.Name), Is.Unique); + } + + [TestCase(false)] + [TestCase(true)] + public void OptionalNullableOverrideSharesBasePresence(bool narrowed) + { + var baseModel = InputFactory.Model("baseModel", properties: + [InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String))]); + var derivedModel = InputFactory.Model("derivedModel", baseModel: baseModel, properties: + [InputFactory.Property("text", new InputNullableType(narrowed ? InputFactory.Literal.String("value") : InputPrimitiveType.String))]); + MockHelpers.LoadMockGenerator(inputModels: () => [baseModel, derivedModel]); + var provider = (ScmModel)ScmCodeModelGenerator.Instance.TypeFactory.CreateModel(derivedModel)!; + var baseProperty = provider.BaseModelProvider!.Properties.Single(); + var property = provider.Properties.Single(); + + Assert.That(ScmModel.GetNullablePropertyPresence(property), + Is.SameAs(ScmModel.GetNullablePropertyPresence(baseProperty))); + Assert.That(ScmModel.GetNullablePropertyPresence(property), Is.Not.Null); + Assert.That(provider.Fields, Is.Empty); + Assert.That(property.Body, Is.InstanceOf()); + } + + [Test] + public void OptionalNullableOverrideThroughIntermediateModelSharesBasePresence() + { + var baseModel = InputFactory.Model("baseModel", properties: + [InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String))]); + var middleModel = InputFactory.Model("middleModel", baseModel: baseModel); + var derivedModel = InputFactory.Model("derivedModel", baseModel: middleModel, properties: + [InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String))]); + MockHelpers.LoadMockGenerator(inputModels: () => [baseModel, middleModel, derivedModel]); + var model = (ScmModel)ScmCodeModelGenerator.Instance.TypeFactory.CreateModel(derivedModel)!; + var baseProperty = model.BaseModelProvider!.BaseModelProvider!.CanonicalView.Properties.Single(); + var property = model.Properties.Single(); + + Assert.That(property.Modifiers.HasFlag(MethodSignatureModifiers.Override), Is.True); + Assert.That(ScmModel.GetNullablePropertyPresence(property), Is.Not.Null); + Assert.That(ScmModel.GetNullablePropertyPresence(property), Is.SameAs(ScmModel.GetNullablePropertyPresence(baseProperty))); + Assert.That(model.Fields, Is.Empty); + } + + [Test] + public void OptionalNullableBasePreservesStorageUsedByRequiredDerivedProperty() + { + var baseModel = InputFactory.Model("baseModel", properties: + [InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String))]); + var derivedModel = InputFactory.Model("derivedModel", baseModel: baseModel, properties: + [InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String), isRequired: true)]); + MockHelpers.LoadMockGenerator(inputModels: () => [baseModel, derivedModel]); + var model = (ScmModel)ScmCodeModelGenerator.Instance.TypeFactory.CreateModel(derivedModel)!; + var baseProperty = model.BaseModelProvider!.Properties.Single(); + var property = model.Properties.Single(); + + Assert.That(property.Modifiers.HasFlag(MethodSignatureModifiers.New), Is.True); + Assert.That(property.BackingField, Is.Not.Null); + Assert.That(baseProperty.BackingField!.Name, Is.EqualTo(property.BackingField!.Name)); + Assert.That(baseProperty.BackingField.Modifiers, Is.EqualTo(FieldModifiers.Private | FieldModifiers.Protected)); + Assert.That(model.BaseModelProvider.Fields.Select(f => f.Name), Is.Unique); + } + + [TestCase(false)] + [TestCase(true)] + public void OptionalNullableNewPropertyHasOwnPresenceAndStorage(bool baseIsNullable) + { + var baseModel = InputFactory.Model("baseModel", properties: + [InputFactory.Property("text", baseIsNullable ? new InputNullableType(InputPrimitiveType.String) : InputPrimitiveType.String, isRequired: true)]); + var derivedModel = InputFactory.Model("derivedModel", baseModel: baseModel, properties: + [InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String))]); + MockHelpers.LoadMockGenerator(inputModels: () => [baseModel, derivedModel]); + var model = (ScmModel)ScmCodeModelGenerator.Instance.TypeFactory.CreateModel(derivedModel)!; + var property = model.Properties.Single(); + var presence = ScmModel.GetNullablePropertyPresence(property); + + Assert.That(property.Modifiers.HasFlag(MethodSignatureModifiers.New), Is.True); + Assert.That(presence, Is.Not.Null); + Assert.That(presence!.EnclosingType, Is.SameAs(model)); + Assert.That(property.BackingField!.EnclosingType, Is.SameAs(model)); + Assert.That(model.Fields, Does.Contain(property.BackingField)); + Assert.That(model.Fields, Does.Contain(presence)); + var setter = (MethodPropertyBody)property.Body; + Assert.That(setter.Setter!.OfType() + .Select(s => s.Expression).OfType().Select(a => a.Variable), + Is.EquivalentTo(new ValueExpression[] { property.BackingField, presence })); + Assert.That(model.Constructors, Does.Contain(model.FullConstructor)); + Assert.That(model.FullConstructor.BodyStatements!.OfType() + .Select(s => s.Expression).OfType().Select(a => a.Variable), + Does.Contain(property.BackingField.AsValueExpression)); + } + + [Test] + public async Task OptionalNullableOverrideOfCustomBaseRetainsHandwrittenBehavior() + { + var baseModel = InputFactory.Model("baseModel", properties: + [InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String))]); + var derivedModel = InputFactory.Model("derivedModel", baseModel: baseModel, properties: + [InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String))]); + await MockHelpers.LoadMockGeneratorAsync( + inputModels: () => [baseModel, derivedModel], + compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + var model = (ScmModel)ScmCodeModelGenerator.Instance.TypeFactory.CreateModel(derivedModel)!; + + Assert.That(model.BaseModelProvider!.CanonicalView.Properties.Single().EnclosingType, + Is.SameAs(model.BaseModelProvider.CustomCodeView)); + Assert.That(model.Properties, Is.Empty); + Assert.That(model.Fields, Is.Empty); + } + + [Test] + public async Task OptionalNullableCustomPropertyRetainsHandwrittenBehavior() + { + var inputModel = InputFactory.Model("model", properties: + [InputFactory.Property("text", new InputNullableType(InputPrimitiveType.String))]); + await MockHelpers.LoadMockGeneratorAsync( + inputModels: () => [inputModel], + compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + var model = (ScmModel)ScmCodeModelGenerator.Instance.TypeFactory.CreateModel(inputModel)!; + var property = model.CanonicalView.Properties.Single(); + + Assert.That(property.Name, Is.EqualTo("RenamedText")); + Assert.That(ScmModel.GetNullablePropertyPresence(property), Is.Null); + Assert.That(model.Fields.Select(f => f.Name), Is.EqualTo(new[] { "_additionalBinaryDataProperties" })); + Assert.That(model.FullConstructor.Signature.Parameters.First().Name, Is.EqualTo("renamedText")); + } + [Test] public void TestSimpleDynamicModel() { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/OptionalNullableCustomPropertyRetainsHandwrittenBehavior/Model.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/OptionalNullableCustomPropertyRetainsHandwrittenBehavior/Model.cs new file mode 100644 index 00000000000..f5d65944eba --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/OptionalNullableCustomPropertyRetainsHandwrittenBehavior/Model.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.TypeSpec.Generator.Customizations; + +namespace Sample.Models +{ + public partial class Model + { + [CodeGenMember("Text")] + public string RenamedText { get; set; } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/OptionalNullableFieldsDoNotHideInheritedCustomFields/BaseModel.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/OptionalNullableFieldsDoNotHideInheritedCustomFields/BaseModel.cs new file mode 100644 index 00000000000..7bdc0699eb7 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/OptionalNullableFieldsDoNotHideInheritedCustomFields/BaseModel.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Sample.Models +{ + public partial class BaseModel + { + protected string _text; + protected bool _textIsDefined; + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/OptionalNullableOverrideOfCustomBaseRetainsHandwrittenBehavior/BaseModel.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/OptionalNullableOverrideOfCustomBaseRetainsHandwrittenBehavior/BaseModel.cs new file mode 100644 index 00000000000..4f3ab03525d --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/OptionalNullableOverrideOfCustomBaseRetainsHandwrittenBehavior/BaseModel.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Sample.Models +{ + public partial class BaseModel + { + public virtual string Text { get; set; } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local.Tests/ModelSerializationExtensionsTests.cs b/packages/http-client-csharp/generator/TestProjects/Local.Tests/ModelSerializationExtensionsTests.cs index 276d7571d6a..4de7ccd7ac1 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local.Tests/ModelSerializationExtensionsTests.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local.Tests/ModelSerializationExtensionsTests.cs @@ -3,6 +3,8 @@ using System; using System.Buffers; +using System.ClientModel.Primitives; +using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.Json; @@ -79,6 +81,495 @@ public void WriteBase64StringValueHandlesUrlUnsafeCharacters(byte[] payload, str AssertBase64Value(payload, format); } + private static readonly Dictionary SetValue, Func GetValue)> OptionalNullableScalarCases = new() + { + ["inheritedNullable"] = ("\"inherited\"", (model, isNull) => model.InheritedNullable = isNull ? null : "inherited", model => model.InheritedNullable), + ["nullableModel"] = ("""{"value":"child"}""", (model, isNull) => model.NullableModel = isNull ? null : new OptionalNullableChild("child"), model => model.NullableModel), + ["nullableString"] = ("\"text\"", (model, isNull) => model.NullableString = isNull ? null : "text", model => model.NullableString), + ["nullableInt"] = ("42", (model, isNull) => model.NullableInt = isNull ? null : 42, model => model.NullableInt), + ["nullableBoolean"] = ("false", (model, isNull) => model.NullableBoolean = isNull ? null : false, model => model.NullableBoolean), + ["nullableEnum"] = ("\"2\"", (model, isNull) => model.NullableEnum = isNull ? null : StringFixedEnum.Two, model => model.NullableEnum), + ["nullableDateTime"] = ("\"2026-01-02T03:04:05.0000000Z\"", (model, isNull) => model.NullableOn = isNull ? null : new DateTimeOffset(2026, 1, 2, 3, 4, 5, TimeSpan.Zero), model => model.NullableOn), + ["nullableBytes"] = ("\"AAH/\"", (model, isNull) => model.NullableBytes = isNull ? null : BinaryData.FromBytes(new byte[] { 0, 1, 255 }), model => model.NullableBytes), + }; + + private static IEnumerable OptionalNullableScalarNames => OptionalNullableScalarCases.Keys; + + [Test] + public void OptionalNullableFieldNames_PreserveAdditionalProperties([Values("W", "J")] string format) + { + var model = new OptionalNullableFieldNames(); + model.AdditionalProperties["extra"] = "additional"; + AssertModelJson(model, """{"extra":"additional"}""", format); + + model.AdditionalStringProperties = null; + model.AdditionalStringPropertiesIsDefined = "defined"; + AssertModelJson(model, """{"additionalStringProperties":null,"additionalStringPropertiesIsDefined":"defined","extra":"additional"}""", format); + + model.AdditionalStringProperties = "value"; + model.AdditionalStringPropertiesIsDefined = null; + AssertModelJson(model, """{"additionalStringProperties":"value","additionalStringPropertiesIsDefined":null,"extra":"additional"}""", format); + } + + [Test] + public void OptionalNullableFieldNames_RoundTrip( + [Values("{}", """{"additionalStringProperties":null}""", """{"additionalStringProperties":"value","additionalStringPropertiesIsDefined":null,"extra":"additional"}""")] string json, + [Values("W", "J")] string readFormat, + [Values("W", "J")] string writeFormat) + { + var model = ModelReaderWriter.Read(BinaryData.FromString(json), + new ModelReaderWriterOptions(readFormat), SampleTypeSpecContext.Default)!; + + AssertModelJson(model, json, writeFormat); + } + + [Test] + public void OptionalNullableProperties_PublicPropertyTypesAreUnchanged() + { + var propertyTypes = new Dictionary + { + [nameof(OptionalNullableProperties.InheritedNullable)] = typeof(string), + [nameof(OptionalNullableProperties.NullableModel)] = typeof(OptionalNullableChild), + [nameof(OptionalNullableProperties.NullableString)] = typeof(string), + [nameof(OptionalNullableProperties.NullableInt)] = typeof(int?), + [nameof(OptionalNullableProperties.NullableBoolean)] = typeof(bool?), + [nameof(OptionalNullableProperties.NullableEnum)] = typeof(StringFixedEnum?), + [nameof(OptionalNullableProperties.NullableOn)] = typeof(DateTimeOffset?), + [nameof(OptionalNullableProperties.NullableBytes)] = typeof(BinaryData), + [nameof(OptionalNullableProperties.NullableList)] = typeof(IList), + [nameof(OptionalNullableProperties.NullableDictionary)] = typeof(IDictionary), + [nameof(OptionalNullableProperties.ReadOnlyNullable)] = typeof(string), + [nameof(OptionalNullableProperties.RequiredNullable)] = typeof(string), + [nameof(OptionalNullableProperties.OptionalNonNullable)] = typeof(string), + [nameof(OptionalNullableProperties.OptionalNonNullableInt)] = typeof(int?) + }; + + foreach (var property in propertyTypes) + { + Assert.That(typeof(OptionalNullableProperties).GetProperty(property.Key)!.PropertyType, Is.EqualTo(property.Value), property.Key); + } + } + + [Test] + public void OptionalNullableProperties_UntouchedPropertiesAreOmitted([Values("W", "J")] string format) + { + var model = new OptionalNullableProperties(requiredNullable: null); + + foreach (var scalarCase in OptionalNullableScalarCases.Values) + { + Assert.That(scalarCase.GetValue(model), Is.Null); + } + Assert.That(model.NullableList, Is.Empty); + Assert.That(model.NullableDictionary, Is.Empty); + AssertModelJson(model, """{"requiredNullable":null}""", format); + } + + [Test] + public void OptionalNullableProperties_ExplicitNullSetterWritesNull( + [ValueSource(nameof(OptionalNullableScalarNames))] string propertyName, + [Values("W", "J")] string format, + [Values(false, true)] bool deserialize) + { + var model = deserialize + ? ReadOptionalNullableProperties("""{"requiredNullable":null}""", format) + : new OptionalNullableProperties(requiredNullable: null); + var scalarCase = OptionalNullableScalarCases[propertyName]; + + scalarCase.SetValue(model, true); + Assert.That(scalarCase.GetValue(model), Is.Null); + AssertModelJson(model, OptionalNullableJson(propertyName, "null"), format); + + scalarCase.SetValue(model, true); + AssertModelJson(model, OptionalNullableJson(propertyName, "null"), format); + } + + [Test] + public void OptionalNullableProperties_ValueNullValueTransitions( + [ValueSource(nameof(OptionalNullableScalarNames))] string propertyName, + [Values("W", "J")] string format, + [Values(false, true)] bool deserialize) + { + var model = deserialize + ? ReadOptionalNullableProperties(OptionalNullableJson(propertyName, "null"), format) + : new OptionalNullableProperties(requiredNullable: null); + var scalarCase = OptionalNullableScalarCases[propertyName]; + + scalarCase.SetValue(model, false); + Assert.That(scalarCase.GetValue(model), Is.Not.Null); + AssertModelJson(model, OptionalNullableJson(propertyName, scalarCase.Json), format); + + scalarCase.SetValue(model, true); + Assert.That(scalarCase.GetValue(model), Is.Null); + AssertModelJson(model, OptionalNullableJson(propertyName, "null"), format); + + scalarCase.SetValue(model, false); + Assert.That(scalarCase.GetValue(model), Is.Not.Null); + AssertModelJson(model, OptionalNullableJson(propertyName, scalarCase.Json), format); + } + + [Test] + public void OptionalNullableProperties_DeserializePreservesPresence( + [ValueSource(nameof(OptionalNullableScalarNames))] string propertyName, + [Values("absent", "null", "value")] string state, + [Values("W", "J")] string readFormat, + [Values("W", "J")] string writeFormat, + [Values(false, true)] bool useJsonModel) + { + var scalarCase = OptionalNullableScalarCases[propertyName]; + string json = state == "absent" + ? """{"requiredNullable":null}""" + : OptionalNullableJson(propertyName, state == "null" ? "null" : scalarCase.Json); + var model = ReadOptionalNullableProperties(json, readFormat, useJsonModel); + + Assert.That(scalarCase.GetValue(model), state == "value" ? Is.Not.Null : Is.Null); + AssertModelJson(model, json, writeFormat); + } + + [Test] + public void OptionalNullableProperties_DuplicatePropertyLastValueWins( + [ValueSource(nameof(OptionalNullableScalarNames))] string propertyName, + [Values(false, true)] bool nullLast, + [Values("W", "J")] string format) + { + var scalarCase = OptionalNullableScalarCases[propertyName]; + string first = nullLast ? scalarCase.Json : "null"; + string last = nullLast ? "null" : scalarCase.Json; + string json = $"{{\"requiredNullable\":null,\"{propertyName}\":{first},\"{propertyName}\":{last}}}"; + var model = ReadOptionalNullableProperties(json, format); + + Assert.That(scalarCase.GetValue(model), nullLast ? Is.Null : Is.Not.Null); + AssertModelJson(model, OptionalNullableJson(propertyName, last), format); + } + + [Test] + public void OptionalNullableProperties_RequiredAndNonNullableControls([Values("W", "J")] string format) + { + var model = new OptionalNullableProperties(requiredNullable: null) + { + OptionalNonNullable = null, + OptionalNonNullableInt = null, + NullableInt = 0, + NullableBoolean = false, + NullableString = string.Empty + }; + AssertModelJson(model, """{"requiredNullable":null,"nullableInt":0,"nullableBoolean":false,"nullableString":""}""", format); + + model.RequiredNullable = "required"; + model.OptionalNonNullable = "optional"; + model.OptionalNonNullableInt = 0; + AssertModelJson(model, """{"requiredNullable":"required","optionalNonNullable":"optional","optionalNonNullableInt":0,"nullableInt":0,"nullableBoolean":false,"nullableString":""}""", format); + + model.RequiredNullable = null; + model.OptionalNonNullable = null; + model.OptionalNonNullableInt = null; + AssertModelJson(model, """{"requiredNullable":null,"nullableInt":0,"nullableBoolean":false,"nullableString":""}""", format); + } + + [Test] + public void OptionalNullableProperties_FactoryDefaultsRemainOmitted([Values("W", "J")] string format) + { + var model = SampleTypeSpecModelFactory.OptionalNullableProperties(); + AssertModelJson(model, """{"requiredNullable":null,"nullableList":[]}""", format); + + // Factories retain their existing collection materialization and scalar default semantics. + model = SampleTypeSpecModelFactory.OptionalNullableProperties( + inheritedNullable: null, + nullableModel: null, + nullableString: null, + nullableInt: null, + nullableBoolean: null, + nullableEnum: null, + nullableOn: null, + nullableBytes: null, + nullableList: null, + nullableDictionary: null, + readOnlyNullable: null); + AssertModelJson(model, """{"requiredNullable":null,"nullableList":[]}""", format); + + model.NullableString = null; + AssertModelJson(model, """{"requiredNullable":null,"nullableList":[],"nullableString":null}""", format); + } + + [Test] + public void OptionalNullableProperties_FactoryValuesCanBeCleared([Values("W", "J")] string format) + { + var model = SampleTypeSpecModelFactory.OptionalNullableProperties( + inheritedNullable: "inherited", + nullableModel: new OptionalNullableChild("child"), + nullableString: "text", + nullableInt: 42, + nullableBoolean: false, + nullableEnum: StringFixedEnum.Two, + nullableOn: new DateTimeOffset(2026, 1, 2, 3, 4, 5, TimeSpan.Zero), + nullableBytes: BinaryData.FromBytes(new byte[] { 0, 1, 255 })); + string values = string.Join(",", OptionalNullableScalarCases.Select(item => $"\"{item.Key}\":{item.Value.Json}")); + AssertModelJson(model, $"{{\"requiredNullable\":null,\"nullableList\":[],{values}}}", format); + + foreach (var scalarCase in OptionalNullableScalarCases.Values) + { + scalarCase.SetValue(model, true); + } + string nulls = string.Join(",", OptionalNullableScalarNames.Select(name => $"\"{name}\":null")); + AssertModelJson(model, $"{{\"requiredNullable\":null,\"nullableList\":[],{nulls}}}", format); + } + + [Test] + public void OptionalNullableCollections_ReadingEmptyCollectionsDoesNotDefineThem( + [Values("W", "J")] string format, + [Values(false, true)] bool deserialize) + { + var model = deserialize + ? ReadOptionalNullableProperties("""{"requiredNullable":null}""", format) + : new OptionalNullableProperties(requiredNullable: null); + + Assert.That(model.NullableList.Count, Is.Zero); + Assert.That(model.NullableList.Any(), Is.False); + Assert.That(model.NullableDictionary.Count, Is.Zero); + Assert.That(model.NullableDictionary.Keys, Is.Empty); + Assert.That(model.NullableDictionary.Values, Is.Empty); + AssertModelJson(model, """{"requiredNullable":null}""", format); + } + + [Test] + public void OptionalNullableCollections_AddAndClearDefineCollections( + [Values("W", "J")] string format, + [Values(false, true)] bool clearWithoutAdding) + { + var model = new OptionalNullableProperties(requiredNullable: null); + + if (!clearWithoutAdding) + { + model.NullableList.Add(null); + model.NullableList.Add("item"); + model.NullableDictionary.Add("null", null); + model.NullableDictionary.Add("value", 42); + AssertModelJson(model, """{"requiredNullable":null,"nullableList":[null,"item"],"nullableDictionary":{"null":null,"value":42}}""", format); + } + + model.NullableList.Clear(); + model.NullableDictionary.Clear(); + AssertModelJson(model, """{"requiredNullable":null,"nullableList":[],"nullableDictionary":{}}""", format); + } + + [Test] + public void OptionalNullableCollections_ExplicitNullAndReplacementPreservePresence( + [Values("W", "J")] string format, + [Values(false, true)] bool deserialize) + { + var model = deserialize + ? ReadOptionalNullableProperties("""{"requiredNullable":null}""", format) + : new OptionalNullableProperties(requiredNullable: null); + model.NullableList = null; + model.NullableDictionary = null; + AssertModelJson(model, """{"requiredNullable":null,"nullableList":null,"nullableDictionary":null}""", format); + + model.NullableList = new List { null!, "item" }; + model.NullableDictionary = new Dictionary { ["null"] = null, ["value"] = 42 }; + AssertModelJson(model, """{"requiredNullable":null,"nullableList":[null,"item"],"nullableDictionary":{"null":null,"value":42}}""", format); + + model.NullableList = null; + model.NullableDictionary = null; + AssertModelJson(model, """{"requiredNullable":null,"nullableList":null,"nullableDictionary":null}""", format); + + model.NullableList = new List(); + model.NullableDictionary = new Dictionary(); + AssertModelJson(model, """{"requiredNullable":null,"nullableList":[],"nullableDictionary":{}}""", format); + } + + [Test] + public void OptionalNullableCollections_DeserializePreservesPresence( + [Values("absent", "null", "empty", "populated")] string state, + [Values("W", "J")] string readFormat, + [Values("W", "J")] string writeFormat, + [Values(false, true)] bool useJsonModel) + { + string json = state switch + { + "absent" => """{"requiredNullable":null}""", + "null" => """{"requiredNullable":null,"nullableList":null,"nullableDictionary":null}""", + "empty" => """{"requiredNullable":null,"nullableList":[],"nullableDictionary":{}}""", + _ => """{"requiredNullable":null,"nullableList":[null,"item"],"nullableDictionary":{"null":null,"value":42}}""" + }; + var model = ReadOptionalNullableProperties(json, readFormat, useJsonModel); + + if (state == "populated") + { + Assert.That(model.NullableList, Is.EqualTo(new string?[] { null, "item" })); + Assert.That(model.NullableDictionary["null"], Is.Null); + Assert.That(model.NullableDictionary["value"], Is.EqualTo(42)); + } + AssertModelJson(model, json, writeFormat); + } + + [Test] + public void OptionalNullableCollections_DuplicatePropertyLastValueWins( + [Values(false, true)] bool nullLast, + [Values("W", "J")] string format) + { + string firstList = nullLast ? "[null,\"item\"]" : "null"; + string lastList = nullLast ? "null" : "[null,\"item\"]"; + string firstDictionary = nullLast ? "{\"key\":null}" : "null"; + string lastDictionary = nullLast ? "null" : "{\"key\":null}"; + string json = $"{{\"requiredNullable\":null,\"nullableList\":{firstList},\"nullableList\":{lastList},\"nullableDictionary\":{firstDictionary},\"nullableDictionary\":{lastDictionary}}}"; + var model = ReadOptionalNullableProperties(json, format); + + AssertModelJson(model, $"{{\"requiredNullable\":null,\"nullableList\":{lastList},\"nullableDictionary\":{lastDictionary}}}", format); + } + + [Test] + public void OptionalNullableProperties_ReadOnlyNullPersistsOnlyInJson( + [Values("absent", "null", "value")] string state, + [Values("W", "J")] string format) + { + string json = state == "absent" + ? """{"requiredNullable":null}""" + : OptionalNullableJson("readOnlyNullable", state == "null" ? "null" : "\"output\""); + var model = ReadOptionalNullableProperties(json, "J"); + + Assert.That(model.ReadOnlyNullable, Is.EqualTo(state == "value" ? "output" : null)); + AssertModelJson(model, format == "J" ? json : """{"requiredNullable":null}""", format); + } + + [Test] + public void OptionalNullableProperties_NestedWireSerializationPreservesPresence() + { + var child = new OptionalNullableProperties(requiredNullable: null); + var model = new OptionalNullableContainer(child); + AssertModelJson(model, """{"child":{"requiredNullable":null}}""", "W"); + + child.InheritedNullable = null; + child.NullableModel = null; + child.NullableString = null; + child.NullableList = null; + child.NullableDictionary = null; + AssertModelJson(model, """{"child":{"requiredNullable":null,"inheritedNullable":null,"nullableModel":null,"nullableString":null,"nullableList":null,"nullableDictionary":null}}""", "W"); + + child = ReadOptionalNullableProperties("""{"requiredNullable":null,"inheritedNullable":null,"nullableModel":null,"readOnlyNullable":null}""", "J"); + model = new OptionalNullableContainer(child); + AssertModelJson(model, """{"child":{"requiredNullable":null,"inheritedNullable":null,"nullableModel":null}}""", "W"); + } + + [Test] + public void NullableDynamicModel_PropertySetterNullPreservesPresence([Values("W", "J")] string format) + { + var model = new NullableDynamicModel(); + AssertModelJson(model, "{}", format); + + model.ModelValue = null; + AssertModelJson(model, """{"modelValue":null}""", format); + + model.ModelValue = new AnotherDynamicModel("value"); + AssertModelJson(model, """{"modelValue":{"bar":"value"}}""", format); + + model.ModelValue = null; + AssertModelJson(model, """{"modelValue":null}""", format); + } + + [Test] + public void OptionalNullableDynamicProperties_PreserveInheritedPresence([Values("W", "J")] string format) + { + var model = new OptionalNullableDynamicProperties(); + AssertModelJson(model, "{}", format); + model.InheritedNullable = null; + AssertModelJson(model, """{"inheritedNullable":null}""", format); + model.InheritedNullable = "typed"; + AssertModelJson(model, """{"inheritedNullable":"typed"}""", format); + + model = ModelReaderWriter.Read( + BinaryData.FromString("""{"inheritedNullable":null}"""), + new ModelReaderWriterOptions(format), SampleTypeSpecContext.Default)!; + AssertModelJson(model, """{"inheritedNullable":null}""", format); + +#pragma warning disable SCME0001 + model.Patch.Set("$.inheritedNullable"u8, "patched"); + AssertModelJson(model, """{"inheritedNullable":"patched"}""", format); + model.Patch.Remove("$.inheritedNullable"u8); + AssertModelJson(model, "{}", format); +#pragma warning restore SCME0001 + } + + [Test] + public void NullableDynamicModel_PatchOverridesExplicitPropertyNull([Values("W", "J")] string format) + { + var model = new NullableDynamicModel { ModelValue = null }; + +#pragma warning disable SCME0001 + model.Patch.Set("$.modelValue"u8, """{"bar":"patched"}"""u8); + AssertModelJson(model, """{"modelValue":{"bar":"patched"}}""", format); + + model.Patch.SetNull("$.modelValue"u8); + AssertModelJson(model, """{"modelValue":null}""", format); + + model.ModelValue = new AnotherDynamicModel("typed"); + AssertModelJson(model, """{"modelValue":null}""", format); + + model.ModelValue = null; + model.Patch.Remove("$.modelValue"u8); + AssertModelJson(model, "{}", format); +#pragma warning restore SCME0001 + } + + private static string OptionalNullableJson(string propertyName, string value) => + $"{{\"requiredNullable\":null,\"{propertyName}\":{value}}}"; + + private static OptionalNullableProperties ReadOptionalNullableProperties(string json, string format, bool useJsonModel = false) + { + var options = new ModelReaderWriterOptions(format); + var data = BinaryData.FromString(json); + if (useJsonModel) + { + var reader = new Utf8JsonReader(data.ToMemory().Span); + return ((IJsonModel)new OptionalNullableProperties(requiredNullable: null)).Create(ref reader, options)!; + } + return ModelReaderWriter.Read(data, options, SampleTypeSpecContext.Default)!; + } + + private static void AssertModelJson(T model, string expectedJson, string format) where T : IJsonModel + { + var options = new ModelReaderWriterOptions(format); + using var expected = JsonDocument.Parse(expectedJson); + using var actual = JsonDocument.Parse(ModelReaderWriter.Write(model, options, SampleTypeSpecContext.Default)); + AssertJsonShape(expected.RootElement, actual.RootElement); + + var buffer = new ArrayBufferWriter(); + using (var writer = new Utf8JsonWriter(buffer)) + { + model.Write(writer, options); + } + using var direct = JsonDocument.Parse(buffer.WrittenMemory); + AssertJsonShape(expected.RootElement, direct.RootElement); + } + + private static void AssertJsonShape(JsonElement expected, JsonElement actual) + { + Assert.That(actual.ValueKind, Is.EqualTo(expected.ValueKind)); + switch (expected.ValueKind) + { + case JsonValueKind.Object: + Assert.That( + actual.EnumerateObject().Select(property => property.Name), + Is.EquivalentTo(expected.EnumerateObject().Select(property => property.Name))); + foreach (var property in expected.EnumerateObject()) + { + AssertJsonShape(property.Value, actual.GetProperty(property.Name)); + } + break; + case JsonValueKind.Array: + Assert.That(actual.GetArrayLength(), Is.EqualTo(expected.GetArrayLength())); + for (int i = 0; i < expected.GetArrayLength(); i++) + { + AssertJsonShape(expected[i], actual[i]); + } + break; + case JsonValueKind.String: + Assert.That(actual.GetString(), Is.EqualTo(expected.GetString())); + break; + case JsonValueKind.Number: + Assert.That(actual.GetDecimal(), Is.EqualTo(expected.GetDecimal())); + break; + } + } + private static void AssertBase64Value(byte[] payload, string format) { string expected = Convert.ToBase64String(payload); diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/Sample-TypeSpec.tsp b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/Sample-TypeSpec.tsp index 7daa3c98cff..992097cf8c7 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/Sample-TypeSpec.tsp +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/Sample-TypeSpec.tsp @@ -330,6 +330,57 @@ model NullableDynamicModel { listOfDictionaries?: (Record | null)[] | null; } +@usage(Usage.input | Usage.output | Usage.json) +@access(Access.public) +model OptionalNullableBase { + inheritedNullable?: string | null; +} + +@dynamicModel +@usage(Usage.input | Usage.output | Usage.json) +@access(Access.public) +model OptionalNullableDynamicProperties extends OptionalNullableBase {} + +@usage(Usage.input | Usage.output | Usage.json) +@access(Access.public) +model OptionalNullableChild { + value: string; +} + +@usage(Usage.input | Usage.output | Usage.json) +@access(Access.public) +model OptionalNullableProperties extends OptionalNullableBase { + nullableModel?: OptionalNullableChild | null; + nullableString?: string | null; + nullableInt?: int32 | null; + nullableBoolean?: boolean | null; + nullableEnum?: StringFixedEnum | null; + nullableDateTime?: utcDateTime | null; + nullableBytes?: bytes | null; + nullableList?: (string | null)[] | null; + nullableDictionary?: Record | null; + + @visibility(Lifecycle.Read) + readOnlyNullable?: string | null; + + requiredNullable: string | null; + optionalNonNullable?: string; + optionalNonNullableInt?: int32; +} + +@usage(Usage.input | Usage.output | Usage.json) +@access(Access.public) +model OptionalNullableContainer { + child: OptionalNullableProperties; +} + +@usage(Usage.input | Usage.output | Usage.json) +@access(Access.public) +model OptionalNullableFieldNames extends Record { + additionalStringProperties?: string | null; + additionalStringPropertiesIsDefined?: string | null; +} + @nsDeclarations enum XmlNamespaces { ns1: "https://example.com/ns1", 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..4257548c95a 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 @@ -131,18 +131,25 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit } else if (Optional.IsCollectionDefined(OptionalNullableList)) { - writer.WritePropertyName("optionalNullableList"u8); - writer.WriteStartArray(); - for (int i = 0; i < OptionalNullableList.Count; i++) + if (OptionalNullableList != null) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.optionalNullableList[{i}]"))) + writer.WritePropertyName("optionalNullableList"u8); + writer.WriteStartArray(); + for (int i = 0; i < OptionalNullableList.Count; i++) { - continue; + if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.optionalNullableList[{i}]"))) + { + continue; + } + writer.WriteNumberValue(OptionalNullableList[i]); } - writer.WriteNumberValue(OptionalNullableList[i]); + Patch.WriteTo(writer, "$.optionalNullableList"u8); + writer.WriteEndArray(); + } + else + { + writer.WriteNull("optionalNullableList"u8); } - Patch.WriteTo(writer, "$.optionalNullableList"u8); - writer.WriteEndArray(); } if (Patch.Contains("$.requiredNullableList"u8)) { @@ -173,28 +180,35 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit } if (Optional.IsCollectionDefined(OptionalNullableDictionary) && !Patch.Contains("$.optionalNullableDictionary"u8)) { - writer.WritePropertyName("optionalNullableDictionary"u8); - writer.WriteStartObject(); + if (OptionalNullableDictionary != null) + { + writer.WritePropertyName("optionalNullableDictionary"u8); + writer.WriteStartObject(); #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) - { - writer.WritePropertyName(item.Key); - writer.WriteNumberValue(item.Value); + if (!patchContains) + { + writer.WritePropertyName(item.Key); + writer.WriteNumberValue(item.Value); + } } - } - Patch.WriteTo(writer, "$.optionalNullableDictionary"u8); - writer.WriteEndObject(); + Patch.WriteTo(writer, "$.optionalNullableDictionary"u8); + writer.WriteEndObject(); + } + else + { + writer.WriteNull("optionalNullableDictionary"u8); + } } if (Optional.IsCollectionDefined(RequiredNullableDictionary) && !Patch.Contains("$.requiredNullableDictionary"u8)) { @@ -513,9 +527,9 @@ internal static DynamicModel DeserializeDynamicModel(JsonElement element, Binary string name = default; BinaryData optionalUnknown = default; int? optionalInt = default; - IList optionalNullableList = default; + IList optionalNullableList = new ChangeTrackingList(); IList requiredNullableList = default; - IDictionary optionalNullableDictionary = default; + IDictionary optionalNullableDictionary = new ChangeTrackingDictionary(); IDictionary requiredNullableDictionary = default; IDictionary primitiveDictionary = default; AnotherDynamicModel foo = default; @@ -557,6 +571,7 @@ internal static DynamicModel DeserializeDynamicModel(JsonElement element, Binary { if (prop.Value.ValueKind == JsonValueKind.Null) { + optionalNullableList = null; continue; } List array = new List(); @@ -586,6 +601,7 @@ internal static DynamicModel DeserializeDynamicModel(JsonElement element, Binary { if (prop.Value.ValueKind == JsonValueKind.Null) { + optionalNullableDictionary = null; continue; } Dictionary dictionary = new Dictionary(); @@ -740,9 +756,9 @@ internal static DynamicModel DeserializeDynamicModel(JsonElement element, Binary name, optionalUnknown, optionalInt, - optionalNullableList ?? new ChangeTrackingList(), + optionalNullableList, requiredNullableList, - optionalNullableDictionary ?? new ChangeTrackingDictionary(), + optionalNullableDictionary, requiredNullableDictionary, primitiveDictionary, foo, 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..570f2703bc7 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 @@ -83,10 +83,17 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit throw new FormatException($"The model {nameof(NullableDynamicModel)} does not support writing '{format}' format."); } #pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. - if (Optional.IsDefined(ModelValue) && !Patch.Contains("$.modelValue"u8)) + if ((_modelValueIsDefined || Optional.IsDefined(ModelValue)) && !Patch.Contains("$.modelValue"u8)) { - writer.WritePropertyName("modelValue"u8); - writer.WriteObjectValue(ModelValue, options); + if (ModelValue != null) + { + writer.WritePropertyName("modelValue"u8); + writer.WriteObjectValue(ModelValue, options); + } + else + { + writer.WriteNull("modelValue"u8); + } } if (Patch.Contains("$.children"u8)) { @@ -98,43 +105,57 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit } else if (Optional.IsCollectionDefined(Children)) { - writer.WritePropertyName("children"u8); - writer.WriteStartArray(); - for (int i = 0; i < Children.Count; i++) + if (Children != null) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.children[{i}]")) || Children[i] != null && Children[i].Patch.IsRemoved("$"u8)) + writer.WritePropertyName("children"u8); + writer.WriteStartArray(); + for (int i = 0; i < Children.Count; i++) { - continue; + if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.children[{i}]")) || Children[i] != null && Children[i].Patch.IsRemoved("$"u8)) + { + continue; + } + writer.WriteObjectValue(Children[i], options); } - writer.WriteObjectValue(Children[i], options); + Patch.WriteTo(writer, "$.children"u8); + writer.WriteEndArray(); + } + else + { + writer.WriteNull("children"u8); } - Patch.WriteTo(writer, "$.children"u8); - writer.WriteEndArray(); } if (Optional.IsCollectionDefined(ChildDictionary) && !Patch.Contains("$.childDictionary"u8)) { - writer.WritePropertyName("childDictionary"u8); - writer.WriteStartObject(); + if (ChildDictionary != null) + { + writer.WritePropertyName("childDictionary"u8); + writer.WriteStartObject(); #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) - { - writer.WritePropertyName(item.Key); - writer.WriteObjectValue(item.Value, options); + if (!patchContains) + { + writer.WritePropertyName(item.Key); + writer.WriteObjectValue(item.Value, options); + } } - } - Patch.WriteTo(writer, "$.childDictionary"u8); - writer.WriteEndObject(); + Patch.WriteTo(writer, "$.childDictionary"u8); + writer.WriteEndObject(); + } + else + { + writer.WriteNull("childDictionary"u8); + } } if (Patch.Contains("$.nestedChildren"u8)) { @@ -146,123 +167,144 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit } else if (Optional.IsCollectionDefined(NestedChildren)) { - writer.WritePropertyName("nestedChildren"u8); - writer.WriteStartArray(); - for (int i = 0; i < NestedChildren.Count; i++) + if (NestedChildren != null) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.nestedChildren[{i}]"))) - { - continue; - } - if (NestedChildren[i] == null) - { - writer.WriteNullValue(); - continue; - } + writer.WritePropertyName("nestedChildren"u8); writer.WriteStartArray(); - for (int i0 = 0; i0 < NestedChildren[i].Count; i0++) + for (int i = 0; i < NestedChildren.Count; i++) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.nestedChildren[{i}][{i0}]")) || NestedChildren[i][i0] != null && NestedChildren[i][i0].Patch.IsRemoved("$"u8)) + if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.nestedChildren[{i}]"))) { continue; } - writer.WriteObjectValue(NestedChildren[i][i0], options); + if (NestedChildren[i] == null) + { + writer.WriteNullValue(); + continue; + } + 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)) + { + continue; + } + writer.WriteObjectValue(NestedChildren[i][i0], options); + } + Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.nestedChildren[{i}]")); + writer.WriteEndArray(); } - Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.nestedChildren[{i}]")); + Patch.WriteTo(writer, "$.nestedChildren"u8); writer.WriteEndArray(); } - Patch.WriteTo(writer, "$.nestedChildren"u8); - writer.WriteEndArray(); + else + { + writer.WriteNull("nestedChildren"u8); + } } if (Optional.IsCollectionDefined(NestedChildDictionary) && !Patch.Contains("$.nestedChildDictionary"u8)) { - writer.WritePropertyName("nestedChildDictionary"u8); - writer.WriteStartObject(); + if (NestedChildDictionary != null) + { + writer.WritePropertyName("nestedChildDictionary"u8); + writer.WriteStartObject(); #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) - { - 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 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($"$.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)); + 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)); + 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); + if (!patchContains0) + { + writer.WritePropertyName(item0.Key); + writer.WriteObjectValue(item0.Value, options); + } } - } - Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.nestedChildDictionary[\"{item.Key}\"]")); - writer.WriteEndObject(); + Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.nestedChildDictionary[\"{item.Key}\"]")); + writer.WriteEndObject(); + } } - } - Patch.WriteTo(writer, "$.nestedChildDictionary"u8); - writer.WriteEndObject(); + Patch.WriteTo(writer, "$.nestedChildDictionary"u8); + writer.WriteEndObject(); + } + else + { + writer.WriteNull("nestedChildDictionary"u8); + } } if (Optional.IsCollectionDefined(DictionaryChildren) && !Patch.Contains("$.dictionaryChildren"u8)) { - writer.WritePropertyName("dictionaryChildren"u8); - writer.WriteStartObject(); + if (DictionaryChildren != null) + { + writer.WritePropertyName("dictionaryChildren"u8); + writer.WriteStartObject(); #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) - { - writer.WritePropertyName(item.Key); - if (item.Value == null) - { - writer.WriteNullValue(); - continue; - } - writer.WriteStartArray(); - for (int i = 0; i < item.Value.Count; i++) + if (!patchContains) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.dictionaryChildren[\"{item.Key}\"][{i}]")) || item.Value[i] != null && item.Value[i].Patch.IsRemoved("$"u8)) + writer.WritePropertyName(item.Key); + if (item.Value == null) { + writer.WriteNullValue(); continue; } - writer.WriteObjectValue(item.Value[i], options); + 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)) + { + continue; + } + writer.WriteObjectValue(item.Value[i], options); + } + Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.dictionaryChildren[\"{item.Key}\"]")); + writer.WriteEndArray(); } - Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.dictionaryChildren[\"{item.Key}\"]")); - writer.WriteEndArray(); } - } - Patch.WriteTo(writer, "$.dictionaryChildren"u8); - writer.WriteEndObject(); + Patch.WriteTo(writer, "$.dictionaryChildren"u8); + writer.WriteEndObject(); + } + else + { + writer.WriteNull("dictionaryChildren"u8); + } } if (Patch.Contains("$.listOfDictionaries"u8)) { @@ -274,43 +316,50 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit } else if (Optional.IsCollectionDefined(ListOfDictionaries)) { - writer.WritePropertyName("listOfDictionaries"u8); - writer.WriteStartArray(); - for (int i = 0; i < ListOfDictionaries.Count; i++) + if (ListOfDictionaries != null) { - if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]"))) - { - continue; - } - if (ListOfDictionaries[i] == null) + writer.WritePropertyName("listOfDictionaries"u8); + writer.WriteStartArray(); + for (int i = 0; i < ListOfDictionaries.Count; i++) { - writer.WriteNullValue(); - continue; - } - writer.WriteStartObject(); + if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]"))) + { + continue; + } + if (ListOfDictionaries[i] == null) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStartObject(); #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) - { - writer.WritePropertyName(item.Key); - writer.WriteObjectValue(item.Value, options); + if (!patchContains) + { + writer.WritePropertyName(item.Key); + writer.WriteObjectValue(item.Value, options); + } } - } - Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]")); - writer.WriteEndObject(); + Patch.WriteTo(writer, Encoding.UTF8.GetBytes($"$.listOfDictionaries[{i}]")); + writer.WriteEndObject(); + } + Patch.WriteTo(writer, "$.listOfDictionaries"u8); + writer.WriteEndArray(); + } + else + { + writer.WriteNull("listOfDictionaries"u8); } - Patch.WriteTo(writer, "$.listOfDictionaries"u8); - writer.WriteEndArray(); } Patch.WriteTo(writer); @@ -343,13 +392,14 @@ internal static NullableDynamicModel DeserializeNullableDynamicModel(JsonElement { return null; } + bool modelValueIsDefined = false; AnotherDynamicModel modelValue = default; - IList children = default; - IDictionary childDictionary = default; - IList> nestedChildren = default; - IDictionary> nestedChildDictionary = default; - IDictionary> dictionaryChildren = default; - IList> listOfDictionaries = default; + IList children = new ChangeTrackingList(); + IDictionary childDictionary = new ChangeTrackingDictionary(); + IList> nestedChildren = new ChangeTrackingList>(); + IDictionary> nestedChildDictionary = new ChangeTrackingDictionary>(); + IDictionary> dictionaryChildren = new ChangeTrackingDictionary>(); + IList> listOfDictionaries = new ChangeTrackingList>(); #pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. JsonPatch patch = new JsonPatch(data is null ? ReadOnlyMemory.Empty : data.ToMemory()); #pragma warning restore SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. @@ -357,6 +407,7 @@ internal static NullableDynamicModel DeserializeNullableDynamicModel(JsonElement { if (prop.NameEquals("modelValue"u8)) { + modelValueIsDefined = true; if (prop.Value.ValueKind == JsonValueKind.Null) { modelValue = null; @@ -369,6 +420,7 @@ internal static NullableDynamicModel DeserializeNullableDynamicModel(JsonElement { if (prop.Value.ValueKind == JsonValueKind.Null) { + children = null; continue; } List array = new List(); @@ -383,6 +435,7 @@ internal static NullableDynamicModel DeserializeNullableDynamicModel(JsonElement { if (prop.Value.ValueKind == JsonValueKind.Null) { + childDictionary = null; continue; } Dictionary dictionary = new Dictionary(); @@ -397,6 +450,7 @@ internal static NullableDynamicModel DeserializeNullableDynamicModel(JsonElement { if (prop.Value.ValueKind == JsonValueKind.Null) { + nestedChildren = null; continue; } List> array = new List>(); @@ -423,6 +477,7 @@ internal static NullableDynamicModel DeserializeNullableDynamicModel(JsonElement { if (prop.Value.ValueKind == JsonValueKind.Null) { + nestedChildDictionary = null; continue; } Dictionary> dictionary = new Dictionary>(); @@ -449,6 +504,7 @@ internal static NullableDynamicModel DeserializeNullableDynamicModel(JsonElement { if (prop.Value.ValueKind == JsonValueKind.Null) { + dictionaryChildren = null; continue; } Dictionary> dictionary = new Dictionary>(); @@ -475,6 +531,7 @@ internal static NullableDynamicModel DeserializeNullableDynamicModel(JsonElement { if (prop.Value.ValueKind == JsonValueKind.Null) { + listOfDictionaries = null; continue; } List> array = new List>(); @@ -501,13 +558,16 @@ internal static NullableDynamicModel DeserializeNullableDynamicModel(JsonElement } return new NullableDynamicModel( modelValue, - children ?? new ChangeTrackingList(), - childDictionary ?? new ChangeTrackingDictionary(), - nestedChildren ?? new ChangeTrackingList>(), - nestedChildDictionary ?? new ChangeTrackingDictionary>(), - dictionaryChildren ?? new ChangeTrackingDictionary>(), - listOfDictionaries ?? new ChangeTrackingList>(), - patch); + children, + childDictionary, + nestedChildren, + nestedChildDictionary, + dictionaryChildren, + listOfDictionaries, + patch) + { + _modelValueIsDefined = modelValueIsDefined + }; } /// diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/NullableDynamicModel.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/NullableDynamicModel.cs index 646b38bb9ed..070644855e5 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/NullableDynamicModel.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/NullableDynamicModel.cs @@ -18,6 +18,8 @@ public partial class NullableDynamicModel { [Experimental("SCME0001")] private JsonPatch _patch; + private AnotherDynamicModel _modelValue; + internal bool _modelValueIsDefined; /// Initializes a new instance of . #pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. @@ -45,7 +47,7 @@ public NullableDynamicModel() #pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. internal NullableDynamicModel(AnotherDynamicModel modelValue, IList children, IDictionary childDictionary, IList> nestedChildren, IDictionary> nestedChildDictionary, IDictionary> dictionaryChildren, IList> listOfDictionaries, in JsonPatch patch) { - ModelValue = modelValue; + _modelValue = modelValue; Children = children; ChildDictionary = childDictionary; NestedChildren = nestedChildren; @@ -64,7 +66,18 @@ internal NullableDynamicModel(AnotherDynamicModel modelValue, IList ref _patch; /// Gets or sets the ModelValue. - public AnotherDynamicModel ModelValue { get; set; } + public AnotherDynamicModel ModelValue + { + get + { + return _modelValue; + } + set + { + _modelValue = value; + _modelValueIsDefined = true; + } + } /// Gets or sets the Children. public IList Children { get; set; } diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableBase.Serialization.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableBase.Serialization.cs new file mode 100644 index 00000000000..4492922b267 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableBase.Serialization.cs @@ -0,0 +1,157 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace SampleTypeSpec +{ + /// The OptionalNullableBase. + public partial class OptionalNullableBase : IJsonModel + { + /// The data to parse. + /// The client options for reading and writing models. + protected virtual OptionalNullableBase PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data, ModelSerializationExtensions.JsonDocumentOptions)) + { + return DeserializeOptionalNullableBase(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(OptionalNullableBase)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, SampleTypeSpecContext.Default); + default: + throw new FormatException($"The model {nameof(OptionalNullableBase)} does not support writing '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The data to parse. + /// The client options for reading and writing models. + OptionalNullableBase IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(OptionalNullableBase)} does not support writing '{format}' format."); + } + if (_inheritedNullableIsDefined || Optional.IsDefined(InheritedNullable)) + { + if (InheritedNullable != null) + { + writer.WritePropertyName("inheritedNullable"u8); + writer.WriteStringValue(InheritedNullable); + } + else + { + writer.WriteNull("inheritedNullable"u8); + } + } + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + OptionalNullableBase IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual OptionalNullableBase JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(OptionalNullableBase)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeOptionalNullableBase(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static OptionalNullableBase DeserializeOptionalNullableBase(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + bool inheritedNullableIsDefined = false; + string inheritedNullable = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("inheritedNullable"u8)) + { + inheritedNullableIsDefined = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + inheritedNullable = null; + continue; + } + inheritedNullable = prop.Value.GetString(); + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, prop.Value.GetUtf8Bytes()); + } + } + return new OptionalNullableBase(inheritedNullable, additionalBinaryDataProperties) + { + _inheritedNullableIsDefined = inheritedNullableIsDefined + }; + } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableBase.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableBase.cs new file mode 100644 index 00000000000..4cd6b2b6271 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableBase.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace SampleTypeSpec +{ + /// The OptionalNullableBase. + public partial class OptionalNullableBase + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + private string _inheritedNullable; + internal bool _inheritedNullableIsDefined; + + /// Initializes a new instance of . + public OptionalNullableBase() + { + } + + /// Initializes a new instance of . + /// + /// Keeps track of any properties unknown to the library. + internal OptionalNullableBase(string inheritedNullable, IDictionary additionalBinaryDataProperties) + { + _inheritedNullable = inheritedNullable; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// Gets or sets the InheritedNullable. + public string InheritedNullable + { + get + { + return _inheritedNullable; + } + set + { + _inheritedNullable = value; + _inheritedNullableIsDefined = true; + } + } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableChild.Serialization.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableChild.Serialization.cs new file mode 100644 index 00000000000..5d619ba0e15 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableChild.Serialization.cs @@ -0,0 +1,142 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace SampleTypeSpec +{ + /// The OptionalNullableChild. + public partial class OptionalNullableChild : IJsonModel + { + /// Initializes a new instance of for deserialization. + internal OptionalNullableChild() + { + } + + /// The data to parse. + /// The client options for reading and writing models. + protected virtual OptionalNullableChild PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data, ModelSerializationExtensions.JsonDocumentOptions)) + { + return DeserializeOptionalNullableChild(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(OptionalNullableChild)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, SampleTypeSpecContext.Default); + default: + throw new FormatException($"The model {nameof(OptionalNullableChild)} does not support writing '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The data to parse. + /// The client options for reading and writing models. + OptionalNullableChild IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(OptionalNullableChild)} does not support writing '{format}' format."); + } + writer.WritePropertyName("value"u8); + writer.WriteStringValue(Value); + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + OptionalNullableChild IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual OptionalNullableChild JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(OptionalNullableChild)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeOptionalNullableChild(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static OptionalNullableChild DeserializeOptionalNullableChild(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string value = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("value"u8)) + { + value = prop.Value.GetString(); + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, prop.Value.GetUtf8Bytes()); + } + } + return new OptionalNullableChild(value, additionalBinaryDataProperties); + } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableChild.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableChild.cs new file mode 100644 index 00000000000..efc5ff1fee0 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableChild.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace SampleTypeSpec +{ + /// The OptionalNullableChild. + public partial class OptionalNullableChild + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + + /// Initializes a new instance of . + /// + /// is null. + public OptionalNullableChild(string value) + { + Argument.AssertNotNull(value, nameof(value)); + + Value = value; + } + + /// Initializes a new instance of . + /// + /// Keeps track of any properties unknown to the library. + internal OptionalNullableChild(string value, IDictionary additionalBinaryDataProperties) + { + Value = value; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// Gets or sets the Value. + public string Value { get; set; } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableContainer.Serialization.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableContainer.Serialization.cs new file mode 100644 index 00000000000..f1c7c65686e --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableContainer.Serialization.cs @@ -0,0 +1,142 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace SampleTypeSpec +{ + /// The OptionalNullableContainer. + public partial class OptionalNullableContainer : IJsonModel + { + /// Initializes a new instance of for deserialization. + internal OptionalNullableContainer() + { + } + + /// The data to parse. + /// The client options for reading and writing models. + protected virtual OptionalNullableContainer PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data, ModelSerializationExtensions.JsonDocumentOptions)) + { + return DeserializeOptionalNullableContainer(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(OptionalNullableContainer)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, SampleTypeSpecContext.Default); + default: + throw new FormatException($"The model {nameof(OptionalNullableContainer)} does not support writing '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The data to parse. + /// The client options for reading and writing models. + OptionalNullableContainer IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(OptionalNullableContainer)} does not support writing '{format}' format."); + } + writer.WritePropertyName("child"u8); + writer.WriteObjectValue(Child, options); + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + OptionalNullableContainer IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual OptionalNullableContainer JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(OptionalNullableContainer)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeOptionalNullableContainer(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static OptionalNullableContainer DeserializeOptionalNullableContainer(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + OptionalNullableProperties child = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("child"u8)) + { + child = OptionalNullableProperties.DeserializeOptionalNullableProperties(prop.Value, options); + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, prop.Value.GetUtf8Bytes()); + } + } + return new OptionalNullableContainer(child, additionalBinaryDataProperties); + } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableContainer.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableContainer.cs new file mode 100644 index 00000000000..680d401c960 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableContainer.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace SampleTypeSpec +{ + /// The OptionalNullableContainer. + public partial class OptionalNullableContainer + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + + /// Initializes a new instance of . + /// + /// is null. + public OptionalNullableContainer(OptionalNullableProperties child) + { + Argument.AssertNotNull(child, nameof(child)); + + Child = child; + } + + /// Initializes a new instance of . + /// + /// Keeps track of any properties unknown to the library. + internal OptionalNullableContainer(OptionalNullableProperties child, IDictionary additionalBinaryDataProperties) + { + Child = child; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// Gets or sets the Child. + public OptionalNullableProperties Child { get; set; } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableDynamicProperties.Serialization.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableDynamicProperties.Serialization.cs new file mode 100644 index 00000000000..e311409044c --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableDynamicProperties.Serialization.cs @@ -0,0 +1,154 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Text; +using System.Text.Json; + +namespace SampleTypeSpec +{ + /// The OptionalNullableDynamicProperties. + public partial class OptionalNullableDynamicProperties : OptionalNullableBase, IJsonModel + { + /// The data to parse. + /// The client options for reading and writing models. + protected override OptionalNullableBase PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data, ModelSerializationExtensions.JsonDocumentOptions)) + { + return DeserializeOptionalNullableDynamicProperties(document.RootElement, data, options); + } + default: + throw new FormatException($"The model {nameof(OptionalNullableDynamicProperties)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + protected override BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, SampleTypeSpecContext.Default); + default: + throw new FormatException($"The model {nameof(OptionalNullableDynamicProperties)} does not support writing '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The data to parse. + /// The client options for reading and writing models. + OptionalNullableDynamicProperties IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => (OptionalNullableDynamicProperties)PersistableModelCreateCore(data, options); + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { +#pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. + if (Patch.Contains("$"u8)) + { + writer.WriteRawValue(Patch.GetJson("$"u8)); + return; + } +#pragma warning restore SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. + + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected override void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(OptionalNullableDynamicProperties)} does not support writing '{format}' format."); + } +#pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. + if ((_inheritedNullableIsDefined || Optional.IsDefined(InheritedNullable)) && !Patch.Contains("$.inheritedNullable"u8)) + { + if (InheritedNullable != null) + { + writer.WritePropertyName("inheritedNullable"u8); + writer.WriteStringValue(InheritedNullable); + } + else + { + writer.WriteNull("inheritedNullable"u8); + } + } + + Patch.WriteTo(writer); +#pragma warning restore SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. + } + + /// The JSON reader. + /// The client options for reading and writing models. + OptionalNullableDynamicProperties IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => (OptionalNullableDynamicProperties)JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected override OptionalNullableBase JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(OptionalNullableDynamicProperties)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeOptionalNullableDynamicProperties(document.RootElement, null, options); + } + + /// The JSON element to deserialize. + /// The data to parse. + /// The client options for reading and writing models. + internal static OptionalNullableDynamicProperties DeserializeOptionalNullableDynamicProperties(JsonElement element, BinaryData data, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + bool inheritedNullableIsDefined = false; + string inheritedNullable = default; +#pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. + JsonPatch patch = new JsonPatch(data is null ? ReadOnlyMemory.Empty : data.ToMemory()); +#pragma warning restore SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("inheritedNullable"u8)) + { + inheritedNullableIsDefined = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + inheritedNullable = null; + continue; + } + inheritedNullable = prop.Value.GetString(); + continue; + } + patch.Set([.. "$."u8, .. Encoding.UTF8.GetBytes(prop.Name)], prop.Value.GetUtf8Bytes()); + } + return new OptionalNullableDynamicProperties(inheritedNullable, patch) + { + _inheritedNullableIsDefined = inheritedNullableIsDefined + }; + } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableDynamicProperties.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableDynamicProperties.cs new file mode 100644 index 00000000000..0b505d82834 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableDynamicProperties.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.ClientModel.Primitives; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace SampleTypeSpec +{ + /// The OptionalNullableDynamicProperties. + public partial class OptionalNullableDynamicProperties : OptionalNullableBase + { + [Experimental("SCME0001")] + private JsonPatch _patch; + + /// Initializes a new instance of . + public OptionalNullableDynamicProperties() + { + } + + /// Initializes a new instance of . + /// + /// +#pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. + internal OptionalNullableDynamicProperties(string inheritedNullable, in JsonPatch patch) : base(inheritedNullable, default) + { + _patch = patch; + } +#pragma warning restore SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. + + /// Gets the Patch. + [JsonIgnore] + [EditorBrowsable(EditorBrowsableState.Never)] + [Experimental("SCME0001")] + public ref JsonPatch Patch => ref _patch; + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableFieldNames.Serialization.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableFieldNames.Serialization.cs new file mode 100644 index 00000000000..7d0f7273b08 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableFieldNames.Serialization.cs @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace SampleTypeSpec +{ + /// The OptionalNullableFieldNames. + public partial class OptionalNullableFieldNames : IJsonModel + { + /// The data to parse. + /// The client options for reading and writing models. + protected virtual OptionalNullableFieldNames PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data, ModelSerializationExtensions.JsonDocumentOptions)) + { + return DeserializeOptionalNullableFieldNames(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(OptionalNullableFieldNames)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, SampleTypeSpecContext.Default); + default: + throw new FormatException($"The model {nameof(OptionalNullableFieldNames)} does not support writing '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The data to parse. + /// The client options for reading and writing models. + OptionalNullableFieldNames IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(OptionalNullableFieldNames)} does not support writing '{format}' format."); + } + if (_additionalStringPropertiesIsDefined0 || Optional.IsDefined(AdditionalStringProperties)) + { + if (AdditionalStringProperties != null) + { + writer.WritePropertyName("additionalStringProperties"u8); + writer.WriteStringValue(AdditionalStringProperties); + } + else + { + writer.WriteNull("additionalStringProperties"u8); + } + } + if (_additionalStringPropertiesIsDefinedIsDefined || Optional.IsDefined(AdditionalStringPropertiesIsDefined)) + { + if (AdditionalStringPropertiesIsDefined != null) + { + writer.WritePropertyName("additionalStringPropertiesIsDefined"u8); + writer.WriteStringValue(AdditionalStringPropertiesIsDefined); + } + else + { + writer.WriteNull("additionalStringPropertiesIsDefined"u8); + } + } + foreach (var item in AdditionalProperties) + { + writer.WritePropertyName(item.Key); + writer.WriteStringValue(item.Value); + } + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + OptionalNullableFieldNames IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual OptionalNullableFieldNames JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(OptionalNullableFieldNames)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeOptionalNullableFieldNames(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static OptionalNullableFieldNames DeserializeOptionalNullableFieldNames(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + bool additionalStringPropertiesIsDefined0 = false; + string additionalStringProperties = default; + bool additionalStringPropertiesIsDefinedIsDefined = false; + string additionalStringPropertiesIsDefined = default; + IDictionary additionalProperties = new ChangeTrackingDictionary(); + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("additionalStringProperties"u8)) + { + additionalStringPropertiesIsDefined0 = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + additionalStringProperties = null; + continue; + } + additionalStringProperties = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("additionalStringPropertiesIsDefined"u8)) + { + additionalStringPropertiesIsDefinedIsDefined = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + additionalStringPropertiesIsDefined = null; + continue; + } + additionalStringPropertiesIsDefined = prop.Value.GetString(); + continue; + } + switch (prop.Value.ValueKind) + { + case JsonValueKind.String: + additionalProperties.Add(prop.Name, prop.Value.GetString()); + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, prop.Value.GetUtf8Bytes()); + } + } + return new OptionalNullableFieldNames(additionalStringProperties, additionalStringPropertiesIsDefined, additionalProperties, additionalBinaryDataProperties) + { + _additionalStringPropertiesIsDefined0 = additionalStringPropertiesIsDefined0, + _additionalStringPropertiesIsDefinedIsDefined = additionalStringPropertiesIsDefinedIsDefined + }; + } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableFieldNames.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableFieldNames.cs new file mode 100644 index 00000000000..5f4e4bb5cd2 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableFieldNames.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace SampleTypeSpec +{ + /// The OptionalNullableFieldNames. + public partial class OptionalNullableFieldNames + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + private IDictionary _additionalStringProperties; + private string _additionalStringProperties0; + internal bool _additionalStringPropertiesIsDefined0; + private string _additionalStringPropertiesIsDefined; + internal bool _additionalStringPropertiesIsDefinedIsDefined; + + /// Initializes a new instance of . + public OptionalNullableFieldNames() + { + _additionalStringProperties = new ChangeTrackingDictionary(); + } + + /// Initializes a new instance of . + /// + /// + /// + /// Keeps track of any properties unknown to the library. + internal OptionalNullableFieldNames(string additionalStringProperties, string additionalStringPropertiesIsDefined, IDictionary additionalProperties, IDictionary additionalBinaryDataProperties) + { + _additionalStringProperties0 = additionalStringProperties; + _additionalStringPropertiesIsDefined = additionalStringPropertiesIsDefined; + _additionalStringProperties = additionalProperties; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// Gets or sets the AdditionalStringProperties. + public string AdditionalStringProperties + { + get + { + return _additionalStringProperties0; + } + set + { + _additionalStringProperties0 = value; + _additionalStringPropertiesIsDefined0 = true; + } + } + + /// Gets or sets the AdditionalStringPropertiesIsDefined. + public string AdditionalStringPropertiesIsDefined + { + get + { + return _additionalStringPropertiesIsDefined; + } + set + { + _additionalStringPropertiesIsDefined = value; + _additionalStringPropertiesIsDefinedIsDefined = true; + } + } + + /// Gets the AdditionalProperties. + public IDictionary AdditionalProperties => _additionalStringProperties; + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableProperties.Serialization.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableProperties.Serialization.cs new file mode 100644 index 00000000000..ef501f776c5 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableProperties.Serialization.cs @@ -0,0 +1,496 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace SampleTypeSpec +{ + /// The OptionalNullableProperties. + public partial class OptionalNullableProperties : OptionalNullableBase, IJsonModel + { + /// Initializes a new instance of for deserialization. + internal OptionalNullableProperties() + { + } + + /// The data to parse. + /// The client options for reading and writing models. + protected override OptionalNullableBase PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data, ModelSerializationExtensions.JsonDocumentOptions)) + { + return DeserializeOptionalNullableProperties(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(OptionalNullableProperties)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + protected override BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, SampleTypeSpecContext.Default); + default: + throw new FormatException($"The model {nameof(OptionalNullableProperties)} does not support writing '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The data to parse. + /// The client options for reading and writing models. + OptionalNullableProperties IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => (OptionalNullableProperties)PersistableModelCreateCore(data, options); + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected override void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(OptionalNullableProperties)} does not support writing '{format}' format."); + } + base.JsonModelWriteCore(writer, options); + if (_nullableModelIsDefined || Optional.IsDefined(NullableModel)) + { + if (NullableModel != null) + { + writer.WritePropertyName("nullableModel"u8); + writer.WriteObjectValue(NullableModel, options); + } + else + { + writer.WriteNull("nullableModel"u8); + } + } + if (_nullableStringIsDefined || Optional.IsDefined(NullableString)) + { + if (NullableString != null) + { + writer.WritePropertyName("nullableString"u8); + writer.WriteStringValue(NullableString); + } + else + { + writer.WriteNull("nullableString"u8); + } + } + if (_nullableIntIsDefined || Optional.IsDefined(NullableInt)) + { + if (NullableInt != null) + { + writer.WritePropertyName("nullableInt"u8); + writer.WriteNumberValue(NullableInt.Value); + } + else + { + writer.WriteNull("nullableInt"u8); + } + } + if (_nullableBooleanIsDefined || Optional.IsDefined(NullableBoolean)) + { + if (NullableBoolean != null) + { + writer.WritePropertyName("nullableBoolean"u8); + writer.WriteBooleanValue(NullableBoolean.Value); + } + else + { + writer.WriteNull("nullableBoolean"u8); + } + } + if (_nullableEnumIsDefined || Optional.IsDefined(NullableEnum)) + { + if (NullableEnum != null) + { + writer.WritePropertyName("nullableEnum"u8); + writer.WriteStringValue(NullableEnum.Value.ToSerialString()); + } + else + { + writer.WriteNull("nullableEnum"u8); + } + } + if (_nullableOnIsDefined || Optional.IsDefined(NullableOn)) + { + if (NullableOn != null) + { + writer.WritePropertyName("nullableDateTime"u8); + writer.WriteStringValue(NullableOn.Value, "O"); + } + else + { + writer.WriteNull("nullableDateTime"u8); + } + } + if (_nullableBytesIsDefined || Optional.IsDefined(NullableBytes)) + { + if (NullableBytes != null) + { + writer.WritePropertyName("nullableBytes"u8); + writer.WriteBase64StringValue(NullableBytes, "D"); + } + else + { + writer.WriteNull("nullableBytes"u8); + } + } + if (Optional.IsCollectionDefined(NullableList)) + { + if (NullableList != null) + { + writer.WritePropertyName("nullableList"u8); + writer.WriteStartArray(); + foreach (string item in NullableList) + { + if (item == null) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStringValue(item); + } + writer.WriteEndArray(); + } + else + { + writer.WriteNull("nullableList"u8); + } + } + if (Optional.IsCollectionDefined(NullableDictionary)) + { + if (NullableDictionary != null) + { + writer.WritePropertyName("nullableDictionary"u8); + writer.WriteStartObject(); + foreach (var item in NullableDictionary) + { + writer.WritePropertyName(item.Key); + if (item.Value == null) + { + writer.WriteNullValue(); + continue; + } + writer.WriteNumberValue(item.Value.Value); + } + writer.WriteEndObject(); + } + else + { + writer.WriteNull("nullableDictionary"u8); + } + } + if (options.Format != "W" && (_readOnlyNullableIsDefined || Optional.IsDefined(ReadOnlyNullable))) + { + if (ReadOnlyNullable != null) + { + writer.WritePropertyName("readOnlyNullable"u8); + writer.WriteStringValue(ReadOnlyNullable); + } + else + { + writer.WriteNull("readOnlyNullable"u8); + } + } + if (Optional.IsDefined(RequiredNullable)) + { + writer.WritePropertyName("requiredNullable"u8); + writer.WriteStringValue(RequiredNullable); + } + else + { + writer.WriteNull("requiredNullable"u8); + } + if (Optional.IsDefined(OptionalNonNullable)) + { + writer.WritePropertyName("optionalNonNullable"u8); + writer.WriteStringValue(OptionalNonNullable); + } + if (Optional.IsDefined(OptionalNonNullableInt)) + { + writer.WritePropertyName("optionalNonNullableInt"u8); + writer.WriteNumberValue(OptionalNonNullableInt.Value); + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + OptionalNullableProperties IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => (OptionalNullableProperties)JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected override OptionalNullableBase JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(OptionalNullableProperties)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeOptionalNullableProperties(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static OptionalNullableProperties DeserializeOptionalNullableProperties(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + bool inheritedNullableIsDefined = false; + string inheritedNullable = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + bool nullableModelIsDefined = false; + OptionalNullableChild nullableModel = default; + bool nullableStringIsDefined = false; + string nullableString = default; + bool nullableIntIsDefined = false; + int? nullableInt = default; + bool nullableBooleanIsDefined = false; + bool? nullableBoolean = default; + bool nullableEnumIsDefined = false; + StringFixedEnum? nullableEnum = default; + bool nullableOnIsDefined = false; + DateTimeOffset? nullableOn = default; + bool nullableBytesIsDefined = false; + BinaryData nullableBytes = default; + IList nullableList = new ChangeTrackingList(); + IDictionary nullableDictionary = new ChangeTrackingDictionary(); + bool readOnlyNullableIsDefined = false; + string readOnlyNullable = default; + string requiredNullable = default; + string optionalNonNullable = default; + int? optionalNonNullableInt = default; + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("inheritedNullable"u8)) + { + inheritedNullableIsDefined = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + inheritedNullable = null; + continue; + } + inheritedNullable = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("nullableModel"u8)) + { + nullableModelIsDefined = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + nullableModel = null; + continue; + } + nullableModel = OptionalNullableChild.DeserializeOptionalNullableChild(prop.Value, options); + continue; + } + if (prop.NameEquals("nullableString"u8)) + { + nullableStringIsDefined = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + nullableString = null; + continue; + } + nullableString = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("nullableInt"u8)) + { + nullableIntIsDefined = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + nullableInt = null; + continue; + } + nullableInt = prop.Value.GetInt32(); + continue; + } + if (prop.NameEquals("nullableBoolean"u8)) + { + nullableBooleanIsDefined = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + nullableBoolean = null; + continue; + } + nullableBoolean = prop.Value.GetBoolean(); + continue; + } + if (prop.NameEquals("nullableEnum"u8)) + { + nullableEnumIsDefined = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + nullableEnum = null; + continue; + } + nullableEnum = prop.Value.GetString().ToStringFixedEnum(); + continue; + } + if (prop.NameEquals("nullableDateTime"u8)) + { + nullableOnIsDefined = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + nullableOn = null; + continue; + } + nullableOn = prop.Value.GetDateTimeOffset("O"); + continue; + } + if (prop.NameEquals("nullableBytes"u8)) + { + nullableBytesIsDefined = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + nullableBytes = null; + continue; + } + nullableBytes = BinaryData.FromBytes(prop.Value.GetBytesFromBase64("D")); + continue; + } + if (prop.NameEquals("nullableList"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + nullableList = null; + continue; + } + List array = new List(); + foreach (var item in prop.Value.EnumerateArray()) + { + if (item.ValueKind == JsonValueKind.Null) + { + array.Add(null); + } + else + { + array.Add(item.GetString()); + } + } + nullableList = array; + continue; + } + if (prop.NameEquals("nullableDictionary"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + nullableDictionary = null; + continue; + } + Dictionary dictionary = new Dictionary(); + foreach (var prop0 in prop.Value.EnumerateObject()) + { + if (prop0.Value.ValueKind == JsonValueKind.Null) + { + dictionary.Add(prop0.Name, null); + } + else + { + dictionary.Add(prop0.Name, prop0.Value.GetInt32()); + } + } + nullableDictionary = dictionary; + continue; + } + if (prop.NameEquals("readOnlyNullable"u8)) + { + readOnlyNullableIsDefined = true; + if (prop.Value.ValueKind == JsonValueKind.Null) + { + readOnlyNullable = null; + continue; + } + readOnlyNullable = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("requiredNullable"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + requiredNullable = null; + continue; + } + requiredNullable = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("optionalNonNullable"u8)) + { + optionalNonNullable = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("optionalNonNullableInt"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + optionalNonNullableInt = prop.Value.GetInt32(); + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, prop.Value.GetUtf8Bytes()); + } + } + return new OptionalNullableProperties( + inheritedNullable, + additionalBinaryDataProperties, + nullableModel, + nullableString, + nullableInt, + nullableBoolean, + nullableEnum, + nullableOn, + nullableBytes, + nullableList, + nullableDictionary, + readOnlyNullable, + requiredNullable, + optionalNonNullable, + optionalNonNullableInt) + { + _inheritedNullableIsDefined = inheritedNullableIsDefined, + _nullableModelIsDefined = nullableModelIsDefined, + _nullableStringIsDefined = nullableStringIsDefined, + _nullableIntIsDefined = nullableIntIsDefined, + _nullableBooleanIsDefined = nullableBooleanIsDefined, + _nullableEnumIsDefined = nullableEnumIsDefined, + _nullableOnIsDefined = nullableOnIsDefined, + _nullableBytesIsDefined = nullableBytesIsDefined, + _readOnlyNullableIsDefined = readOnlyNullableIsDefined + }; + } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableProperties.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableProperties.cs new file mode 100644 index 00000000000..12ddca173ac --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/OptionalNullableProperties.cs @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace SampleTypeSpec +{ + /// The OptionalNullableProperties. + public partial class OptionalNullableProperties : OptionalNullableBase + { + private OptionalNullableChild _nullableModel; + internal bool _nullableModelIsDefined; + private string _nullableString; + internal bool _nullableStringIsDefined; + private int? _nullableInt; + internal bool _nullableIntIsDefined; + private bool? _nullableBoolean; + internal bool _nullableBooleanIsDefined; + private StringFixedEnum? _nullableEnum; + internal bool _nullableEnumIsDefined; + private DateTimeOffset? _nullableOn; + internal bool _nullableOnIsDefined; + private BinaryData _nullableBytes; + internal bool _nullableBytesIsDefined; + private string _readOnlyNullable; + internal bool _readOnlyNullableIsDefined; + + /// Initializes a new instance of . + /// + public OptionalNullableProperties(string requiredNullable) + { + NullableList = new ChangeTrackingList(); + NullableDictionary = new ChangeTrackingDictionary(); + RequiredNullable = requiredNullable; + } + + /// Initializes a new instance of . + /// + /// Keeps track of any properties unknown to the library. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal OptionalNullableProperties(string inheritedNullable, IDictionary additionalBinaryDataProperties, OptionalNullableChild nullableModel, string nullableString, int? nullableInt, bool? nullableBoolean, StringFixedEnum? nullableEnum, DateTimeOffset? nullableOn, BinaryData nullableBytes, IList nullableList, IDictionary nullableDictionary, string readOnlyNullable, string requiredNullable, string optionalNonNullable, int? optionalNonNullableInt) : base(inheritedNullable, additionalBinaryDataProperties) + { + _nullableModel = nullableModel; + _nullableString = nullableString; + _nullableInt = nullableInt; + _nullableBoolean = nullableBoolean; + _nullableEnum = nullableEnum; + _nullableOn = nullableOn; + _nullableBytes = nullableBytes; + NullableList = nullableList; + NullableDictionary = nullableDictionary; + _readOnlyNullable = readOnlyNullable; + RequiredNullable = requiredNullable; + OptionalNonNullable = optionalNonNullable; + OptionalNonNullableInt = optionalNonNullableInt; + } + + /// Gets or sets the NullableModel. + public OptionalNullableChild NullableModel + { + get + { + return _nullableModel; + } + set + { + _nullableModel = value; + _nullableModelIsDefined = true; + } + } + + /// Gets or sets the NullableString. + public string NullableString + { + get + { + return _nullableString; + } + set + { + _nullableString = value; + _nullableStringIsDefined = true; + } + } + + /// Gets or sets the NullableInt. + public int? NullableInt + { + get + { + return _nullableInt; + } + set + { + _nullableInt = value; + _nullableIntIsDefined = true; + } + } + + /// Gets or sets the NullableBoolean. + public bool? NullableBoolean + { + get + { + return _nullableBoolean; + } + set + { + _nullableBoolean = value; + _nullableBooleanIsDefined = true; + } + } + + /// Gets or sets the NullableEnum. + public StringFixedEnum? NullableEnum + { + get + { + return _nullableEnum; + } + set + { + _nullableEnum = value; + _nullableEnumIsDefined = true; + } + } + + /// Gets or sets the NullableOn. + public DateTimeOffset? NullableOn + { + get + { + return _nullableOn; + } + set + { + _nullableOn = value; + _nullableOnIsDefined = true; + } + } + + /// + /// Gets or sets the NullableBytes. + /// + /// To assign a byte[] to this property use . + /// The byte[] will be serialized to a Base64 encoded string. + /// + /// + /// Examples: + /// + /// + /// BinaryData.FromBytes(new byte[] { 1, 2, 3 }). + /// Creates a payload of "AQID". + /// + /// + /// + /// + public BinaryData NullableBytes + { + get + { + return _nullableBytes; + } + set + { + _nullableBytes = value; + _nullableBytesIsDefined = true; + } + } + + /// Gets or sets the NullableList. + public IList NullableList { get; set; } + + /// Gets or sets the NullableDictionary. + public IDictionary NullableDictionary { get; set; } + + /// Gets the ReadOnlyNullable. + public string ReadOnlyNullable + { + get + { + return _readOnlyNullable; + } + } + + /// Gets or sets the RequiredNullable. + public string RequiredNullable { get; set; } + + /// Gets or sets the OptionalNonNullable. + public string OptionalNonNullable { get; set; } + + /// Gets or sets the OptionalNonNullableInt. + public int? OptionalNonNullableInt { get; set; } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/SampleTypeSpecContext.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/SampleTypeSpecContext.cs index b58f7f1b7f7..774b66f3ecd 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/SampleTypeSpecContext.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/SampleTypeSpecContext.cs @@ -28,6 +28,12 @@ namespace SampleTypeSpec [ModelReaderWriterBuildable(typeof(ModelWithEmbeddedNonBodyParameters))] [ModelReaderWriterBuildable(typeof(ModelWithRequiredNullableProperties))] [ModelReaderWriterBuildable(typeof(NullableDynamicModel))] + [ModelReaderWriterBuildable(typeof(OptionalNullableBase))] + [ModelReaderWriterBuildable(typeof(OptionalNullableChild))] + [ModelReaderWriterBuildable(typeof(OptionalNullableContainer))] + [ModelReaderWriterBuildable(typeof(OptionalNullableDynamicProperties))] + [ModelReaderWriterBuildable(typeof(OptionalNullableFieldNames))] + [ModelReaderWriterBuildable(typeof(OptionalNullableProperties))] [ModelReaderWriterBuildable(typeof(PageThing))] [ModelReaderWriterBuildable(typeof(Pet))] [ModelReaderWriterBuildable(typeof(Plant))] diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/Thing.Serialization.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/Thing.Serialization.cs index f41b3eebe1d..00a6b231389 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/Thing.Serialization.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/Thing.Serialization.cs @@ -119,10 +119,17 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit { writer.WriteNull("requiredNullableString"u8); } - if (Optional.IsDefined(OptionalNullableString)) + if (_optionalNullableStringIsDefined || Optional.IsDefined(OptionalNullableString)) { - writer.WritePropertyName("optionalNullableString"u8); - writer.WriteStringValue(OptionalNullableString); + if (OptionalNullableString != null) + { + writer.WritePropertyName("optionalNullableString"u8); + writer.WriteStringValue(OptionalNullableString); + } + else + { + writer.WriteNull("optionalNullableString"u8); + } } writer.WritePropertyName("requiredLiteralInt"u8); writer.WriteNumberValue(RequiredLiteralInt); @@ -163,13 +170,20 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit writer.WriteStringValue(RequiredBadDescription); if (Optional.IsCollectionDefined(OptionalNullableList)) { - writer.WritePropertyName("optionalNullableList"u8); - writer.WriteStartArray(); - foreach (int item in OptionalNullableList) + if (OptionalNullableList != null) { - writer.WriteNumberValue(item); + writer.WritePropertyName("optionalNullableList"u8); + writer.WriteStartArray(); + foreach (int item in OptionalNullableList) + { + writer.WriteNumberValue(item); + } + writer.WriteEndArray(); + } + else + { + writer.WriteNull("optionalNullableList"u8); } - writer.WriteEndArray(); } if (Optional.IsCollectionDefined(RequiredNullableList)) { @@ -233,6 +247,7 @@ internal static Thing DeserializeThing(JsonElement element, ModelReaderWriterOpt BinaryData requiredUnion = default; string requiredLiteralString = default; string requiredNullableString = default; + bool optionalNullableStringIsDefined = false; string optionalNullableString = default; int requiredLiteralInt = default; float requiredLiteralFloat = default; @@ -243,7 +258,7 @@ internal static Thing DeserializeThing(JsonElement element, ModelReaderWriterOpt ThingOptionalLiteralFloat? optionalLiteralFloat = default; bool? optionalLiteralBool = default; string requiredBadDescription = default; - IList optionalNullableList = default; + IList optionalNullableList = new ChangeTrackingList(); IList requiredNullableList = default; string propertyWithSpecialDocs = default; IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); @@ -276,6 +291,7 @@ internal static Thing DeserializeThing(JsonElement element, ModelReaderWriterOpt } if (prop.NameEquals("optionalNullableString"u8)) { + optionalNullableStringIsDefined = true; if (prop.Value.ValueKind == JsonValueKind.Null) { optionalNullableString = null; @@ -354,6 +370,7 @@ internal static Thing DeserializeThing(JsonElement element, ModelReaderWriterOpt { if (prop.Value.ValueKind == JsonValueKind.Null) { + optionalNullableList = null; continue; } List array = new List(); @@ -404,10 +421,13 @@ internal static Thing DeserializeThing(JsonElement element, ModelReaderWriterOpt optionalLiteralFloat, optionalLiteralBool, requiredBadDescription, - optionalNullableList ?? new ChangeTrackingList(), + optionalNullableList, requiredNullableList, propertyWithSpecialDocs, - additionalBinaryDataProperties); + additionalBinaryDataProperties) + { + _optionalNullableStringIsDefined = optionalNullableStringIsDefined + }; } } } diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/Thing.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/Thing.cs index 96618c816b8..b9703496aba 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/Thing.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Models/Thing.cs @@ -17,6 +17,8 @@ public partial class Thing { /// Keeps track of any properties unknown to the library. private protected readonly IDictionary _additionalBinaryDataProperties; + private string _optionalNullableString; + internal bool _optionalNullableStringIsDefined; /// Initializes a new instance of . /// name of the Thing. @@ -75,7 +77,7 @@ internal Thing(string rename, BinaryData requiredUnion, string requiredLiteralSt RequiredUnion = requiredUnion; RequiredLiteralString = requiredLiteralString; RequiredNullableString = requiredNullableString; - OptionalNullableString = optionalNullableString; + _optionalNullableString = optionalNullableString; RequiredLiteralInt = requiredLiteralInt; RequiredLiteralFloat = requiredLiteralFloat; RequiredLiteralBool = requiredLiteralBool; @@ -142,7 +144,18 @@ internal Thing(string rename, BinaryData requiredUnion, string requiredLiteralSt public string RequiredNullableString { get; set; } /// required optional string. - public string OptionalNullableString { get; set; } + public string OptionalNullableString + { + get + { + return _optionalNullableString; + } + set + { + _optionalNullableString = value; + _optionalNullableStringIsDefined = true; + } + } /// required literal int. public int RequiredLiteralInt { get; } = 123; diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/SampleTypeSpecModelFactory.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/SampleTypeSpecModelFactory.cs index b51746632cb..77697edee74 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/SampleTypeSpecModelFactory.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/SampleTypeSpecModelFactory.cs @@ -538,5 +538,88 @@ public static NullableDynamicModel NullableDynamicModel(AnotherDynamicModel mode listOfDictionaries.ToList(), default); } + + /// The OptionalNullableDynamicProperties. + /// + /// A new instance for mocking. + public static OptionalNullableDynamicProperties OptionalNullableDynamicProperties(string inheritedNullable = default) + { + return new OptionalNullableDynamicProperties(inheritedNullable, default); + } + + /// The OptionalNullableBase. + /// + /// A new instance for mocking. + public static OptionalNullableBase OptionalNullableBase(string inheritedNullable = default) + { + return new OptionalNullableBase(inheritedNullable, additionalBinaryDataProperties: null); + } + + /// The OptionalNullableChild. + /// + /// A new instance for mocking. + public static OptionalNullableChild OptionalNullableChild(string value = default) + { + return new OptionalNullableChild(value, additionalBinaryDataProperties: null); + } + + /// The OptionalNullableProperties. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// A new instance for mocking. + public static OptionalNullableProperties OptionalNullableProperties(string inheritedNullable = default, OptionalNullableChild nullableModel = default, string nullableString = default, int? nullableInt = default, bool? nullableBoolean = default, StringFixedEnum? nullableEnum = default, DateTimeOffset? nullableOn = default, BinaryData nullableBytes = default, IEnumerable nullableList = default, IDictionary nullableDictionary = default, string readOnlyNullable = default, string requiredNullable = default, string optionalNonNullable = default, int? optionalNonNullableInt = default) + { + nullableList ??= new ChangeTrackingList(); + nullableDictionary ??= new ChangeTrackingDictionary(); + + return new OptionalNullableProperties( + inheritedNullable, + additionalBinaryDataProperties: null, + nullableModel, + nullableString, + nullableInt, + nullableBoolean, + nullableEnum, + nullableOn, + nullableBytes, + nullableList.ToList(), + nullableDictionary, + readOnlyNullable, + requiredNullable, + optionalNonNullable, + optionalNonNullableInt); + } + + /// The OptionalNullableContainer. + /// + /// A new instance for mocking. + public static OptionalNullableContainer OptionalNullableContainer(OptionalNullableProperties child = default) + { + return new OptionalNullableContainer(child, additionalBinaryDataProperties: null); + } + + /// The OptionalNullableFieldNames. + /// + /// + /// + /// A new instance for mocking. + public static OptionalNullableFieldNames OptionalNullableFieldNames(string additionalStringProperties = default, string additionalStringPropertiesIsDefined = default, IDictionary additionalProperties = default) + { + additionalProperties ??= new ChangeTrackingDictionary(); + + return new OptionalNullableFieldNames(additionalStringProperties, additionalStringPropertiesIsDefined, additionalProperties, additionalBinaryDataProperties: null); + } } } diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/tspCodeModel.json b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/tspCodeModel.json index c9dfa4e0ea7..2e40dab2e55 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/tspCodeModel.json +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/tspCodeModel.json @@ -9078,6 +9078,796 @@ { "$id": "636", "kind": "model", + "name": "OptionalNullableDynamicProperties", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "namespace": "SampleTypeSpec", + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableDynamicProperties", + "access": "public", + "usage": "Input,Output,Json", + "decorators": [ + { + "name": "TypeSpec.HttpClient.CSharp.@dynamicModel", + "arguments": {} + } + ], + "serializationOptions": { + "json": { + "name": "OptionalNullableDynamicProperties" + } + }, + "isExactName": false, + "baseModel": { + "$id": "637", + "kind": "model", + "name": "OptionalNullableBase", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "namespace": "SampleTypeSpec", + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableBase", + "access": "public", + "usage": "Input,Output,Json", + "decorators": [], + "serializationOptions": { + "json": { + "name": "OptionalNullableBase" + } + }, + "isExactName": false, + "properties": [ + { + "$id": "638", + "kind": "property", + "name": "inheritedNullable", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "inheritedNullable", + "type": { + "$id": "639", + "kind": "nullable", + "type": { + "$id": "640", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableBase.inheritedNullable", + "serializationOptions": { + "json": { + "name": "inheritedNullable" + } + }, + "isHttpMetadata": false, + "isExactName": false + } + ] + }, + "properties": [] + }, + { + "$ref": "637" + }, + { + "$id": "641", + "kind": "model", + "name": "OptionalNullableChild", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "namespace": "SampleTypeSpec", + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableChild", + "access": "public", + "usage": "Input,Output,Json", + "decorators": [], + "serializationOptions": { + "json": { + "name": "OptionalNullableChild" + } + }, + "isExactName": false, + "properties": [ + { + "$id": "642", + "kind": "property", + "name": "value", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "value", + "type": { + "$id": "643", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "optional": false, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableChild.value", + "serializationOptions": { + "json": { + "name": "value" + } + }, + "isHttpMetadata": false, + "isExactName": false + } + ] + }, + { + "$id": "644", + "kind": "model", + "name": "OptionalNullableProperties", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "namespace": "SampleTypeSpec", + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties", + "access": "public", + "usage": "Input,Output,Json", + "decorators": [], + "serializationOptions": { + "json": { + "name": "OptionalNullableProperties" + } + }, + "isExactName": false, + "baseModel": { + "$ref": "637" + }, + "properties": [ + { + "$id": "645", + "kind": "property", + "name": "nullableModel", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "nullableModel", + "type": { + "$id": "646", + "kind": "nullable", + "type": { + "$ref": "641" + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.nullableModel", + "serializationOptions": { + "json": { + "name": "nullableModel" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "647", + "kind": "property", + "name": "nullableString", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "nullableString", + "type": { + "$id": "648", + "kind": "nullable", + "type": { + "$id": "649", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.nullableString", + "serializationOptions": { + "json": { + "name": "nullableString" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "650", + "kind": "property", + "name": "nullableInt", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "nullableInt", + "type": { + "$id": "651", + "kind": "nullable", + "type": { + "$id": "652", + "kind": "int32", + "name": "int32", + "crossLanguageDefinitionId": "TypeSpec.int32", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.nullableInt", + "serializationOptions": { + "json": { + "name": "nullableInt" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "653", + "kind": "property", + "name": "nullableBoolean", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "nullableBoolean", + "type": { + "$id": "654", + "kind": "nullable", + "type": { + "$id": "655", + "kind": "boolean", + "name": "boolean", + "crossLanguageDefinitionId": "TypeSpec.boolean", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.nullableBoolean", + "serializationOptions": { + "json": { + "name": "nullableBoolean" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "656", + "kind": "property", + "name": "nullableEnum", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "nullableEnum", + "type": { + "$id": "657", + "kind": "nullable", + "type": { + "$ref": "17" + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.nullableEnum", + "serializationOptions": { + "json": { + "name": "nullableEnum" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "658", + "kind": "property", + "name": "nullableDateTime", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "nullableDateTime", + "type": { + "$id": "659", + "kind": "nullable", + "type": { + "$id": "660", + "kind": "utcDateTime", + "name": "utcDateTime", + "encode": "rfc3339", + "wireType": { + "$id": "661", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "crossLanguageDefinitionId": "TypeSpec.utcDateTime", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.nullableDateTime", + "serializationOptions": { + "json": { + "name": "nullableDateTime" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "662", + "kind": "property", + "name": "nullableBytes", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "nullableBytes", + "type": { + "$id": "663", + "kind": "nullable", + "type": { + "$id": "664", + "kind": "bytes", + "name": "bytes", + "encode": "base64", + "crossLanguageDefinitionId": "TypeSpec.bytes", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.nullableBytes", + "serializationOptions": { + "json": { + "name": "nullableBytes" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "665", + "kind": "property", + "name": "nullableList", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "nullableList", + "type": { + "$id": "666", + "kind": "nullable", + "type": { + "$id": "667", + "kind": "array", + "name": "Array7", + "valueType": { + "$id": "668", + "kind": "nullable", + "type": { + "$id": "669", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "crossLanguageDefinitionId": "TypeSpec.Array", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.nullableList", + "serializationOptions": { + "json": { + "name": "nullableList" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "670", + "kind": "property", + "name": "nullableDictionary", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "nullableDictionary", + "type": { + "$id": "671", + "kind": "nullable", + "type": { + "$id": "672", + "kind": "dict", + "keyType": { + "$id": "673", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "valueType": { + "$id": "674", + "kind": "nullable", + "type": { + "$id": "675", + "kind": "int32", + "name": "int32", + "crossLanguageDefinitionId": "TypeSpec.int32", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.nullableDictionary", + "serializationOptions": { + "json": { + "name": "nullableDictionary" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "676", + "kind": "property", + "name": "readOnlyNullable", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "readOnlyNullable", + "type": { + "$id": "677", + "kind": "nullable", + "type": { + "$id": "678", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": true, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.readOnlyNullable", + "serializationOptions": { + "json": { + "name": "readOnlyNullable" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "679", + "kind": "property", + "name": "requiredNullable", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "requiredNullable", + "type": { + "$id": "680", + "kind": "nullable", + "type": { + "$id": "681", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "optional": false, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.requiredNullable", + "serializationOptions": { + "json": { + "name": "requiredNullable" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "682", + "kind": "property", + "name": "optionalNonNullable", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "optionalNonNullable", + "type": { + "$id": "683", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.optionalNonNullable", + "serializationOptions": { + "json": { + "name": "optionalNonNullable" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "684", + "kind": "property", + "name": "optionalNonNullableInt", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "optionalNonNullableInt", + "type": { + "$id": "685", + "kind": "int32", + "name": "int32", + "crossLanguageDefinitionId": "TypeSpec.int32", + "decorators": [] + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableProperties.optionalNonNullableInt", + "serializationOptions": { + "json": { + "name": "optionalNonNullableInt" + } + }, + "isHttpMetadata": false, + "isExactName": false + } + ] + }, + { + "$id": "686", + "kind": "model", + "name": "OptionalNullableContainer", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "namespace": "SampleTypeSpec", + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableContainer", + "access": "public", + "usage": "Input,Output,Json", + "decorators": [], + "serializationOptions": { + "json": { + "name": "OptionalNullableContainer" + } + }, + "isExactName": false, + "properties": [ + { + "$id": "687", + "kind": "property", + "name": "child", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "child", + "type": { + "$ref": "644" + }, + "optional": false, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableContainer.child", + "serializationOptions": { + "json": { + "name": "child" + } + }, + "isHttpMetadata": false, + "isExactName": false + } + ] + }, + { + "$id": "688", + "kind": "model", + "name": "OptionalNullableFieldNames", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "namespace": "SampleTypeSpec", + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableFieldNames", + "access": "public", + "usage": "Input,Output,Json", + "decorators": [], + "serializationOptions": { + "json": { + "name": "OptionalNullableFieldNames" + } + }, + "isExactName": false, + "additionalProperties": { + "$id": "689", + "kind": "nullable", + "type": { + "$id": "690", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "properties": [ + { + "$id": "691", + "kind": "property", + "name": "additionalStringProperties", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "additionalStringProperties", + "type": { + "$id": "692", + "kind": "nullable", + "type": { + "$id": "693", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableFieldNames.additionalStringProperties", + "serializationOptions": { + "json": { + "name": "additionalStringProperties" + } + }, + "isHttpMetadata": false, + "isExactName": false + }, + { + "$id": "694", + "kind": "property", + "name": "additionalStringPropertiesIsDefined", + "apiVersions": [ + "2024-07-16-preview", + "2024-08-16-preview" + ], + "serializedName": "additionalStringPropertiesIsDefined", + "type": { + "$id": "695", + "kind": "nullable", + "type": { + "$id": "696", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "namespace": "SampleTypeSpec" + }, + "optional": true, + "readOnly": false, + "discriminator": false, + "flatten": false, + "decorators": [], + "crossLanguageDefinitionId": "SampleTypeSpec.OptionalNullableFieldNames.additionalStringPropertiesIsDefined", + "serializationOptions": { + "json": { + "name": "additionalStringPropertiesIsDefined" + } + }, + "isHttpMetadata": false, + "isExactName": false + } + ] + }, + { + "$id": "697", + "kind": "model", "name": "JsonlStreamStreamingItem", "apiVersions": [], "namespace": "TypeSpec.Http.Streams", @@ -9089,7 +9879,7 @@ "isExactName": false, "properties": [ { - "$id": "637", + "$id": "698", "kind": "property", "name": "contentType", "apiVersions": [ @@ -9110,7 +9900,7 @@ "isExactName": false }, { - "$id": "638", + "$id": "699", "kind": "property", "name": "body", "apiVersions": [ @@ -9118,7 +9908,7 @@ "2024-08-16-preview" ], "type": { - "$id": "639", + "$id": "700", "kind": "bytes", "name": "bytes", "crossLanguageDefinitionId": "", @@ -9139,7 +9929,7 @@ ], "clients": [ { - "$id": "640", + "$id": "701", "kind": "client", "name": "SampleTypeSpecClient", "isExactName": false, @@ -9147,7 +9937,7 @@ "doc": "This is a sample typespec project.", "methods": [ { - "$id": "641", + "$id": "702", "kind": "basic", "name": "sayHi", "isExactName": false, @@ -9158,7 +9948,7 @@ ], "doc": "Return hi", "operation": { - "$id": "642", + "$id": "703", "name": "sayHi", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -9166,12 +9956,12 @@ "accessibility": "public", "parameters": [ { - "$id": "643", + "$id": "704", "kind": "header", "name": "headParameter", "serializedName": "head-parameter", "type": { - "$id": "644", + "$id": "705", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9186,12 +9976,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.sayHi.headParameter", "methodParameterSegments": [ { - "$id": "645", + "$id": "706", "kind": "method", "name": "headParameter", "serializedName": "head-parameter", "type": { - "$id": "646", + "$id": "707", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9211,12 +10001,12 @@ "isExactName": false }, { - "$id": "647", + "$id": "708", "kind": "query", "name": "queryParameter", "serializedName": "queryParameter", "type": { - "$id": "648", + "$id": "709", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9231,12 +10021,12 @@ "readOnly": false, "methodParameterSegments": [ { - "$id": "649", + "$id": "710", "kind": "method", "name": "queryParameter", "serializedName": "queryParameter", "type": { - "$id": "650", + "$id": "711", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9256,12 +10046,12 @@ "isExactName": false }, { - "$id": "651", + "$id": "712", "kind": "query", "name": "optionalQuery", "serializedName": "optionalQuery", "type": { - "$id": "652", + "$id": "713", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9276,12 +10066,12 @@ "readOnly": false, "methodParameterSegments": [ { - "$id": "653", + "$id": "714", "kind": "method", "name": "optionalQuery", "serializedName": "optionalQuery", "type": { - "$id": "654", + "$id": "715", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9301,7 +10091,7 @@ "isExactName": false }, { - "$id": "655", + "$id": "716", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -9317,7 +10107,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.sayHi.accept", "methodParameterSegments": [ { - "$id": "656", + "$id": "717", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -9370,16 +10160,16 @@ }, "parameters": [ { - "$ref": "645" + "$ref": "706" }, { - "$ref": "649" + "$ref": "710" }, { - "$ref": "653" + "$ref": "714" }, { - "$ref": "656" + "$ref": "717" } ], "response": { @@ -9393,7 +10183,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.sayHi" }, { - "$id": "657", + "$id": "718", "kind": "basic", "name": "helloAgain", "isExactName": false, @@ -9404,7 +10194,7 @@ ], "doc": "Return hi again", "operation": { - "$id": "658", + "$id": "719", "name": "helloAgain", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -9412,12 +10202,12 @@ "accessibility": "public", "parameters": [ { - "$id": "659", + "$id": "720", "kind": "header", "name": "p1", "serializedName": "p1", "type": { - "$id": "660", + "$id": "721", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9432,12 +10222,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloAgain.p1", "methodParameterSegments": [ { - "$id": "661", + "$id": "722", "kind": "method", "name": "p1", "serializedName": "p1", "type": { - "$id": "662", + "$id": "723", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9457,7 +10247,7 @@ "isExactName": false }, { - "$id": "663", + "$id": "724", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -9473,7 +10263,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloAgain.contentType", "methodParameterSegments": [ { - "$id": "664", + "$id": "725", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -9494,12 +10284,12 @@ "isExactName": false }, { - "$id": "665", + "$id": "726", "kind": "path", "name": "p2", "serializedName": "p2", "type": { - "$id": "666", + "$id": "727", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9517,12 +10307,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloAgain.p2", "methodParameterSegments": [ { - "$id": "667", + "$id": "728", "kind": "method", "name": "p2", "serializedName": "p2", "type": { - "$id": "668", + "$id": "729", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9542,7 +10332,7 @@ "isExactName": false }, { - "$id": "669", + "$id": "730", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -9558,7 +10348,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloAgain.accept", "methodParameterSegments": [ { - "$id": "670", + "$id": "731", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -9579,7 +10369,7 @@ "isExactName": false }, { - "$id": "671", + "$id": "732", "kind": "body", "name": "action", "serializedName": "action", @@ -9598,7 +10388,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloAgain.action", "methodParameterSegments": [ { - "$id": "672", + "$id": "733", "kind": "method", "name": "action", "serializedName": "action", @@ -9655,19 +10445,19 @@ }, "parameters": [ { - "$ref": "661" + "$ref": "722" }, { - "$ref": "672" + "$ref": "733" }, { - "$ref": "664" + "$ref": "725" }, { - "$ref": "667" + "$ref": "728" }, { - "$ref": "670" + "$ref": "731" } ], "response": { @@ -9681,7 +10471,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloAgain" }, { - "$id": "673", + "$id": "734", "kind": "basic", "name": "noContentType", "isExactName": false, @@ -9692,7 +10482,7 @@ ], "doc": "Return hi again", "operation": { - "$id": "674", + "$id": "735", "name": "noContentType", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -9700,12 +10490,12 @@ "accessibility": "public", "parameters": [ { - "$id": "675", + "$id": "736", "kind": "header", "name": "p1", "serializedName": "p1", "type": { - "$id": "676", + "$id": "737", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9720,7 +10510,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.noContentType.p1", "methodParameterSegments": [ { - "$id": "677", + "$id": "738", "kind": "method", "name": "info", "serializedName": "info", @@ -9738,7 +10528,7 @@ "isExactName": false }, { - "$id": "678", + "$id": "739", "kind": "method", "name": "p1", "serializedName": "p1", @@ -9760,12 +10550,12 @@ "isExactName": false }, { - "$id": "679", + "$id": "740", "kind": "path", "name": "p2", "serializedName": "p2", "type": { - "$id": "680", + "$id": "741", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9783,10 +10573,10 @@ "crossLanguageDefinitionId": "SampleTypeSpec.noContentType.p2", "methodParameterSegments": [ { - "$ref": "677" + "$ref": "738" }, { - "$id": "681", + "$id": "742", "kind": "method", "name": "p2", "serializedName": "p2", @@ -9808,7 +10598,7 @@ "isExactName": false }, { - "$id": "682", + "$id": "743", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -9825,7 +10615,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.noContentType.contentType", "methodParameterSegments": [ { - "$id": "683", + "$id": "744", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -9847,7 +10637,7 @@ "isExactName": false }, { - "$id": "684", + "$id": "745", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -9863,7 +10653,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.noContentType.accept", "methodParameterSegments": [ { - "$id": "685", + "$id": "746", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -9884,7 +10674,7 @@ "isExactName": false }, { - "$id": "686", + "$id": "747", "kind": "body", "name": "action", "serializedName": "action", @@ -9903,10 +10693,10 @@ "crossLanguageDefinitionId": "SampleTypeSpec.noContentType.action", "methodParameterSegments": [ { - "$ref": "677" + "$ref": "738" }, { - "$id": "687", + "$id": "748", "kind": "method", "name": "action", "serializedName": "action", @@ -9968,13 +10758,13 @@ }, "parameters": [ { - "$ref": "677" + "$ref": "738" }, { - "$ref": "683" + "$ref": "744" }, { - "$ref": "685" + "$ref": "746" } ], "response": { @@ -9988,7 +10778,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.noContentType" }, { - "$id": "688", + "$id": "749", "kind": "basic", "name": "helloDemo2", "isExactName": false, @@ -9999,7 +10789,7 @@ ], "doc": "Return hi in demo2", "operation": { - "$id": "689", + "$id": "750", "name": "helloDemo2", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -10007,7 +10797,7 @@ "accessibility": "public", "parameters": [ { - "$id": "690", + "$id": "751", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -10023,7 +10813,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloDemo2.accept", "methodParameterSegments": [ { - "$id": "691", + "$id": "752", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -10076,7 +10866,7 @@ }, "parameters": [ { - "$ref": "691" + "$ref": "752" } ], "response": { @@ -10090,7 +10880,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloDemo2" }, { - "$id": "692", + "$id": "753", "kind": "basic", "name": "createLiteral", "isExactName": false, @@ -10101,7 +10891,7 @@ ], "doc": "Create with literal value", "operation": { - "$id": "693", + "$id": "754", "name": "createLiteral", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -10109,7 +10899,7 @@ "accessibility": "public", "parameters": [ { - "$id": "694", + "$id": "755", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -10126,7 +10916,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.createLiteral.contentType", "methodParameterSegments": [ { - "$id": "695", + "$id": "756", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -10148,7 +10938,7 @@ "isExactName": false }, { - "$id": "696", + "$id": "757", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -10164,7 +10954,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.createLiteral.accept", "methodParameterSegments": [ { - "$id": "697", + "$id": "758", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -10185,7 +10975,7 @@ "isExactName": false }, { - "$id": "698", + "$id": "759", "kind": "body", "name": "body", "serializedName": "body", @@ -10204,7 +10994,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.createLiteral.body", "methodParameterSegments": [ { - "$id": "699", + "$id": "760", "kind": "method", "name": "body", "serializedName": "body", @@ -10265,13 +11055,13 @@ }, "parameters": [ { - "$ref": "699" + "$ref": "760" }, { - "$ref": "695" + "$ref": "756" }, { - "$ref": "697" + "$ref": "758" } ], "response": { @@ -10285,7 +11075,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.createLiteral" }, { - "$id": "700", + "$id": "761", "kind": "basic", "name": "helloLiteral", "isExactName": false, @@ -10296,7 +11086,7 @@ ], "doc": "Send literal parameters", "operation": { - "$id": "701", + "$id": "762", "name": "helloLiteral", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -10304,7 +11094,7 @@ "accessibility": "public", "parameters": [ { - "$id": "702", + "$id": "763", "kind": "header", "name": "p1", "serializedName": "p1", @@ -10320,7 +11110,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloLiteral.p1", "methodParameterSegments": [ { - "$id": "703", + "$id": "764", "kind": "method", "name": "p1", "serializedName": "p1", @@ -10341,7 +11131,7 @@ "isExactName": false }, { - "$id": "704", + "$id": "765", "kind": "path", "name": "p2", "serializedName": "p2", @@ -10360,7 +11150,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloLiteral.p2", "methodParameterSegments": [ { - "$id": "705", + "$id": "766", "kind": "method", "name": "p2", "serializedName": "p2", @@ -10381,7 +11171,7 @@ "isExactName": false }, { - "$id": "706", + "$id": "767", "kind": "query", "name": "p3", "serializedName": "p3", @@ -10397,7 +11187,7 @@ "readOnly": false, "methodParameterSegments": [ { - "$id": "707", + "$id": "768", "kind": "method", "name": "p3", "serializedName": "p3", @@ -10418,7 +11208,7 @@ "isExactName": false }, { - "$id": "708", + "$id": "769", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -10434,7 +11224,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloLiteral.accept", "methodParameterSegments": [ { - "$id": "709", + "$id": "770", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -10487,16 +11277,16 @@ }, "parameters": [ { - "$ref": "703" + "$ref": "764" }, { - "$ref": "705" + "$ref": "766" }, { - "$ref": "707" + "$ref": "768" }, { - "$ref": "709" + "$ref": "770" } ], "response": { @@ -10510,7 +11300,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloLiteral" }, { - "$id": "710", + "$id": "771", "kind": "basic", "name": "topAction", "isExactName": false, @@ -10521,7 +11311,7 @@ ], "doc": "top level method", "operation": { - "$id": "711", + "$id": "772", "name": "topAction", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -10529,17 +11319,17 @@ "accessibility": "public", "parameters": [ { - "$id": "712", + "$id": "773", "kind": "path", "name": "action", "serializedName": "action", "type": { - "$id": "713", + "$id": "774", "kind": "utcDateTime", "name": "utcDateTime", "encode": "rfc3339", "wireType": { - "$id": "714", + "$id": "775", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -10560,17 +11350,17 @@ "crossLanguageDefinitionId": "SampleTypeSpec.topAction.action", "methodParameterSegments": [ { - "$id": "715", + "$id": "776", "kind": "method", "name": "action", "serializedName": "action", "type": { - "$id": "716", + "$id": "777", "kind": "utcDateTime", "name": "utcDateTime", "encode": "rfc3339", "wireType": { - "$id": "717", + "$id": "778", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -10593,7 +11383,7 @@ "isExactName": false }, { - "$id": "718", + "$id": "779", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -10609,7 +11399,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.topAction.accept", "methodParameterSegments": [ { - "$id": "719", + "$id": "780", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -10662,10 +11452,10 @@ }, "parameters": [ { - "$ref": "715" + "$ref": "776" }, { - "$ref": "719" + "$ref": "780" } ], "response": { @@ -10679,7 +11469,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.topAction" }, { - "$id": "720", + "$id": "781", "kind": "basic", "name": "topAction2", "isExactName": false, @@ -10690,7 +11480,7 @@ ], "doc": "top level method2", "operation": { - "$id": "721", + "$id": "782", "name": "topAction2", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -10698,7 +11488,7 @@ "accessibility": "public", "parameters": [ { - "$id": "722", + "$id": "783", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -10714,7 +11504,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.topAction2.accept", "methodParameterSegments": [ { - "$id": "723", + "$id": "784", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -10767,7 +11557,7 @@ }, "parameters": [ { - "$ref": "723" + "$ref": "784" } ], "response": { @@ -10781,7 +11571,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.topAction2" }, { - "$id": "724", + "$id": "785", "kind": "basic", "name": "patchAction", "isExactName": false, @@ -10792,7 +11582,7 @@ ], "doc": "top level patch", "operation": { - "$id": "725", + "$id": "786", "name": "patchAction", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -10800,7 +11590,7 @@ "accessibility": "public", "parameters": [ { - "$id": "726", + "$id": "787", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -10817,7 +11607,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.patchAction.contentType", "methodParameterSegments": [ { - "$id": "727", + "$id": "788", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -10839,7 +11629,7 @@ "isExactName": false }, { - "$id": "728", + "$id": "789", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -10855,7 +11645,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.patchAction.accept", "methodParameterSegments": [ { - "$id": "729", + "$id": "790", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -10876,7 +11666,7 @@ "isExactName": false }, { - "$id": "730", + "$id": "791", "kind": "body", "name": "body", "serializedName": "body", @@ -10895,7 +11685,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.patchAction.body", "methodParameterSegments": [ { - "$id": "731", + "$id": "792", "kind": "method", "name": "body", "serializedName": "body", @@ -10956,13 +11746,13 @@ }, "parameters": [ { - "$ref": "731" + "$ref": "792" }, { - "$ref": "727" + "$ref": "788" }, { - "$ref": "729" + "$ref": "790" } ], "response": { @@ -10976,7 +11766,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.patchAction" }, { - "$id": "732", + "$id": "793", "kind": "basic", "name": "anonymousBody", "isExactName": false, @@ -10987,7 +11777,7 @@ ], "doc": "body parameter without body decorator", "operation": { - "$id": "733", + "$id": "794", "name": "anonymousBody", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -10995,7 +11785,7 @@ "accessibility": "public", "parameters": [ { - "$id": "734", + "$id": "795", "kind": "query", "name": "requiredQueryParam", "serializedName": "requiredQueryParam", @@ -11011,7 +11801,7 @@ "readOnly": false, "methodParameterSegments": [ { - "$id": "735", + "$id": "796", "kind": "method", "name": "requiredQueryParam", "serializedName": "requiredQueryParam", @@ -11032,7 +11822,7 @@ "isExactName": false }, { - "$id": "736", + "$id": "797", "kind": "header", "name": "requiredHeader", "serializedName": "required-header", @@ -11048,7 +11838,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.anonymousBody.requiredHeader", "methodParameterSegments": [ { - "$id": "737", + "$id": "798", "kind": "method", "name": "requiredHeader", "serializedName": "required-header", @@ -11069,7 +11859,7 @@ "isExactName": false }, { - "$id": "738", + "$id": "799", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -11086,7 +11876,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.anonymousBody.contentType", "methodParameterSegments": [ { - "$id": "739", + "$id": "800", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -11108,7 +11898,7 @@ "isExactName": false }, { - "$id": "740", + "$id": "801", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -11124,7 +11914,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.anonymousBody.accept", "methodParameterSegments": [ { - "$id": "741", + "$id": "802", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -11145,7 +11935,7 @@ "isExactName": false }, { - "$id": "742", + "$id": "803", "kind": "body", "name": "thing", "serializedName": "thing", @@ -11164,13 +11954,13 @@ "crossLanguageDefinitionId": "SampleTypeSpec.anonymousBody.body", "methodParameterSegments": [ { - "$id": "743", + "$id": "804", "kind": "method", "name": "name", "serializedName": "name", "doc": "name of the Thing", "type": { - "$id": "744", + "$id": "805", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -11230,10 +12020,10 @@ }, "parameters": [ { - "$ref": "743" + "$ref": "804" }, { - "$id": "745", + "$id": "806", "kind": "method", "name": "requiredUnion", "serializedName": "requiredUnion", @@ -11252,7 +12042,7 @@ "isExactName": false }, { - "$id": "746", + "$id": "807", "kind": "method", "name": "requiredLiteralString", "serializedName": "requiredLiteralString", @@ -11271,7 +12061,7 @@ "isExactName": false }, { - "$id": "747", + "$id": "808", "kind": "method", "name": "requiredNullableString", "serializedName": "requiredNullableString", @@ -11290,7 +12080,7 @@ "isExactName": false }, { - "$id": "748", + "$id": "809", "kind": "method", "name": "optionalNullableString", "serializedName": "optionalNullableString", @@ -11309,7 +12099,7 @@ "isExactName": false }, { - "$id": "749", + "$id": "810", "kind": "method", "name": "requiredLiteralInt", "serializedName": "requiredLiteralInt", @@ -11328,7 +12118,7 @@ "isExactName": false }, { - "$id": "750", + "$id": "811", "kind": "method", "name": "requiredLiteralFloat", "serializedName": "requiredLiteralFloat", @@ -11347,7 +12137,7 @@ "isExactName": false }, { - "$id": "751", + "$id": "812", "kind": "method", "name": "requiredLiteralBool", "serializedName": "requiredLiteralBool", @@ -11366,19 +12156,19 @@ "isExactName": false }, { - "$id": "752", + "$id": "813", "kind": "method", "name": "optionalLiteralString", "serializedName": "optionalLiteralString", "doc": "optional literal string", "type": { - "$id": "753", + "$id": "814", "kind": "enum", "name": "ThingOptionalLiteralString", "apiVersions": [], "crossLanguageDefinitionId": "", "valueType": { - "$id": "754", + "$id": "815", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -11386,12 +12176,12 @@ }, "values": [ { - "$id": "755", + "$id": "816", "kind": "enumvalue", "name": "reject", "value": "reject", "valueType": { - "$id": "756", + "$id": "817", "kind": "string", "decorators": [], "doc": "A sequence of textual characters.", @@ -11399,7 +12189,7 @@ "crossLanguageDefinitionId": "TypeSpec.string" }, "enumType": { - "$ref": "753" + "$ref": "814" }, "decorators": [], "isExactName": false @@ -11423,7 +12213,7 @@ "isExactName": false }, { - "$id": "757", + "$id": "818", "kind": "method", "name": "requiredNullableLiteralString", "serializedName": "requiredNullableLiteralString", @@ -11442,19 +12232,19 @@ "isExactName": false }, { - "$id": "758", + "$id": "819", "kind": "method", "name": "optionalLiteralInt", "serializedName": "optionalLiteralInt", "doc": "optional literal int", "type": { - "$id": "759", + "$id": "820", "kind": "enum", "name": "ThingOptionalLiteralInt", "apiVersions": [], "crossLanguageDefinitionId": "", "valueType": { - "$id": "760", + "$id": "821", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -11462,12 +12252,12 @@ }, "values": [ { - "$id": "761", + "$id": "822", "kind": "enumvalue", "name": "456", "value": 456, "valueType": { - "$id": "762", + "$id": "823", "kind": "int32", "decorators": [], "doc": "A 32-bit integer. (`-2,147,483,648` to `2,147,483,647`)", @@ -11475,7 +12265,7 @@ "crossLanguageDefinitionId": "TypeSpec.int32" }, "enumType": { - "$ref": "759" + "$ref": "820" }, "decorators": [], "isExactName": false @@ -11499,19 +12289,19 @@ "isExactName": false }, { - "$id": "763", + "$id": "824", "kind": "method", "name": "optionalLiteralFloat", "serializedName": "optionalLiteralFloat", "doc": "optional literal float", "type": { - "$id": "764", + "$id": "825", "kind": "enum", "name": "ThingOptionalLiteralFloat", "apiVersions": [], "crossLanguageDefinitionId": "", "valueType": { - "$id": "765", + "$id": "826", "kind": "float32", "name": "float32", "crossLanguageDefinitionId": "TypeSpec.float32", @@ -11519,12 +12309,12 @@ }, "values": [ { - "$id": "766", + "$id": "827", "kind": "enumvalue", "name": "4.56", "value": 4.56, "valueType": { - "$id": "767", + "$id": "828", "kind": "float32", "decorators": [], "doc": "A 32 bit floating point number. (`±1.5 x 10^−45` to `±3.4 x 10^38`)", @@ -11532,7 +12322,7 @@ "crossLanguageDefinitionId": "TypeSpec.float32" }, "enumType": { - "$ref": "764" + "$ref": "825" }, "decorators": [], "isExactName": false @@ -11556,7 +12346,7 @@ "isExactName": false }, { - "$id": "768", + "$id": "829", "kind": "method", "name": "optionalLiteralBool", "serializedName": "optionalLiteralBool", @@ -11575,13 +12365,13 @@ "isExactName": false }, { - "$id": "769", + "$id": "830", "kind": "method", "name": "requiredBadDescription", "serializedName": "requiredBadDescription", "doc": "description with xml <|endoftext|>", "type": { - "$id": "770", + "$id": "831", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -11598,7 +12388,7 @@ "isExactName": false }, { - "$id": "771", + "$id": "832", "kind": "method", "name": "optionalNullableList", "serializedName": "optionalNullableList", @@ -11617,7 +12407,7 @@ "isExactName": false }, { - "$id": "772", + "$id": "833", "kind": "method", "name": "requiredNullableList", "serializedName": "requiredNullableList", @@ -11636,13 +12426,13 @@ "isExactName": false }, { - "$id": "773", + "$id": "834", "kind": "method", "name": "propertyWithSpecialDocs", "serializedName": "propertyWithSpecialDocs", "doc": "This tests:\n- Simple bullet point. This bullet point is going to be very long to test how text wrapping is handled in bullet points within documentation comments. It should properly indent the wrapped lines.\n- Another bullet point with **bold text**. This bullet point is also intentionally long to see how the formatting is preserved when the text wraps onto multiple lines in the generated documentation.\n- Third bullet point with *italic text*. Similar to the previous points, this one is extended to ensure that the wrapping and formatting are correctly applied in the output.\n- Complex bullet point with **bold** and *italic* combined. This bullet point combines both bold and italic formatting and is long enough to test the wrapping behavior in such cases.\n- **Bold bullet point**: A bullet point that is entirely bolded. This point is also made lengthy to observe how the bold formatting is maintained across wrapped lines.\n- *Italic bullet point*: A bullet point that is entirely italicized. This final point is extended to verify that italic formatting is correctly applied even when the text spans multiple lines.", "type": { - "$id": "774", + "$id": "835", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -11659,16 +12449,16 @@ "isExactName": false }, { - "$ref": "735" + "$ref": "796" }, { - "$ref": "737" + "$ref": "798" }, { - "$ref": "739" + "$ref": "800" }, { - "$ref": "741" + "$ref": "802" } ], "response": { @@ -11682,7 +12472,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.anonymousBody" }, { - "$id": "775", + "$id": "836", "kind": "basic", "name": "friendlyModel", "isExactName": false, @@ -11693,7 +12483,7 @@ ], "doc": "Model can have its friendly name", "operation": { - "$id": "776", + "$id": "837", "name": "friendlyModel", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -11701,7 +12491,7 @@ "accessibility": "public", "parameters": [ { - "$id": "777", + "$id": "838", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -11718,7 +12508,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.friendlyModel.contentType", "methodParameterSegments": [ { - "$id": "778", + "$id": "839", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -11740,7 +12530,7 @@ "isExactName": false }, { - "$id": "779", + "$id": "840", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -11756,7 +12546,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.friendlyModel.accept", "methodParameterSegments": [ { - "$id": "780", + "$id": "841", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -11777,7 +12567,7 @@ "isExactName": false }, { - "$id": "781", + "$id": "842", "kind": "body", "name": "friend", "serializedName": "friend", @@ -11796,13 +12586,13 @@ "crossLanguageDefinitionId": "SampleTypeSpec.friendlyModel.body", "methodParameterSegments": [ { - "$id": "782", + "$id": "843", "kind": "method", "name": "name", "serializedName": "name", "doc": "name of the NotFriend", "type": { - "$id": "783", + "$id": "844", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -11862,13 +12652,13 @@ }, "parameters": [ { - "$ref": "782" + "$ref": "843" }, { - "$ref": "778" + "$ref": "839" }, { - "$ref": "780" + "$ref": "841" } ], "response": { @@ -11882,7 +12672,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.friendlyModel" }, { - "$id": "784", + "$id": "845", "kind": "basic", "name": "addTimeHeader", "isExactName": false, @@ -11892,24 +12682,24 @@ "2024-08-16-preview" ], "operation": { - "$id": "785", + "$id": "846", "name": "addTimeHeader", "isExactName": false, "resourceName": "SampleTypeSpec", "accessibility": "public", "parameters": [ { - "$id": "786", + "$id": "847", "kind": "header", "name": "repeatabilityFirstSent", "serializedName": "Repeatability-First-Sent", "type": { - "$id": "787", + "$id": "848", "kind": "utcDateTime", "name": "utcDateTime", "encode": "rfc7231", "wireType": { - "$id": "788", + "$id": "849", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -11927,17 +12717,17 @@ "crossLanguageDefinitionId": "SampleTypeSpec.addTimeHeader.repeatabilityFirstSent", "methodParameterSegments": [ { - "$id": "789", + "$id": "850", "kind": "method", "name": "repeatabilityFirstSent", "serializedName": "Repeatability-First-Sent", "type": { - "$id": "790", + "$id": "851", "kind": "utcDateTime", "name": "utcDateTime", "encode": "rfc7231", "wireType": { - "$id": "791", + "$id": "852", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -11982,7 +12772,7 @@ }, "parameters": [ { - "$ref": "789" + "$ref": "850" } ], "response": {}, @@ -11992,7 +12782,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.addTimeHeader" }, { - "$id": "792", + "$id": "853", "kind": "basic", "name": "projectedNameModel", "isExactName": false, @@ -12003,7 +12793,7 @@ ], "doc": "Model can have its projected name", "operation": { - "$id": "793", + "$id": "854", "name": "projectedNameModel", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -12011,7 +12801,7 @@ "accessibility": "public", "parameters": [ { - "$id": "794", + "$id": "855", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -12028,7 +12818,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.projectedNameModel.contentType", "methodParameterSegments": [ { - "$id": "795", + "$id": "856", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -12050,7 +12840,7 @@ "isExactName": false }, { - "$id": "796", + "$id": "857", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -12066,7 +12856,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.projectedNameModel.accept", "methodParameterSegments": [ { - "$id": "797", + "$id": "858", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -12087,7 +12877,7 @@ "isExactName": false }, { - "$id": "798", + "$id": "859", "kind": "body", "name": "renamedModel", "serializedName": "renamedModel", @@ -12106,13 +12896,13 @@ "crossLanguageDefinitionId": "SampleTypeSpec.projectedNameModel.body", "methodParameterSegments": [ { - "$id": "799", + "$id": "860", "kind": "method", "name": "otherName", "serializedName": "otherName", "doc": "name of the ModelWithClientName", "type": { - "$id": "800", + "$id": "861", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -12172,13 +12962,13 @@ }, "parameters": [ { - "$ref": "799" + "$ref": "860" }, { - "$ref": "795" + "$ref": "856" }, { - "$ref": "797" + "$ref": "858" } ], "response": { @@ -12192,7 +12982,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.projectedNameModel" }, { - "$id": "801", + "$id": "862", "kind": "basic", "name": "returnsAnonymousModel", "isExactName": false, @@ -12203,7 +12993,7 @@ ], "doc": "return anonymous model", "operation": { - "$id": "802", + "$id": "863", "name": "returnsAnonymousModel", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -12211,7 +13001,7 @@ "accessibility": "public", "parameters": [ { - "$id": "803", + "$id": "864", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -12227,7 +13017,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.returnsAnonymousModel.accept", "methodParameterSegments": [ { - "$id": "804", + "$id": "865", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -12280,7 +13070,7 @@ }, "parameters": [ { - "$ref": "804" + "$ref": "865" } ], "response": { @@ -12294,7 +13084,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.returnsAnonymousModel" }, { - "$id": "805", + "$id": "866", "kind": "basic", "name": "getUnknownValue", "isExactName": false, @@ -12305,7 +13095,7 @@ ], "doc": "get extensible enum", "operation": { - "$id": "806", + "$id": "867", "name": "getUnknownValue", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -12313,7 +13103,7 @@ "accessibility": "public", "parameters": [ { - "$id": "807", + "$id": "868", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -12329,7 +13119,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.getUnknownValue.accept", "methodParameterSegments": [ { - "$id": "808", + "$id": "869", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -12378,7 +13168,7 @@ }, "parameters": [ { - "$ref": "808" + "$ref": "869" } ], "response": { @@ -12392,7 +13182,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.getUnknownValue" }, { - "$id": "809", + "$id": "870", "kind": "basic", "name": "internalProtocol", "isExactName": false, @@ -12403,7 +13193,7 @@ ], "doc": "When set protocol false and convenient true, then the protocol method should be internal", "operation": { - "$id": "810", + "$id": "871", "name": "internalProtocol", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -12411,7 +13201,7 @@ "accessibility": "public", "parameters": [ { - "$id": "811", + "$id": "872", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -12428,7 +13218,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.internalProtocol.contentType", "methodParameterSegments": [ { - "$id": "812", + "$id": "873", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -12450,7 +13240,7 @@ "isExactName": false }, { - "$id": "813", + "$id": "874", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -12466,7 +13256,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.internalProtocol.accept", "methodParameterSegments": [ { - "$id": "814", + "$id": "875", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -12487,7 +13277,7 @@ "isExactName": false }, { - "$id": "815", + "$id": "876", "kind": "body", "name": "body", "serializedName": "body", @@ -12506,7 +13296,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.internalProtocol.body", "methodParameterSegments": [ { - "$id": "816", + "$id": "877", "kind": "method", "name": "body", "serializedName": "body", @@ -12567,13 +13357,13 @@ }, "parameters": [ { - "$ref": "816" + "$ref": "877" }, { - "$ref": "812" + "$ref": "873" }, { - "$ref": "814" + "$ref": "875" } ], "response": { @@ -12587,7 +13377,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.internalProtocol" }, { - "$id": "817", + "$id": "878", "kind": "basic", "name": "stillConvenient", "isExactName": false, @@ -12598,7 +13388,7 @@ ], "doc": "When set protocol false and convenient true, the convenient method should be generated even it has the same signature as protocol one", "operation": { - "$id": "818", + "$id": "879", "name": "stillConvenient", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -12633,7 +13423,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.stillConvenient" }, { - "$id": "819", + "$id": "880", "kind": "basic", "name": "headAsBoolean", "isExactName": false, @@ -12644,7 +13434,7 @@ ], "doc": "head as boolean.", "operation": { - "$id": "820", + "$id": "881", "name": "headAsBoolean", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -12652,12 +13442,12 @@ "accessibility": "public", "parameters": [ { - "$id": "821", + "$id": "882", "kind": "path", "name": "id", "serializedName": "id", "type": { - "$id": "822", + "$id": "883", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -12675,12 +13465,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.headAsBoolean.id", "methodParameterSegments": [ { - "$id": "823", + "$id": "884", "kind": "method", "name": "id", "serializedName": "id", "type": { - "$id": "824", + "$id": "885", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -12722,7 +13512,7 @@ }, "parameters": [ { - "$ref": "823" + "$ref": "884" } ], "response": {}, @@ -12732,7 +13522,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.headAsBoolean" }, { - "$id": "825", + "$id": "886", "kind": "basic", "name": "WithApiVersion", "isExactName": false, @@ -12743,7 +13533,7 @@ ], "doc": "Return hi again", "operation": { - "$id": "826", + "$id": "887", "name": "WithApiVersion", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -12751,12 +13541,12 @@ "accessibility": "public", "parameters": [ { - "$id": "827", + "$id": "888", "kind": "header", "name": "p1", "serializedName": "p1", "type": { - "$id": "828", + "$id": "889", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -12771,12 +13561,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.WithApiVersion.p1", "methodParameterSegments": [ { - "$id": "829", + "$id": "890", "kind": "method", "name": "p1", "serializedName": "p1", "type": { - "$id": "830", + "$id": "891", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -12796,12 +13586,12 @@ "isExactName": false }, { - "$id": "831", + "$id": "892", "kind": "query", "name": "apiVersion", "serializedName": "apiVersion", "type": { - "$id": "832", + "$id": "893", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -12811,7 +13601,7 @@ "explode": false, "defaultValue": { "type": { - "$id": "833", + "$id": "894", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string" @@ -12825,12 +13615,12 @@ "readOnly": false, "methodParameterSegments": [ { - "$id": "834", + "$id": "895", "kind": "method", "name": "apiVersion", "serializedName": "apiVersion", "type": { - "$id": "835", + "$id": "896", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -12840,7 +13630,7 @@ "isApiVersion": true, "defaultValue": { "type": { - "$id": "836", + "$id": "897", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string" @@ -12881,7 +13671,7 @@ }, "parameters": [ { - "$ref": "829" + "$ref": "890" } ], "response": {}, @@ -12891,7 +13681,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.WithApiVersion" }, { - "$id": "837", + "$id": "898", "kind": "paging", "name": "ListWithNextLink", "isExactName": false, @@ -12902,7 +13692,7 @@ ], "doc": "List things with nextlink", "operation": { - "$id": "838", + "$id": "899", "name": "ListWithNextLink", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -12910,7 +13700,7 @@ "accessibility": "public", "parameters": [ { - "$id": "839", + "$id": "900", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -12926,7 +13716,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.ListWithNextLink.accept", "methodParameterSegments": [ { - "$id": "840", + "$id": "901", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -12979,7 +13769,7 @@ }, "parameters": [ { - "$ref": "840" + "$ref": "901" } ], "response": { @@ -13008,7 +13798,7 @@ } }, { - "$id": "841", + "$id": "902", "kind": "paging", "name": "ListWithStringNextLink", "isExactName": false, @@ -13019,7 +13809,7 @@ ], "doc": "List things with nextlink", "operation": { - "$id": "842", + "$id": "903", "name": "ListWithStringNextLink", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -13027,7 +13817,7 @@ "accessibility": "public", "parameters": [ { - "$id": "843", + "$id": "904", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -13043,7 +13833,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.ListWithStringNextLink.accept", "methodParameterSegments": [ { - "$id": "844", + "$id": "905", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -13096,7 +13886,7 @@ }, "parameters": [ { - "$ref": "844" + "$ref": "905" } ], "response": { @@ -13125,7 +13915,7 @@ } }, { - "$id": "845", + "$id": "906", "kind": "paging", "name": "ListWithContinuationToken", "isExactName": false, @@ -13136,7 +13926,7 @@ ], "doc": "List things with continuation token", "operation": { - "$id": "846", + "$id": "907", "name": "ListWithContinuationToken", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -13144,12 +13934,12 @@ "accessibility": "public", "parameters": [ { - "$id": "847", + "$id": "908", "kind": "query", "name": "token", "serializedName": "token", "type": { - "$id": "848", + "$id": "909", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -13164,12 +13954,12 @@ "readOnly": false, "methodParameterSegments": [ { - "$id": "849", + "$id": "910", "kind": "method", "name": "token", "serializedName": "token", "type": { - "$id": "850", + "$id": "911", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -13189,7 +13979,7 @@ "isExactName": false }, { - "$id": "851", + "$id": "912", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -13205,7 +13995,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.ListWithContinuationToken.accept", "methodParameterSegments": [ { - "$id": "852", + "$id": "913", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -13258,10 +14048,10 @@ }, "parameters": [ { - "$ref": "849" + "$ref": "910" }, { - "$ref": "852" + "$ref": "913" } ], "response": { @@ -13282,7 +14072,7 @@ ], "continuationToken": { "parameter": { - "$ref": "847" + "$ref": "908" }, "responseSegments": [ "nextToken" @@ -13293,7 +14083,7 @@ } }, { - "$id": "853", + "$id": "914", "kind": "paging", "name": "ListWithContinuationTokenHeaderResponse", "isExactName": false, @@ -13304,7 +14094,7 @@ ], "doc": "List things with continuation token header response", "operation": { - "$id": "854", + "$id": "915", "name": "ListWithContinuationTokenHeaderResponse", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -13312,12 +14102,12 @@ "accessibility": "public", "parameters": [ { - "$id": "855", + "$id": "916", "kind": "query", "name": "token", "serializedName": "token", "type": { - "$id": "856", + "$id": "917", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -13332,12 +14122,12 @@ "readOnly": false, "methodParameterSegments": [ { - "$id": "857", + "$id": "918", "kind": "method", "name": "token", "serializedName": "token", "type": { - "$id": "858", + "$id": "919", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -13357,7 +14147,7 @@ "isExactName": false }, { - "$id": "859", + "$id": "920", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -13373,7 +14163,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.ListWithContinuationTokenHeaderResponse.accept", "methodParameterSegments": [ { - "$id": "860", + "$id": "921", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -13407,7 +14197,7 @@ "name": "nextToken", "nameInResponse": "next-token", "type": { - "$id": "861", + "$id": "922", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -13438,10 +14228,10 @@ }, "parameters": [ { - "$ref": "857" + "$ref": "918" }, { - "$ref": "860" + "$ref": "921" } ], "response": { @@ -13462,7 +14252,7 @@ ], "continuationToken": { "parameter": { - "$ref": "855" + "$ref": "916" }, "responseSegments": [ "next-token" @@ -13473,7 +14263,7 @@ } }, { - "$id": "862", + "$id": "923", "kind": "paging", "name": "ListWithPaging", "isExactName": false, @@ -13484,7 +14274,7 @@ ], "doc": "List things with paging", "operation": { - "$id": "863", + "$id": "924", "name": "ListWithPaging", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -13492,7 +14282,7 @@ "accessibility": "public", "parameters": [ { - "$id": "864", + "$id": "925", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -13508,7 +14298,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.ListWithPaging.accept", "methodParameterSegments": [ { - "$id": "865", + "$id": "926", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -13561,7 +14351,7 @@ }, "parameters": [ { - "$ref": "865" + "$ref": "926" } ], "response": { @@ -13584,7 +14374,7 @@ } }, { - "$id": "866", + "$id": "927", "kind": "basic", "name": "EmbeddedParameters", "isExactName": false, @@ -13595,7 +14385,7 @@ ], "doc": "An operation with embedded parameters within the body", "operation": { - "$id": "867", + "$id": "928", "name": "EmbeddedParameters", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -13603,13 +14393,13 @@ "accessibility": "public", "parameters": [ { - "$id": "868", + "$id": "929", "kind": "header", "name": "requiredHeader", "serializedName": "required-header", "doc": "required header parameter", "type": { - "$id": "869", + "$id": "930", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -13624,7 +14414,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.ModelWithEmbeddedNonBodyParameters.requiredHeader", "methodParameterSegments": [ { - "$id": "870", + "$id": "931", "kind": "method", "name": "body", "serializedName": "body", @@ -13642,7 +14432,7 @@ "isExactName": false }, { - "$id": "871", + "$id": "932", "kind": "method", "name": "requiredHeader", "serializedName": "requiredHeader", @@ -13664,13 +14454,13 @@ "isExactName": false }, { - "$id": "872", + "$id": "933", "kind": "header", "name": "optionalHeader", "serializedName": "optional-header", "doc": "optional header parameter", "type": { - "$id": "873", + "$id": "934", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -13685,10 +14475,10 @@ "crossLanguageDefinitionId": "SampleTypeSpec.ModelWithEmbeddedNonBodyParameters.optionalHeader", "methodParameterSegments": [ { - "$ref": "870" + "$ref": "931" }, { - "$id": "874", + "$id": "935", "kind": "method", "name": "optionalHeader", "serializedName": "optionalHeader", @@ -13710,13 +14500,13 @@ "isExactName": false }, { - "$id": "875", + "$id": "936", "kind": "query", "name": "requiredQuery", "serializedName": "requiredQuery", "doc": "required query parameter", "type": { - "$id": "876", + "$id": "937", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -13731,10 +14521,10 @@ "readOnly": false, "methodParameterSegments": [ { - "$ref": "870" + "$ref": "931" }, { - "$id": "877", + "$id": "938", "kind": "method", "name": "requiredQuery", "serializedName": "requiredQuery", @@ -13756,13 +14546,13 @@ "isExactName": false }, { - "$id": "878", + "$id": "939", "kind": "query", "name": "optionalQuery", "serializedName": "optionalQuery", "doc": "optional query parameter", "type": { - "$id": "879", + "$id": "940", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -13777,10 +14567,10 @@ "readOnly": false, "methodParameterSegments": [ { - "$ref": "870" + "$ref": "931" }, { - "$id": "880", + "$id": "941", "kind": "method", "name": "optionalQuery", "serializedName": "optionalQuery", @@ -13802,7 +14592,7 @@ "isExactName": false }, { - "$id": "881", + "$id": "942", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -13819,7 +14609,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.EmbeddedParameters.contentType", "methodParameterSegments": [ { - "$id": "882", + "$id": "943", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -13841,7 +14631,7 @@ "isExactName": false }, { - "$id": "883", + "$id": "944", "kind": "body", "name": "body", "serializedName": "body", @@ -13860,7 +14650,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.EmbeddedParameters.body", "methodParameterSegments": [ { - "$ref": "870" + "$ref": "931" } ], "isExactName": false, @@ -13896,10 +14686,10 @@ }, "parameters": [ { - "$ref": "870" + "$ref": "931" }, { - "$ref": "882" + "$ref": "943" } ], "response": {}, @@ -13909,7 +14699,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.EmbeddedParameters" }, { - "$id": "884", + "$id": "945", "kind": "basic", "name": "DynamicModelOperation", "isExactName": false, @@ -13920,7 +14710,7 @@ ], "doc": "An operation with a dynamic model", "operation": { - "$id": "885", + "$id": "946", "name": "DynamicModelOperation", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -13928,7 +14718,7 @@ "accessibility": "public", "parameters": [ { - "$id": "886", + "$id": "947", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -13945,7 +14735,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.DynamicModelOperation.contentType", "methodParameterSegments": [ { - "$id": "887", + "$id": "948", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -13967,7 +14757,7 @@ "isExactName": false }, { - "$id": "888", + "$id": "949", "kind": "body", "name": "body", "serializedName": "body", @@ -13986,7 +14776,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.DynamicModelOperation.body", "methodParameterSegments": [ { - "$id": "889", + "$id": "950", "kind": "method", "name": "body", "serializedName": "body", @@ -14037,10 +14827,10 @@ }, "parameters": [ { - "$ref": "889" + "$ref": "950" }, { - "$ref": "887" + "$ref": "948" } ], "response": {}, @@ -14050,7 +14840,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.DynamicModelOperation" }, { - "$id": "890", + "$id": "951", "kind": "basic", "name": "GetXmlAdvancedModel", "isExactName": false, @@ -14061,7 +14851,7 @@ ], "doc": "Get an advanced XML model with various property types", "operation": { - "$id": "891", + "$id": "952", "name": "GetXmlAdvancedModel", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -14069,7 +14859,7 @@ "accessibility": "public", "parameters": [ { - "$id": "892", + "$id": "953", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -14085,7 +14875,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.GetXmlAdvancedModel.accept", "methodParameterSegments": [ { - "$id": "893", + "$id": "954", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -14146,7 +14936,7 @@ }, "parameters": [ { - "$ref": "893" + "$ref": "954" } ], "response": { @@ -14160,7 +14950,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.GetXmlAdvancedModel" }, { - "$id": "894", + "$id": "955", "kind": "basic", "name": "UpdateXmlAdvancedModel", "isExactName": false, @@ -14171,7 +14961,7 @@ ], "doc": "Update an advanced XML model with various property types", "operation": { - "$id": "895", + "$id": "956", "name": "UpdateXmlAdvancedModel", "isExactName": false, "resourceName": "SampleTypeSpec", @@ -14179,7 +14969,7 @@ "accessibility": "public", "parameters": [ { - "$id": "896", + "$id": "957", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -14195,7 +14985,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.UpdateXmlAdvancedModel.contentType", "methodParameterSegments": [ { - "$id": "897", + "$id": "958", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -14216,7 +15006,7 @@ "isExactName": false }, { - "$id": "898", + "$id": "959", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -14232,7 +15022,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.UpdateXmlAdvancedModel.accept", "methodParameterSegments": [ { - "$id": "899", + "$id": "960", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -14253,7 +15043,7 @@ "isExactName": false }, { - "$id": "900", + "$id": "961", "kind": "body", "name": "body", "serializedName": "body", @@ -14272,7 +15062,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.UpdateXmlAdvancedModel.body", "methodParameterSegments": [ { - "$id": "901", + "$id": "962", "kind": "method", "name": "body", "serializedName": "body", @@ -14341,13 +15131,13 @@ }, "parameters": [ { - "$ref": "901" + "$ref": "962" }, { - "$ref": "897" + "$ref": "958" }, { - "$ref": "899" + "$ref": "960" } ], "response": { @@ -14361,7 +15151,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.UpdateXmlAdvancedModel" }, { - "$id": "902", + "$id": "963", "kind": "basic", "name": "uploadCat", "isExactName": false, @@ -14371,14 +15161,14 @@ "2024-08-16-preview" ], "operation": { - "$id": "903", + "$id": "964", "name": "uploadCat", "isExactName": false, "resourceName": "SampleTypeSpec", "accessibility": "public", "parameters": [ { - "$id": "904", + "$id": "965", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -14394,7 +15184,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.uploadCat.contentType", "methodParameterSegments": [ { - "$id": "905", + "$id": "966", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -14415,7 +15205,7 @@ "isExactName": false }, { - "$id": "906", + "$id": "967", "kind": "body", "name": "body", "serializedName": "body", @@ -14434,7 +15224,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.uploadCat.body", "methodParameterSegments": [ { - "$id": "907", + "$id": "968", "kind": "method", "name": "body", "serializedName": "body", @@ -14481,10 +15271,10 @@ }, "parameters": [ { - "$ref": "905" + "$ref": "966" }, { - "$ref": "907" + "$ref": "968" } ], "response": {}, @@ -14494,7 +15284,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.uploadCat" }, { - "$id": "908", + "$id": "969", "kind": "basic", "name": "sendJsonLines", "isExactName": false, @@ -14504,14 +15294,14 @@ "2024-08-16-preview" ], "operation": { - "$id": "909", + "$id": "970", "name": "sendJsonLines", "isExactName": false, "resourceName": "SampleTypeSpec", "accessibility": "public", "parameters": [ { - "$id": "910", + "$id": "971", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -14527,12 +15317,12 @@ "crossLanguageDefinitionId": "TypeSpec.Http.Streams.JsonlStream.contentType", "methodParameterSegments": [ { - "$id": "911", + "$id": "972", "kind": "method", "name": "stream", "serializedName": "stream", "type": { - "$id": "912", + "$id": "973", "kind": "streaming", "name": "JsonlStreamStreamingItem", "valueType": { @@ -14555,7 +15345,7 @@ "isExactName": false }, { - "$id": "913", + "$id": "974", "kind": "method", "name": "contentType", "serializedName": "contentType", @@ -14576,12 +15366,12 @@ "isExactName": false }, { - "$id": "914", + "$id": "975", "kind": "body", "name": "body", "serializedName": "body", "type": { - "$id": "915", + "$id": "976", "kind": "streaming", "name": "JsonlStreamStreamingItem", "valueType": { @@ -14605,15 +15395,15 @@ "crossLanguageDefinitionId": "TypeSpec.Http.Streams.JsonlStream.body", "methodParameterSegments": [ { - "$ref": "911" + "$ref": "972" }, { - "$id": "916", + "$id": "977", "kind": "method", "name": "body", "serializedName": "body", "type": { - "$ref": "639" + "$ref": "700" }, "location": "", "isApiVersion": false, @@ -14659,7 +15449,7 @@ }, "parameters": [ { - "$ref": "911" + "$ref": "972" } ], "response": {}, @@ -14669,7 +15459,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.sendJsonLines" }, { - "$id": "917", + "$id": "978", "kind": "basic", "name": "receiveJsonLines", "isExactName": false, @@ -14679,14 +15469,14 @@ "2024-08-16-preview" ], "operation": { - "$id": "918", + "$id": "979", "name": "receiveJsonLines", "isExactName": false, "resourceName": "SampleTypeSpec", "accessibility": "public", "parameters": [ { - "$id": "919", + "$id": "980", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -14702,7 +15492,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.receiveJsonLines.accept", "methodParameterSegments": [ { - "$id": "920", + "$id": "981", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -14729,7 +15519,7 @@ 200 ], "bodyType": { - "$id": "921", + "$id": "982", "kind": "streaming", "name": "JsonlStreamStreamingItem", "valueType": { @@ -14773,12 +15563,12 @@ }, "parameters": [ { - "$ref": "920" + "$ref": "981" } ], "response": { "type": { - "$id": "922", + "$id": "983", "kind": "streaming", "name": "JsonlStreamStreamingItem", "valueType": { @@ -14797,7 +15587,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.receiveJsonLines" }, { - "$id": "923", + "$id": "984", "kind": "basic", "name": "receiveSse", "isExactName": false, @@ -14807,14 +15597,14 @@ "2024-08-16-preview" ], "operation": { - "$id": "924", + "$id": "985", "name": "receiveSse", "isExactName": false, "resourceName": "SampleTypeSpec", "accessibility": "public", "parameters": [ { - "$id": "925", + "$id": "986", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -14830,7 +15620,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.receiveSse.accept", "methodParameterSegments": [ { - "$id": "926", + "$id": "987", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -14857,11 +15647,11 @@ 200 ], "bodyType": { - "$id": "927", + "$id": "988", "kind": "streaming", "name": "SSEStreamSampleEvents", "valueType": { - "$id": "928", + "$id": "989", "kind": "union", "name": "SampleEvents", "variantTypes": [ @@ -14911,16 +15701,16 @@ }, "parameters": [ { - "$ref": "926" + "$ref": "987" } ], "response": { "type": { - "$id": "929", + "$id": "990", "kind": "streaming", "name": "SSEStreamSampleEvents", "valueType": { - "$ref": "928" + "$ref": "989" }, "streamKind": "sse", "contentTypes": [ @@ -14938,12 +15728,12 @@ ], "parameters": [ { - "$id": "930", + "$id": "991", "kind": "endpoint", "name": "sampleTypeSpecUrl", "serializedName": "sampleTypeSpecUrl", "type": { - "$id": "931", + "$id": "992", "kind": "url", "name": "endpoint", "crossLanguageDefinitionId": "TypeSpec.url" @@ -14959,7 +15749,7 @@ "isExactName": false }, { - "$ref": "834" + "$ref": "895" } ], "initializedBy": 1, @@ -14971,14 +15761,14 @@ ], "children": [ { - "$id": "932", + "$id": "993", "kind": "client", "name": "AnimalOperations", "isExactName": false, "namespace": "SampleTypeSpec", "methods": [ { - "$id": "933", + "$id": "994", "kind": "basic", "name": "updatePetAsAnimal", "isExactName": false, @@ -14989,7 +15779,7 @@ ], "doc": "Update a pet as an animal", "operation": { - "$id": "934", + "$id": "995", "name": "updatePetAsAnimal", "isExactName": false, "resourceName": "AnimalOperations", @@ -14997,7 +15787,7 @@ "accessibility": "public", "parameters": [ { - "$id": "935", + "$id": "996", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -15014,7 +15804,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.AnimalOperations.updatePetAsAnimal.contentType", "methodParameterSegments": [ { - "$id": "936", + "$id": "997", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -15036,7 +15826,7 @@ "isExactName": false }, { - "$id": "937", + "$id": "998", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -15052,7 +15842,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.AnimalOperations.updatePetAsAnimal.accept", "methodParameterSegments": [ { - "$id": "938", + "$id": "999", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -15073,7 +15863,7 @@ "isExactName": false }, { - "$id": "939", + "$id": "1000", "kind": "body", "name": "animal", "serializedName": "animal", @@ -15092,7 +15882,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.AnimalOperations.updatePetAsAnimal.animal", "methodParameterSegments": [ { - "$id": "940", + "$id": "1001", "kind": "method", "name": "animal", "serializedName": "animal", @@ -15153,13 +15943,13 @@ }, "parameters": [ { - "$ref": "940" + "$ref": "1001" }, { - "$ref": "936" + "$ref": "997" }, { - "$ref": "938" + "$ref": "999" } ], "response": { @@ -15173,7 +15963,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.AnimalOperations.updatePetAsAnimal" }, { - "$id": "941", + "$id": "1002", "kind": "basic", "name": "updateDogAsAnimal", "isExactName": false, @@ -15184,7 +15974,7 @@ ], "doc": "Update a dog as an animal", "operation": { - "$id": "942", + "$id": "1003", "name": "updateDogAsAnimal", "isExactName": false, "resourceName": "AnimalOperations", @@ -15192,7 +15982,7 @@ "accessibility": "public", "parameters": [ { - "$id": "943", + "$id": "1004", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -15209,7 +15999,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.AnimalOperations.updateDogAsAnimal.contentType", "methodParameterSegments": [ { - "$id": "944", + "$id": "1005", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -15231,7 +16021,7 @@ "isExactName": false }, { - "$id": "945", + "$id": "1006", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -15247,7 +16037,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.AnimalOperations.updateDogAsAnimal.accept", "methodParameterSegments": [ { - "$id": "946", + "$id": "1007", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -15268,7 +16058,7 @@ "isExactName": false }, { - "$id": "947", + "$id": "1008", "kind": "body", "name": "animal", "serializedName": "animal", @@ -15287,7 +16077,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.AnimalOperations.updateDogAsAnimal.animal", "methodParameterSegments": [ { - "$id": "948", + "$id": "1009", "kind": "method", "name": "animal", "serializedName": "animal", @@ -15348,13 +16138,13 @@ }, "parameters": [ { - "$ref": "948" + "$ref": "1009" }, { - "$ref": "944" + "$ref": "1005" }, { - "$ref": "946" + "$ref": "1007" } ], "response": { @@ -15370,12 +16160,12 @@ ], "parameters": [ { - "$id": "949", + "$id": "1010", "kind": "endpoint", "name": "sampleTypeSpecUrl", "serializedName": "sampleTypeSpecUrl", "type": { - "$id": "950", + "$id": "1011", "kind": "url", "name": "endpoint", "crossLanguageDefinitionId": "TypeSpec.url" @@ -15399,19 +16189,19 @@ "2024-08-16-preview" ], "parent": { - "$ref": "640" + "$ref": "701" }, "isMultiServiceClient": false }, { - "$id": "951", + "$id": "1012", "kind": "client", "name": "PetOperations", "isExactName": false, "namespace": "SampleTypeSpec", "methods": [ { - "$id": "952", + "$id": "1013", "kind": "basic", "name": "updatePetAsPet", "isExactName": false, @@ -15422,7 +16212,7 @@ ], "doc": "Update a pet as a pet", "operation": { - "$id": "953", + "$id": "1014", "name": "updatePetAsPet", "isExactName": false, "resourceName": "PetOperations", @@ -15430,7 +16220,7 @@ "accessibility": "public", "parameters": [ { - "$id": "954", + "$id": "1015", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -15447,7 +16237,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PetOperations.updatePetAsPet.contentType", "methodParameterSegments": [ { - "$id": "955", + "$id": "1016", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -15469,7 +16259,7 @@ "isExactName": false }, { - "$id": "956", + "$id": "1017", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -15485,7 +16275,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PetOperations.updatePetAsPet.accept", "methodParameterSegments": [ { - "$id": "957", + "$id": "1018", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -15506,7 +16296,7 @@ "isExactName": false }, { - "$id": "958", + "$id": "1019", "kind": "body", "name": "pet", "serializedName": "pet", @@ -15525,7 +16315,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PetOperations.updatePetAsPet.pet", "methodParameterSegments": [ { - "$id": "959", + "$id": "1020", "kind": "method", "name": "pet", "serializedName": "pet", @@ -15586,13 +16376,13 @@ }, "parameters": [ { - "$ref": "959" + "$ref": "1020" }, { - "$ref": "955" + "$ref": "1016" }, { - "$ref": "957" + "$ref": "1018" } ], "response": { @@ -15606,7 +16396,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PetOperations.updatePetAsPet" }, { - "$id": "960", + "$id": "1021", "kind": "basic", "name": "updateDogAsPet", "isExactName": false, @@ -15617,7 +16407,7 @@ ], "doc": "Update a dog as a pet", "operation": { - "$id": "961", + "$id": "1022", "name": "updateDogAsPet", "isExactName": false, "resourceName": "PetOperations", @@ -15625,7 +16415,7 @@ "accessibility": "public", "parameters": [ { - "$id": "962", + "$id": "1023", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -15642,7 +16432,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PetOperations.updateDogAsPet.contentType", "methodParameterSegments": [ { - "$id": "963", + "$id": "1024", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -15664,7 +16454,7 @@ "isExactName": false }, { - "$id": "964", + "$id": "1025", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -15680,7 +16470,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PetOperations.updateDogAsPet.accept", "methodParameterSegments": [ { - "$id": "965", + "$id": "1026", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -15701,7 +16491,7 @@ "isExactName": false }, { - "$id": "966", + "$id": "1027", "kind": "body", "name": "pet", "serializedName": "pet", @@ -15720,7 +16510,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PetOperations.updateDogAsPet.pet", "methodParameterSegments": [ { - "$id": "967", + "$id": "1028", "kind": "method", "name": "pet", "serializedName": "pet", @@ -15781,13 +16571,13 @@ }, "parameters": [ { - "$ref": "967" + "$ref": "1028" }, { - "$ref": "963" + "$ref": "1024" }, { - "$ref": "965" + "$ref": "1026" } ], "response": { @@ -15803,12 +16593,12 @@ ], "parameters": [ { - "$id": "968", + "$id": "1029", "kind": "endpoint", "name": "sampleTypeSpecUrl", "serializedName": "sampleTypeSpecUrl", "type": { - "$id": "969", + "$id": "1030", "kind": "url", "name": "endpoint", "crossLanguageDefinitionId": "TypeSpec.url" @@ -15832,19 +16622,19 @@ "2024-08-16-preview" ], "parent": { - "$ref": "640" + "$ref": "701" }, "isMultiServiceClient": false }, { - "$id": "970", + "$id": "1031", "kind": "client", "name": "DogOperations", "isExactName": false, "namespace": "SampleTypeSpec", "methods": [ { - "$id": "971", + "$id": "1032", "kind": "basic", "name": "updateDogAsDog", "isExactName": false, @@ -15855,7 +16645,7 @@ ], "doc": "Update a dog as a dog", "operation": { - "$id": "972", + "$id": "1033", "name": "updateDogAsDog", "isExactName": false, "resourceName": "DogOperations", @@ -15863,7 +16653,7 @@ "accessibility": "public", "parameters": [ { - "$id": "973", + "$id": "1034", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -15880,7 +16670,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.DogOperations.updateDogAsDog.contentType", "methodParameterSegments": [ { - "$id": "974", + "$id": "1035", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -15902,7 +16692,7 @@ "isExactName": false }, { - "$id": "975", + "$id": "1036", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -15918,7 +16708,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.DogOperations.updateDogAsDog.accept", "methodParameterSegments": [ { - "$id": "976", + "$id": "1037", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -15939,7 +16729,7 @@ "isExactName": false }, { - "$id": "977", + "$id": "1038", "kind": "body", "name": "dog", "serializedName": "dog", @@ -15958,7 +16748,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.DogOperations.updateDogAsDog.dog", "methodParameterSegments": [ { - "$id": "978", + "$id": "1039", "kind": "method", "name": "dog", "serializedName": "dog", @@ -16019,13 +16809,13 @@ }, "parameters": [ { - "$ref": "978" + "$ref": "1039" }, { - "$ref": "974" + "$ref": "1035" }, { - "$ref": "976" + "$ref": "1037" } ], "response": { @@ -16041,12 +16831,12 @@ ], "parameters": [ { - "$id": "979", + "$id": "1040", "kind": "endpoint", "name": "sampleTypeSpecUrl", "serializedName": "sampleTypeSpecUrl", "type": { - "$id": "980", + "$id": "1041", "kind": "url", "name": "endpoint", "crossLanguageDefinitionId": "TypeSpec.url" @@ -16070,19 +16860,19 @@ "2024-08-16-preview" ], "parent": { - "$ref": "640" + "$ref": "701" }, "isMultiServiceClient": false }, { - "$id": "981", + "$id": "1042", "kind": "client", "name": "PlantOperations", "isExactName": false, "namespace": "SampleTypeSpec", "methods": [ { - "$id": "982", + "$id": "1043", "kind": "basic", "name": "getTree", "isExactName": false, @@ -16093,7 +16883,7 @@ ], "doc": "Get a tree as a plant", "operation": { - "$id": "983", + "$id": "1044", "name": "getTree", "isExactName": false, "resourceName": "PlantOperations", @@ -16101,7 +16891,7 @@ "accessibility": "public", "parameters": [ { - "$id": "984", + "$id": "1045", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -16117,7 +16907,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PlantOperations.getTree.accept", "methodParameterSegments": [ { - "$id": "985", + "$id": "1046", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -16178,7 +16968,7 @@ }, "parameters": [ { - "$ref": "985" + "$ref": "1046" } ], "response": { @@ -16192,7 +16982,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PlantOperations.getTree" }, { - "$id": "986", + "$id": "1047", "kind": "basic", "name": "getTreeAsJson", "isExactName": false, @@ -16203,7 +16993,7 @@ ], "doc": "Get a tree as a plant", "operation": { - "$id": "987", + "$id": "1048", "name": "getTreeAsJson", "isExactName": false, "resourceName": "PlantOperations", @@ -16211,7 +17001,7 @@ "accessibility": "public", "parameters": [ { - "$id": "988", + "$id": "1049", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -16227,7 +17017,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PlantOperations.getTreeAsJson.accept", "methodParameterSegments": [ { - "$id": "989", + "$id": "1050", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -16288,7 +17078,7 @@ }, "parameters": [ { - "$ref": "989" + "$ref": "1050" } ], "response": { @@ -16302,7 +17092,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PlantOperations.getTreeAsJson" }, { - "$id": "990", + "$id": "1051", "kind": "basic", "name": "updateTree", "isExactName": false, @@ -16313,7 +17103,7 @@ ], "doc": "Update a tree as a plant", "operation": { - "$id": "991", + "$id": "1052", "name": "updateTree", "isExactName": false, "resourceName": "PlantOperations", @@ -16321,7 +17111,7 @@ "accessibility": "public", "parameters": [ { - "$id": "992", + "$id": "1053", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -16337,7 +17127,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PlantOperations.updateTree.contentType", "methodParameterSegments": [ { - "$id": "993", + "$id": "1054", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -16358,7 +17148,7 @@ "isExactName": false }, { - "$id": "994", + "$id": "1055", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -16374,7 +17164,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PlantOperations.updateTree.accept", "methodParameterSegments": [ { - "$id": "995", + "$id": "1056", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -16395,7 +17185,7 @@ "isExactName": false }, { - "$id": "996", + "$id": "1057", "kind": "body", "name": "tree", "serializedName": "tree", @@ -16414,7 +17204,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PlantOperations.updateTree.tree", "methodParameterSegments": [ { - "$id": "997", + "$id": "1058", "kind": "method", "name": "tree", "serializedName": "tree", @@ -16483,13 +17273,13 @@ }, "parameters": [ { - "$ref": "997" + "$ref": "1058" }, { - "$ref": "993" + "$ref": "1054" }, { - "$ref": "995" + "$ref": "1056" } ], "response": { @@ -16503,7 +17293,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PlantOperations.updateTree" }, { - "$id": "998", + "$id": "1059", "kind": "basic", "name": "updateTreeAsJson", "isExactName": false, @@ -16514,7 +17304,7 @@ ], "doc": "Update a tree as a plant", "operation": { - "$id": "999", + "$id": "1060", "name": "updateTreeAsJson", "isExactName": false, "resourceName": "PlantOperations", @@ -16522,7 +17312,7 @@ "accessibility": "public", "parameters": [ { - "$id": "1000", + "$id": "1061", "kind": "header", "name": "contentType", "serializedName": "Content-Type", @@ -16538,7 +17328,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PlantOperations.updateTreeAsJson.contentType", "methodParameterSegments": [ { - "$id": "1001", + "$id": "1062", "kind": "method", "name": "contentType", "serializedName": "Content-Type", @@ -16559,7 +17349,7 @@ "isExactName": false }, { - "$id": "1002", + "$id": "1063", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -16575,7 +17365,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PlantOperations.updateTreeAsJson.accept", "methodParameterSegments": [ { - "$id": "1003", + "$id": "1064", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -16596,7 +17386,7 @@ "isExactName": false }, { - "$id": "1004", + "$id": "1065", "kind": "body", "name": "tree", "serializedName": "tree", @@ -16615,7 +17405,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.PlantOperations.updateTreeAsJson.tree", "methodParameterSegments": [ { - "$id": "1005", + "$id": "1066", "kind": "method", "name": "tree", "serializedName": "tree", @@ -16684,13 +17474,13 @@ }, "parameters": [ { - "$ref": "1005" + "$ref": "1066" }, { - "$ref": "1001" + "$ref": "1062" }, { - "$ref": "1003" + "$ref": "1064" } ], "response": { @@ -16706,12 +17496,12 @@ ], "parameters": [ { - "$id": "1006", + "$id": "1067", "kind": "endpoint", "name": "sampleTypeSpecUrl", "serializedName": "sampleTypeSpecUrl", "type": { - "$id": "1007", + "$id": "1068", "kind": "url", "name": "endpoint", "crossLanguageDefinitionId": "TypeSpec.url" @@ -16735,19 +17525,19 @@ "2024-08-16-preview" ], "parent": { - "$ref": "640" + "$ref": "701" }, "isMultiServiceClient": false }, { - "$id": "1008", + "$id": "1069", "kind": "client", "name": "Metrics", "isExactName": false, "namespace": "SampleTypeSpec", "methods": [ { - "$id": "1009", + "$id": "1070", "kind": "basic", "name": "getWidgetMetrics", "isExactName": false, @@ -16758,7 +17548,7 @@ ], "doc": "Get Widget metrics for given day of week", "operation": { - "$id": "1010", + "$id": "1071", "name": "getWidgetMetrics", "isExactName": false, "resourceName": "Metrics", @@ -16766,12 +17556,12 @@ "accessibility": "public", "parameters": [ { - "$id": "1011", + "$id": "1072", "kind": "path", "name": "metricsNamespace", "serializedName": "metricsNamespace", "type": { - "$id": "1012", + "$id": "1073", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -16789,12 +17579,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.Metrics.getWidgetMetrics.metricsNamespace", "methodParameterSegments": [ { - "$id": "1013", + "$id": "1074", "kind": "method", "name": "metricsNamespace", "serializedName": "metricsNamespace", "type": { - "$id": "1014", + "$id": "1075", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -16814,7 +17604,7 @@ "isExactName": false }, { - "$id": "1015", + "$id": "1076", "kind": "path", "name": "day", "serializedName": "day", @@ -16833,7 +17623,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.Metrics.getWidgetMetrics.day", "methodParameterSegments": [ { - "$id": "1016", + "$id": "1077", "kind": "method", "name": "day", "serializedName": "day", @@ -16854,7 +17644,7 @@ "isExactName": false }, { - "$id": "1017", + "$id": "1078", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -16870,7 +17660,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.Metrics.getWidgetMetrics.accept", "methodParameterSegments": [ { - "$id": "1018", + "$id": "1079", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -16923,10 +17713,10 @@ }, "parameters": [ { - "$ref": "1016" + "$ref": "1077" }, { - "$ref": "1018" + "$ref": "1079" } ], "response": { @@ -16942,12 +17732,12 @@ ], "parameters": [ { - "$id": "1019", + "$id": "1080", "kind": "endpoint", "name": "sampleTypeSpecUrl", "serializedName": "sampleTypeSpecUrl", "type": { - "$id": "1020", + "$id": "1081", "kind": "url", "name": "endpoint", "crossLanguageDefinitionId": "TypeSpec.url" @@ -16963,7 +17753,7 @@ "isExactName": false }, { - "$ref": "1013" + "$ref": "1074" } ], "initializedBy": 3, @@ -16974,19 +17764,19 @@ "2024-08-16-preview" ], "parent": { - "$ref": "640" + "$ref": "701" }, "isMultiServiceClient": false }, { - "$id": "1021", + "$id": "1082", "kind": "client", "name": "Notebooks", "isExactName": false, "namespace": "SampleTypeSpec", "methods": [ { - "$id": "1022", + "$id": "1083", "kind": "basic", "name": "getNotebook", "isExactName": false, @@ -16997,7 +17787,7 @@ ], "doc": "Get a notebook by name", "operation": { - "$id": "1023", + "$id": "1084", "name": "getNotebook", "isExactName": false, "resourceName": "Notebooks", @@ -17005,12 +17795,12 @@ "accessibility": "public", "parameters": [ { - "$id": "1024", + "$id": "1085", "kind": "path", "name": "notebookName", "serializedName": "notebookName", "type": { - "$id": "1025", + "$id": "1086", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -17028,12 +17818,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.Notebooks.getNotebook.notebookName", "methodParameterSegments": [ { - "$id": "1026", + "$id": "1087", "kind": "method", "name": "notebook", "serializedName": "notebook", "type": { - "$id": "1027", + "$id": "1088", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -17054,7 +17844,7 @@ "isExactName": false }, { - "$id": "1028", + "$id": "1089", "kind": "header", "name": "accept", "serializedName": "Accept", @@ -17070,7 +17860,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.Notebooks.getNotebook.accept", "methodParameterSegments": [ { - "$id": "1029", + "$id": "1090", "kind": "method", "name": "accept", "serializedName": "Accept", @@ -17123,7 +17913,7 @@ }, "parameters": [ { - "$ref": "1029" + "$ref": "1090" } ], "response": { @@ -17139,12 +17929,12 @@ ], "parameters": [ { - "$id": "1030", + "$id": "1091", "kind": "endpoint", "name": "sampleTypeSpecUrl", "serializedName": "sampleTypeSpecUrl", "type": { - "$id": "1031", + "$id": "1092", "kind": "url", "name": "endpoint", "crossLanguageDefinitionId": "TypeSpec.url" @@ -17160,7 +17950,7 @@ "isExactName": false }, { - "$ref": "1026" + "$ref": "1087" } ], "initializedBy": 3, @@ -17171,7 +17961,7 @@ "2024-08-16-preview" ], "parent": { - "$ref": "640" + "$ref": "701" }, "isMultiServiceClient": false }