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 bbbaa9daa94..1cb7f967592 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 @@ -183,7 +183,6 @@ private MethodBodyStatement CreateXmlWriteAttributeStatement(XmlPropertyInfo pro var xmlWireInfo = prop.XmlWireInfo; if (xmlWireInfo.Namespace != null && namespaces?.TryGetValue(xmlWireInfo.Namespace.Namespace, out var nsInfo) == true) { - var stringValue = CreateXmlSerializeValueExpression(prop.SerializationExp, prop.PropertyType, prop.SerializationFormat); var writeStatement = _xmlWriterSnippet.WriteAttributeString( nsInfo.Prefix, xmlWireInfo.Name, @@ -268,9 +267,7 @@ private MethodBodyStatement CreateXmlWriteValueStatement(ValueExpression value, if (!underlyingType.IsFrameworkType) { - return underlyingType.IsEnum - ? _xmlWriterSnippet.WriteValue(CreateXmlSerializeValueExpression(value, valueType, serializationFormat)) - : _xmlWriterSnippet.WriteObjectValue(value.As(valueType), _serializationOptionsParameter); + return ScmCodeModelGenerator.Instance.TypeFactory.SerializeXmlValue(valueType, value, _xmlWriterSnippet, _mrwOptionsParameterSnippet, serializationFormat); } return underlyingType.FrameworkType switch @@ -282,7 +279,7 @@ Type t when (t == typeof(byte[]) || t == typeof(BinaryData)) && serializationFor ? value.As().ToArray() : value.NullableStructValue(valueType), serializationFormat.ToFormatSpecifier()), - _ => _xmlWriterSnippet.WriteValue(CreateXmlSerializeValueExpression(value, valueType, serializationFormat)) + _ => ScmCodeModelGenerator.Instance.TypeFactory.SerializeXmlValue(valueType, value, _xmlWriterSnippet, _mrwOptionsParameterSnippet, serializationFormat) }; } @@ -430,27 +427,8 @@ private MethodBodyStatement CreateXmlWriteDictionaryEntryStatement( private MethodBodyStatement CreateXmlWriteTextContentStatement(XmlPropertyInfo prop) { - var serializedValue = CreateXmlSerializeValueExpression(prop.SerializationExp, prop.PropertyType, prop.SerializationFormat); - return WrapInIsDefinedCheck(prop, _xmlWriterSnippet.WriteValue(serializedValue)); - } - - private ValueExpression CreateXmlSerializeValueExpression(ValueExpression value, CSharpType valueType, SerializationFormat serializationFormat) - { - var underlyingType = valueType.IsNullable && valueType.Arguments.Count > 0 - ? valueType.Arguments[0] - : valueType; - - if (underlyingType.IsEnum) - { - return underlyingType.ToSerial(value.NullableStructValue(valueType)); - } - - if (!underlyingType.IsFrameworkType) - { - return value; - } - - return CreateXmlSerializePrimitiveExpression(value.NullableStructValue(valueType), underlyingType, serializationFormat); + var writeStatement = ScmCodeModelGenerator.Instance.TypeFactory.SerializeXmlValue(prop.PropertyType, prop.SerializationExp, _xmlWriterSnippet, _mrwOptionsParameterSnippet, prop.SerializationFormat); + return WrapInIsDefinedCheck(prop, writeStatement); } private static ValueExpression CreateXmlSerializePrimitiveExpression(ValueExpression value, CSharpType valueType, SerializationFormat serializationFormat) @@ -886,7 +864,7 @@ private MethodBodyStatement CreateXmlDeserializePropertyAssignment( return CreateXmlDeserializeDictionaryAssignment(childElement, propertyType, propertyExpression, xmlWireInfo, serializationFormat); } - var deserializedValue = CreateXmlDeserializeValueExpression(childElement, propertyType, serializationFormat); + var deserializedValue = ScmCodeModelGenerator.Instance.TypeFactory.DeserializeXmlValue(propertyType, childElement, _mrwOptionsParameterSnippet, serializationFormat); return propertyExpression.Assign(deserializedValue).Terminate(); } @@ -1028,30 +1006,10 @@ private MethodBodyStatement DeserializeXmlValue( return new MethodBodyStatement[] { dictDeclaration, foreachStatement }; } - value = CreateXmlDeserializeValueExpression(element, valueType, serializationFormat); + value = ScmCodeModelGenerator.Instance.TypeFactory.DeserializeXmlValue(valueType, element, _mrwOptionsParameterSnippet, serializationFormat); return MethodBodyStatement.Empty; } - private ValueExpression CreateXmlDeserializeValueExpression(ScopedApi element, CSharpType valueType, SerializationFormat serializationFormat) - { - var underlyingType = valueType.IsNullable && valueType.Arguments.Count > 0 - ? valueType.Arguments[0] - : valueType; - - if (underlyingType.IsEnum && underlyingType.UnderlyingEnumType != null) - { - var underlyingExpression = CreateXmlDeserializePrimitiveExpression(element, underlyingType.UnderlyingEnumType, serializationFormat); - return underlyingType.ToEnum(underlyingExpression); - } - - if (!underlyingType.IsFrameworkType) - { - return GetDeserializationMethodInvocationForType(underlyingType, element, null, _serializationOptionsParameter); - } - - return CreateXmlDeserializePrimitiveExpression(element, valueType, serializationFormat); - } - private static ValueExpression CreateXmlDeserializePrimitiveExpression( ScopedApi element, CSharpType valueType, @@ -1076,6 +1034,54 @@ private static ValueExpression CreateXmlDeserializePrimitiveExpression( }; } + internal static ValueExpression DeserializeXmlValueCore( + CSharpType valueType, + ScopedApi element, + ScopedApi mrwOptions, + SerializationFormat format) + { + var underlyingType = valueType.IsNullable && valueType.Arguments.Count > 0 + ? valueType.Arguments[0] + : valueType; + + if (underlyingType.IsEnum && underlyingType.UnderlyingEnumType != null) + { + var underlyingExpression = CreateXmlDeserializePrimitiveExpression(element, underlyingType.UnderlyingEnumType, format); + return underlyingType.ToEnum(underlyingExpression); + } + + if (!underlyingType.IsFrameworkType) + { + return GetDeserializationMethodInvocationForType(underlyingType, element, null, mrwOptions); + } + + return CreateXmlDeserializePrimitiveExpression(element, valueType, format); + } + + internal static MethodBodyStatement SerializeXmlValueCore( + CSharpType valueType, + ValueExpression value, + ScopedApi xmlWriter, + ScopedApi mrwOptionsParameter, + SerializationFormat serializationFormat) + { + var underlyingType = valueType.IsNullable && valueType.Arguments.Count > 0 + ? valueType.Arguments[0] + : valueType; + + if (underlyingType.IsEnum) + { + return xmlWriter.WriteValue(underlyingType.ToSerial(value.NullableStructValue(valueType))); + } + + if (!underlyingType.IsFrameworkType) + { + return xmlWriter.WriteObjectValue(value.As(valueType), mrwOptionsParameter); + } + + return xmlWriter.WriteValue(CreateXmlSerializePrimitiveExpression(value.NullableStructValue(valueType), underlyingType, serializationFormat)); + } + private MethodBodyStatement CreateXmlDeserializeAttributeStatements( ScopedApi attrVariable, List attributeProperties, diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmTypeFactory.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmTypeFactory.cs index d1d644a638b..9936e75a22a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmTypeFactory.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmTypeFactory.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json; +using System.Xml; +using System.Xml.Linq; using Microsoft.TypeSpec.Generator.ClientModel.Primitives; using Microsoft.TypeSpec.Generator.ClientModel.Providers; using Microsoft.TypeSpec.Generator.Expressions; @@ -246,6 +248,21 @@ public virtual MethodBodyStatement SerializeJsonValue( SerializationFormat serializationFormat) => MrwSerializationTypeDefinition.SerializeJsonValueCore(valueType, value, utf8JsonWriter, mrwOptionsParameter, serializationFormat); + public virtual ValueExpression DeserializeXmlValue( + CSharpType valueType, + ScopedApi element, + ScopedApi mrwOptionsParameter, + SerializationFormat format) + => MrwSerializationTypeDefinition.DeserializeXmlValueCore(valueType, element, mrwOptionsParameter, format); + + public virtual MethodBodyStatement SerializeXmlValue( + CSharpType valueType, + ValueExpression value, + ScopedApi xmlWriter, + ScopedApi mrwOptionsParameter, + SerializationFormat format) + => MrwSerializationTypeDefinition.SerializeXmlValueCore(valueType, value, xmlWriter, mrwOptionsParameter, format); + protected override ModelProvider? CreateModelCore(InputModelType model) => new ScmModelProvider(model); protected override ScmSerializationOptions? CreateSerializationOptionsCore(InputSerializationOptions inputSerializationOptions) 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 1ea6851d8b1..4ce3df526ad 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 @@ -1464,5 +1464,6 @@ public void TestDeserializationOfNonBase64ByteArrayPropertyUsesGetRawText() Assert.IsFalse(methodBody.Contains("EnumerateArray"), $"byte[] property should not use array enumeration. Actual:\n{methodBody}"); } + } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/XmlDeserializationTests/DeserializeXmlValueOverride_CustomTypeDeserialization.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/XmlDeserializationTests/DeserializeXmlValueOverride_CustomTypeDeserialization.cs new file mode 100644 index 00000000000..16d6c5bd4c0 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/XmlDeserializationTests/DeserializeXmlValueOverride_CustomTypeDeserialization.cs @@ -0,0 +1,37 @@ +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Xml.Linq; +using Sample.Models; + +namespace Sample +{ + public partial class TestXmlModel + { + internal static global::Sample.Models.TestXmlModel DeserializeTestXmlModel(global::System.Xml.Linq.XElement element, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) + { + if ((element == null)) + { + return null; + } + + string name = default; + global::System.Collections.Generic.IDictionary additionalBinaryDataProperties = new global::Sample.ChangeTrackingDictionary(); + + foreach (var child in element.Elements()) + { + string localName = child.Name.LocalName; + if ((localName == "Name")) + { + name = child.ToString(); + continue; + } + } + return new global::Sample.Models.TestXmlModel(name, additionalBinaryDataProperties); + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/XmlSerializationTests/SerializeXmlValueOverride_CustomTypeSerialization.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/XmlSerializationTests/SerializeXmlValueOverride_CustomTypeSerialization.cs new file mode 100644 index 00000000000..40be7e5713d --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/XmlSerializationTests/SerializeXmlValueOverride_CustomTypeSerialization.cs @@ -0,0 +1,30 @@ +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Xml; +using Sample.Models; + +namespace Sample +{ + public partial class TestXmlModel + { + internal virtual void XmlModelWriteCore(global::System.Xml.XmlWriter writer, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) + { + string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if ((format != "X")) + { + throw new global::System.FormatException($"The model {nameof(global::Sample.Models.TestXmlModel)} does not support writing '{format}' format."); + } + + if (global::Sample.Optional.IsDefined(Name)) + { + writer.WriteStartElement("Name"); + writer.WriteValue(Name.ToString()); + writer.WriteEndElement(); + } + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/XmlDeserializationTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/XmlDeserializationTests.cs index 90b7639a9f8..5402cfe4b86 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/XmlDeserializationTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/XmlDeserializationTests.cs @@ -1,16 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.ClientModel.Primitives; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.TypeSpec.Generator.ClientModel.Providers; +using Microsoft.TypeSpec.Generator.Expressions; using Microsoft.TypeSpec.Generator.Input; using Microsoft.TypeSpec.Generator.Primitives; using Microsoft.TypeSpec.Generator.Providers; +using Microsoft.TypeSpec.Generator.Snippets; using Microsoft.TypeSpec.Generator.Tests.Common; +using Moq; using NUnit.Framework; namespace Microsoft.TypeSpec.Generator.ClientModel.Tests.Providers.MrwSerializationTypeDefinitions @@ -601,5 +605,82 @@ protected override MethodProvider[] BuildMethods() protected override FieldProvider[] BuildFields() => []; } + + [TestCase(typeof(int), SerializationFormat.Default, ExpectedResult = "((int)foo)")] + [TestCase(typeof(string), SerializationFormat.Default, ExpectedResult = "((string)foo)")] + [TestCase(typeof(bool), SerializationFormat.Default, ExpectedResult = "((bool)foo)")] + [TestCase(typeof(long), SerializationFormat.Default, ExpectedResult = "((long)foo)")] + [TestCase(typeof(float), SerializationFormat.Default, ExpectedResult = "((float)foo)")] + [TestCase(typeof(double), SerializationFormat.Default, ExpectedResult = "((double)foo)")] + [TestCase(typeof(byte), SerializationFormat.Default, ExpectedResult = "((byte)((int)foo))")] + [TestCase(typeof(sbyte), SerializationFormat.Default, ExpectedResult = "((sbyte)((int)foo))")] + [TestCase(typeof(short), SerializationFormat.Default, ExpectedResult = "((short)((int)foo))")] + public string DeserializeXmlValueCore_PrimitiveTypes(Type type, SerializationFormat format) + { + var expr = MrwSerializationTypeDefinition.DeserializeXmlValueCore( + type, + new ScopedApi(new VariableExpression(typeof(XElement), "foo")), + new ScopedApi(new VariableExpression(typeof(ModelReaderWriterOptions), "options")), + format); + return expr.ToDisplayString(); + } + + [TestCase(SerializationFormat.DateTime_ISO8601, ExpectedResult = "foo.GetDateTimeOffset(\"O\")")] + [TestCase(SerializationFormat.DateTime_RFC1123, ExpectedResult = "foo.GetDateTimeOffset(\"R\")")] + [TestCase(SerializationFormat.DateTime_RFC3339, ExpectedResult = "foo.GetDateTimeOffset(\"O\")")] + public string DeserializeXmlValueCore_DateTimeOffset(SerializationFormat format) + { + var expr = MrwSerializationTypeDefinition.DeserializeXmlValueCore( + typeof(DateTimeOffset), + new ScopedApi(new VariableExpression(typeof(XElement), "foo")), + new ScopedApi(new VariableExpression(typeof(ModelReaderWriterOptions), "options")), + format); + return expr.ToDisplayString(); + } + + [TestCase(SerializationFormat.Duration_ISO8601, ExpectedResult = "foo.GetTimeSpan(\"P\")")] + [TestCase(SerializationFormat.Duration_Constant, ExpectedResult = "foo.GetTimeSpan(\"c\")")] + public string DeserializeXmlValueCore_TimeSpan(SerializationFormat format) + { + var expr = MrwSerializationTypeDefinition.DeserializeXmlValueCore( + typeof(TimeSpan), + new ScopedApi(new VariableExpression(typeof(XElement), "foo")), + new ScopedApi(new VariableExpression(typeof(ModelReaderWriterOptions), "options")), + format); + return expr.ToDisplayString(); + } + + [Test] + public void DeserializeXmlValueOverride_CustomTypeDeserialization() + { + var inputModel = InputFactory.Model( + "TestXmlModel", + usage: InputModelTypeUsage.Input | InputModelTypeUsage.Xml, + properties: [InputFactory.Property("Name", InputPrimitiveType.String, + serializationOptions: InputFactory.Serialization.Options(xml: InputFactory.Serialization.Xml("Name")))]); + + var mockGenerator = MockHelpers.LoadMockGenerator( + inputModels: () => [inputModel]); + + // override DeserializeXmlValue to return a custom expression for string types + var mockTypeFactory = Mock.Get((ScmTypeFactory)mockGenerator.Object.TypeFactory); + mockTypeFactory.Setup(p => p.DeserializeXmlValue( + It.Is(t => t.FrameworkType == typeof(string)), + It.IsAny>(), + It.IsAny>(), + It.IsAny())) + .Returns((CSharpType type, ScopedApi element, ScopedApi mrwOptions, SerializationFormat format) => + element.InvokeToString()); + + var modelProvider = mockGenerator.Object.OutputLibrary.TypeProviders.Single(t => t is ModelProvider && t.Name == "TestXmlModel"); + var serializationProvider = modelProvider.SerializationProviders.Single(t => t is MrwSerializationTypeDefinition); + Assert.IsNotNull(serializationProvider); + + var writer = new TypeProviderWriter(new FilteredMethodsTypeProvider( + serializationProvider, + name => name == "DeserializeTestXmlModel")); + var file = writer.Write(); + Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content); + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/XmlSerializationTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/XmlSerializationTests.cs index 4f3a60697c8..9c22c8c08c1 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/XmlSerializationTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/XmlSerializationTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.ClientModel; using System.ClientModel.Primitives; using System.Collections.Generic; @@ -8,10 +9,14 @@ using System.Threading.Tasks; using System.Xml; using Microsoft.TypeSpec.Generator.ClientModel.Providers; +using Microsoft.TypeSpec.Generator.ClientModel.Snippets; +using Microsoft.TypeSpec.Generator.Expressions; using Microsoft.TypeSpec.Generator.Input; using Microsoft.TypeSpec.Generator.Primitives; using Microsoft.TypeSpec.Generator.Providers; +using Microsoft.TypeSpec.Generator.Snippets; using Microsoft.TypeSpec.Generator.Tests.Common; +using Moq; using NUnit.Framework; namespace Microsoft.TypeSpec.Generator.ClientModel.Tests.Providers.MrwSerializationTypeDefinitions @@ -687,6 +692,84 @@ public async Task PersistableModelWriteCoreHandlesJsonAndXmlFormats() Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content); } + [TestCase(typeof(int), SerializationFormat.Default, ExpectedResult = "writer.WriteValue(value);\n")] + [TestCase(typeof(string), SerializationFormat.Default, ExpectedResult = "writer.WriteValue(value);\n")] + [TestCase(typeof(bool), SerializationFormat.Default, ExpectedResult = "writer.WriteValue(value);\n")] + [TestCase(typeof(long), SerializationFormat.Default, ExpectedResult = "writer.WriteValue(value);\n")] + [TestCase(typeof(float), SerializationFormat.Default, ExpectedResult = "writer.WriteValue(value);\n")] + [TestCase(typeof(double), SerializationFormat.Default, ExpectedResult = "writer.WriteValue(value);\n")] + public string SerializeXmlValueCore_PrimitiveTypes(Type type, SerializationFormat format) + { + var statement = MrwSerializationTypeDefinition.SerializeXmlValueCore( + type, + new VariableExpression(type, "value"), + new ScopedApi(new VariableExpression(typeof(XmlWriter), "writer")), + new ScopedApi(new VariableExpression(typeof(ModelReaderWriterOptions), "options")), + format); + return statement.ToDisplayString(); + } + + [TestCase(SerializationFormat.DateTime_ISO8601, ExpectedResult = "writer.WriteValue(value.ToString(\"O\"));\n")] + [TestCase(SerializationFormat.DateTime_RFC1123, ExpectedResult = "writer.WriteValue(value.ToString(\"R\"));\n")] + [TestCase(SerializationFormat.DateTime_RFC3339, ExpectedResult = "writer.WriteValue(value.ToString(\"O\"));\n")] + public string SerializeXmlValueCore_DateTimeOffset(SerializationFormat format) + { + var statement = MrwSerializationTypeDefinition.SerializeXmlValueCore( + typeof(DateTimeOffset), + new VariableExpression(typeof(DateTimeOffset), "value"), + new ScopedApi(new VariableExpression(typeof(XmlWriter), "writer")), + new ScopedApi(new VariableExpression(typeof(ModelReaderWriterOptions), "options")), + format); + return statement.ToDisplayString(); + } + + [TestCase(SerializationFormat.Duration_ISO8601, ExpectedResult = "writer.WriteValue(value.ToString(\"P\"));\n")] + [TestCase(SerializationFormat.Duration_Constant, ExpectedResult = "writer.WriteValue(value.ToString(\"c\"));\n")] + public string SerializeXmlValueCore_TimeSpan(SerializationFormat format) + { + var statement = MrwSerializationTypeDefinition.SerializeXmlValueCore( + typeof(TimeSpan), + new VariableExpression(typeof(TimeSpan), "value"), + new ScopedApi(new VariableExpression(typeof(XmlWriter), "writer")), + new ScopedApi(new VariableExpression(typeof(ModelReaderWriterOptions), "options")), + format); + return statement.ToDisplayString(); + } + + [Test] + public void SerializeXmlValueOverride_CustomTypeSerialization() + { + var inputModel = InputFactory.Model( + "TestXmlModel", + usage: InputModelTypeUsage.Input | InputModelTypeUsage.Xml, + properties: [InputFactory.Property("Name", InputPrimitiveType.String, + serializationOptions: InputFactory.Serialization.Options(xml: InputFactory.Serialization.Xml("Name")))]); + + var mockGenerator = MockHelpers.LoadMockGenerator( + inputModels: () => [inputModel]); + + // override SerializeXmlValue to return a custom statement for string types + var mockTypeFactory = Mock.Get((ScmTypeFactory)mockGenerator.Object.TypeFactory); + mockTypeFactory.Setup(p => p.SerializeXmlValue( + It.Is(t => t.FrameworkType == typeof(string)), + It.IsAny(), + It.IsAny>(), + It.IsAny>(), + It.IsAny())) + .Returns((CSharpType type, ValueExpression value, ScopedApi xmlWriter, ScopedApi options, SerializationFormat format) => + xmlWriter.WriteValue(value.Invoke(nameof(ToString)))); + + var modelProvider = mockGenerator.Object.OutputLibrary.TypeProviders.Single(t => t is ModelProvider && t.Name == "TestXmlModel"); + var serializationProvider = modelProvider.SerializationProviders.Single(t => t is MrwSerializationTypeDefinition); + Assert.IsNotNull(serializationProvider); + + var writer = new TypeProviderWriter(new FilteredMethodsTypeProvider( + serializationProvider, + name => name == "XmlModelWriteCore")); + var file = writer.Write(); + Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content); + } + private class MockMrwProvider : MrwSerializationTypeDefinition { public MockMrwProvider(InputModelType inputModel, ModelProvider modelProvider)