diff --git a/packages/XmlSchemaClassGenerator-beta.99.0.11-local.nupkg b/packages/XmlSchemaClassGenerator-beta.99.0.11-local.nupkg deleted file mode 100644 index 59a9ec5..0000000 Binary files a/packages/XmlSchemaClassGenerator-beta.99.0.11-local.nupkg and /dev/null differ diff --git a/packages/XmlSchemaClassGenerator-beta.99.0.14-local.nupkg b/packages/XmlSchemaClassGenerator-beta.99.0.14-local.nupkg new file mode 100644 index 0000000..72a1a31 Binary files /dev/null and b/packages/XmlSchemaClassGenerator-beta.99.0.14-local.nupkg differ diff --git a/packages/XmlSchemaClassGenerator.Analyzer.99.0.11-local.nupkg b/packages/XmlSchemaClassGenerator.Analyzer.99.0.11-local.nupkg deleted file mode 100644 index dff2b17..0000000 Binary files a/packages/XmlSchemaClassGenerator.Analyzer.99.0.11-local.nupkg and /dev/null differ diff --git a/packages/XmlSchemaClassGenerator.Analyzer.99.0.14-local.nupkg b/packages/XmlSchemaClassGenerator.Analyzer.99.0.14-local.nupkg new file mode 100644 index 0000000..ae0efd8 Binary files /dev/null and b/packages/XmlSchemaClassGenerator.Analyzer.99.0.14-local.nupkg differ diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index c25bbbb..7913daa 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -8,8 +8,8 @@ - - + + diff --git a/src/generator/Spillgebees.Transmodel.Generator/Services/CodeGenerator.cs b/src/generator/Spillgebees.Transmodel.Generator/Services/CodeGenerator.cs index 81436c3..6c00c05 100644 --- a/src/generator/Spillgebees.Transmodel.Generator/Services/CodeGenerator.cs +++ b/src/generator/Spillgebees.Transmodel.Generator/Services/CodeGenerator.cs @@ -148,6 +148,7 @@ private static XscGenerator CreateBaseGenerator( EnableNullableDirective = true, GenerateRequiredModifier = true, UseShouldSerializePattern = true, + UseShouldSerializeForDefaultValues = true, GenerateChoiceGroupAttributes = true, ChoiceGroupAttributeNamespace = rootNamespace, GenerateStrictFixedValues = true, diff --git a/src/netex/Spillgebees.NeTEx.Models.Tests/Serialization/Netex/v1/SerializationTests.cs b/src/netex/Spillgebees.NeTEx.Models.Tests/Serialization/Netex/v1/SerializationTests.cs index 5219507..c770c69 100644 --- a/src/netex/Spillgebees.NeTEx.Models.Tests/Serialization/Netex/v1/SerializationTests.cs +++ b/src/netex/Spillgebees.NeTEx.Models.Tests/Serialization/Netex/v1/SerializationTests.cs @@ -186,6 +186,31 @@ public void Should_serialize_and_deserialize_optional_element_with_default_value deserialized.Note.Value.Should().Be("false."); } + [Test] + [Arguments("changed")] + [Arguments("false.")] + public void Should_preserve_edits_to_the_contents_of_a_default_object(string text) + { + // arrange + var serializer = new XmlSerializer(typeof(AccessVehicleEquipment)); + var equipment = new AccessVehicleEquipment + { + Id = "NSR:AccessVehicleEquipment:1", + Version = "1", + }; + + // act + equipment.Note!.Value = text; + using var writer = new StringWriter(); + serializer.Serialize(writer, equipment); + using var reader = new StringReader(writer.ToString()); + var result = (AccessVehicleEquipment)serializer.Deserialize(reader)!; + + // assert + writer.ToString().Should().Contain($"{text}"); + result.Note!.Value.Should().Be(text); + } + [Test] public void Should_serialize_simple_content_ref_as_self_closing_element_when_value_is_null() { diff --git a/src/netex/Spillgebees.NeTEx.Models.Tests/XsdTypeMappingTests.cs b/src/netex/Spillgebees.NeTEx.Models.Tests/XsdTypeMappingTests.cs index cff8535..9e7778b 100644 --- a/src/netex/Spillgebees.NeTEx.Models.Tests/XsdTypeMappingTests.cs +++ b/src/netex/Spillgebees.NeTEx.Models.Tests/XsdTypeMappingTests.cs @@ -118,7 +118,7 @@ public void Should_not_have_default_value_attribute_on_nullable_string_property_ } [Test] - public void Should_still_have_default_value_attribute_on_value_type_property_with_xsd_default() + public void Should_track_assignment_instead_of_omitting_explicit_default_values() { // arrange var property = typeof(DayTypeAssignmentVersionStructure) @@ -128,8 +128,8 @@ public void Should_still_have_default_value_attribute_on_value_type_property_wit var attr = property.GetCustomAttribute(); // assert - attr.Should().NotBeNull(); - attr!.Value.Should().Be(true); + attr.Should().BeNull(); + property.DeclaringType!.GetMethod("ShouldSerialize" + property.Name).Should().NotBeNull(); } [Test] diff --git a/src/siri/Spillgebees.SIRI.Models.Tests/ChoiceSchemaRegressionTests.cs b/src/siri/Spillgebees.SIRI.Models.Tests/ChoiceSchemaRegressionTests.cs new file mode 100644 index 0000000..9c26ea7 --- /dev/null +++ b/src/siri/Spillgebees.SIRI.Models.Tests/ChoiceSchemaRegressionTests.cs @@ -0,0 +1,46 @@ +using System.Reflection; +using AwesomeAssertions; + +namespace Spillgebees.SIRI.Models.Tests; + +public class ChoiceSchemaRegressionTests +{ + [Test] + [Arguments(typeof(V2_1.SIRI.AffectedSectionStructureIndirectSectionRef))] + [Arguments(typeof(V2_2.SIRI.AffectedSectionStructureIndirectSectionRef))] + public void Should_allow_mixed_references_in_repeated_situation_exchange_choice(Type sectionType) + { + // arrange + var properties = new[] { "IntermediateStopPointRef", "IntermediateStopPlaceRef", "IntermediateQuayRef" }; + + // act + var attributes = properties.SelectMany(name => sectionType.GetProperty(name)!.GetCustomAttributesData()) + .Where(attribute => attribute.AttributeType.Name == "XmlChoiceGroupAttribute") + .ToList(); + + // assert + attributes.Should().BeEmpty("each repetition can select a different reference type"); + } + + [Test] + [Arguments(typeof(V2_1.SIRI.VehicleMonitoringSubscriptionStructure), "ChangeBeforeUpdates", "UpdateInterval")] + [Arguments(typeof(V2_2.SIRI.VehicleMonitoringSubscriptionStructure), "ChangeBeforeUpdates", "UpdateInterval")] + [Arguments(typeof(V2_1.SIRI.SituationExchangeRequestStructure), "LineRef", "Lines")] + [Arguments(typeof(V2_2.SIRI.SituationExchangeRequestStructure), "LineRef", "Lines")] + public void Should_allow_omitting_choices_with_optional_alternatives(Type modelType, string first, string second) + { + // arrange + var properties = new[] { first, second }; + + // act + var attributes = properties.Select(name => modelType.GetProperty(name)!.GetCustomAttributesData() + .Single(attribute => attribute.AttributeType.Name == "XmlChoiceGroupAttribute")).ToList(); + + // assert + attributes[0].ConstructorArguments[0].Value.Should().Be(attributes[1].ConstructorArguments[0].Value); + attributes[0].ConstructorArguments[1].Value.Should().NotBe(attributes[1].ConstructorArguments[1].Value); + attributes.Should().AllSatisfy(attribute => + attribute.NamedArguments.Single(argument => argument.MemberName == "IsRequired") + .TypedValue.Value.Should().Be(false)); + } +} diff --git a/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/DefaultPresenceTests.cs b/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/DefaultPresenceTests.cs new file mode 100644 index 0000000..15d8073 --- /dev/null +++ b/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/DefaultPresenceTests.cs @@ -0,0 +1,70 @@ +using System.Xml.Linq; +using System.Xml.Serialization; +using AwesomeAssertions; +using Spillgebees.SIRI.Models.V2_2.SIRI; + +namespace Spillgebees.SIRI.Models.Tests.Serialization; + +public class DefaultPresenceTests +{ + [Test] + [Arguments(null)] + [Arguments(true)] + [Arguments(false)] + public void Should_preserve_assignment_of_defaulted_monitored_value(bool? assignedValue) + { + // arrange + var original = new MonitoredVehicleJourneyStructure(); + if (assignedValue.HasValue) + { + original.Monitored = assignedValue.Value; + } + + // act + var xml = Serialize(original); + var monitored = xml.Root!.Element(XName.Get("Monitored", "http://www.siri.org.uk/siri")); + + // assert + original.Monitored.Should().Be(assignedValue ?? true); + if (assignedValue.HasValue) + { + monitored.Should().NotBeNull(); + monitored!.Value.Should().Be(assignedValue.Value ? "true" : "false"); + } + else + { + monitored.Should().BeNull(); + } + } + + [Test] + [Arguments("")] + [Arguments("true")] + [Arguments("false")] + public void Should_preserve_default_presence_after_deserialization(string element) + { + // arrange + var serializer = new XmlSerializer(typeof(MonitoredVehicleJourneyStructure)); + using var reader = new StringReader($"{element}"); + + // act + var model = (MonitoredVehicleJourneyStructure)serializer.Deserialize(reader)!; + var xml = Serialize(model); + + // assert + var monitored = xml.Root!.Element(XName.Get("Monitored", "http://www.siri.org.uk/siri")); + (monitored is not null).Should().Be(element.Length != 0); + if (monitored is not null) + { + monitored.Value.Should().Be(model.Monitored ? "true" : "false"); + } + } + + private static XDocument Serialize(MonitoredVehicleJourneyStructure model) + { + var serializer = new XmlSerializer(typeof(MonitoredVehicleJourneyStructure)); + using var writer = new StringWriter(); + serializer.Serialize(writer, model); + return XDocument.Parse(writer.ToString()); + } +} diff --git a/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/FixedValueSerializationTests.cs b/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/FixedValueSerializationTests.cs new file mode 100644 index 0000000..920f662 --- /dev/null +++ b/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/FixedValueSerializationTests.cs @@ -0,0 +1,83 @@ +using System.Xml.Linq; +using System.Xml.Serialization; +using AwesomeAssertions; +using Spillgebees.SIRI.Models.V2_2.SIRI; + +namespace Spillgebees.SIRI.Models.Tests.Serialization; + +public class FixedValueSerializationTests +{ + [Test] + public void Should_serialize_required_fixed_discovery_version() + { + // arrange + var original = new ConnectionLinksDiscoveryRequestStructure + { + RequestTimestamp = DateTimeOffset.UnixEpoch, + RequestorRef = new ParticipantRefStructure { Value = "test:requestor" }, + }; + var serializer = new XmlSerializer(original.GetType()); + + // act + using var writer = new StringWriter(); + serializer.Serialize(writer, original); + var xml = XDocument.Parse(writer.ToString()); + + // assert + var version = xml.Root!.Attribute("version"); + version.Should().NotBeNull(); + version!.Value.Should().Be("2.1"); + typeof(ConnectionLinksDiscoveryRequestStructure).GetProperty(nameof(original.Version))!.CanWrite.Should().BeFalse(); + } + + [Test] + public void Should_include_optional_fixed_capability_only_when_requested() + { + // arrange + var original = new SituationExchangeServiceCapabilitiesStructureTopicFiltering + { + DefaultPreviewInterval = TimeSpan.FromHours(1), + }; + var serializer = new XmlSerializer(original.GetType()); + using var untouchedWriter = new StringWriter(); + serializer.Serialize(untouchedWriter, original); + + // act + original.IncludeFilterByLocationRef(); + using var writer = new StringWriter(); + serializer.Serialize(writer, original); + var xml = XDocument.Parse(writer.ToString()); + using var reader = new StringReader(writer.ToString()); + var result = (SituationExchangeServiceCapabilitiesStructureTopicFiltering)serializer.Deserialize(reader)!; + using var roundTripWriter = new StringWriter(); + serializer.Serialize(roundTripWriter, result); + + // assert + XDocument.Parse(untouchedWriter.ToString()).Descendants() + .Should().NotContain(element => element.Name.LocalName == "FilterByLocationRef"); + xml.Descendants().Single(element => element.Name.LocalName == "FilterByLocationRef").Value.Should().Be("true"); + XDocument.Parse(roundTripWriter.ToString()).Descendants() + .Single(element => element.Name.LocalName == "FilterByLocationRef").Value.Should().Be("true"); + } + + [Test] + public void Should_serialize_required_fixed_vehicle_monitoring_capability() + { + // arrange + var original = new VehicleMonitoringServiceCapabilitiesStructureTopicFiltering + { + DefaultPreviewInterval = TimeSpan.FromHours(1), + }; + var serializer = new XmlSerializer(original.GetType()); + + // act + using var writer = new StringWriter(); + serializer.Serialize(writer, original); + var xml = XDocument.Parse(writer.ToString()); + + // assert + var capability = xml.Root!.Element(XName.Get("FilterByVehicleMonitoringRef", "http://www.siri.org.uk/siri")); + capability.Should().NotBeNull(); + capability!.Value.Should().Be("true"); + } +} diff --git a/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/SiriSerializationTests.cs b/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/SiriSerializationTests.cs index ba51476..55259cd 100644 --- a/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/SiriSerializationTests.cs +++ b/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/SiriSerializationTests.cs @@ -153,13 +153,8 @@ public void Should_have_correct_xml_type_namespace_on_siri_types() xmlTypeAttr.Namespace.Should().Be("http://www.siri.org.uk/siri"); } - /// - /// Siri.Version defaults to "2.1" via backing field initialization. - /// With [DefaultValueAttribute] suppressed on string? properties, - /// XmlSerializer must include the default value in the serialized XML. - /// [Test] - public void Should_serialize_default_string_value_when_property_is_left_as_default() + public void Should_omit_untouched_default_string_value() { // arrange var serializer = new XmlSerializer(typeof(Siri)); @@ -174,7 +169,7 @@ public void Should_serialize_default_string_value_when_property_is_left_as_defau var deserialized = serializer.Deserialize(reader) as Siri; // assert - xml.Should().Contain("version=\"2.1\""); + xml.Should().NotContain("version=\"2.1\""); deserialized.Should().NotBeNull(); deserialized.Version.Should().Be("2.1"); } diff --git a/src/siri/Spillgebees.SIRI.Models.Tests/XsdTypeMappingTests.cs b/src/siri/Spillgebees.SIRI.Models.Tests/XsdTypeMappingTests.cs index e7dd629..ec50c7c 100644 --- a/src/siri/Spillgebees.SIRI.Models.Tests/XsdTypeMappingTests.cs +++ b/src/siri/Spillgebees.SIRI.Models.Tests/XsdTypeMappingTests.cs @@ -110,7 +110,7 @@ public void Should_not_have_default_value_attribute_on_nullable_string_property_ } [Test] - public void Should_still_have_default_value_attribute_on_value_type_property_with_xsd_default() + public void Should_track_assignment_instead_of_omitting_explicit_default_values() { // arrange var property = typeof(HalfOpenTimestampOutputRangeStructure) @@ -120,8 +120,8 @@ public void Should_still_have_default_value_attribute_on_value_type_property_wit var attr = property.GetCustomAttribute(); // assert - attr.Should().NotBeNull(); - attr!.Value.Should().Be(EndTimeStatusEnumeration.Undefined); + attr.Should().BeNull(); + property.DeclaringType!.GetMethod("ShouldSerialize" + property.Name).Should().NotBeNull(); } [Test]