diff --git a/.github/workflows/build-test-publish.yml b/.github/workflows/build-test-publish.yml
index 0b914e8..57b59f7 100644
--- a/.github/workflows/build-test-publish.yml
+++ b/.github/workflows/build-test-publish.yml
@@ -43,10 +43,13 @@ jobs:
dotnet pack src/netex/Spillgebees.NeTEx.Models.V1_2_3/Spillgebees.NeTEx.Models.V1_2_3.csproj --configuration Release --no-build --output package
dotnet pack src/netex/Spillgebees.NeTEx.Models.V1_3_0/Spillgebees.NeTEx.Models.V1_3_0.csproj --configuration Release --no-build --output package
dotnet pack src/netex/Spillgebees.NeTEx.Models.V1_3_1/Spillgebees.NeTEx.Models.V1_3_1.csproj --configuration Release --no-build --output package
+ dotnet pack src/netex/Spillgebees.NeTEx.Models.V1_3_2/Spillgebees.NeTEx.Models.V1_3_2.csproj --configuration Release --no-build --output package
dotnet pack src/netex/Spillgebees.NeTEx.Models.V2_0_0/Spillgebees.NeTEx.Models.V2_0_0.csproj --configuration Release --no-build --output package
dotnet pack src/netex/Spillgebees.NeTEx.Models/Spillgebees.NeTEx.Models.csproj --configuration Release --no-build --output package
dotnet pack src/siri/Spillgebees.SIRI.Models.V2_1/Spillgebees.SIRI.Models.V2_1.csproj --configuration Release --no-build --output package
dotnet pack src/siri/Spillgebees.SIRI.Models.V2_2/Spillgebees.SIRI.Models.V2_2.csproj --configuration Release --no-build --output package
+ dotnet pack src/siri/Spillgebees.SIRI.Models.V2_2_1/Spillgebees.SIRI.Models.V2_2_1.csproj --configuration Release --no-build --output package
+ dotnet pack src/siri/Spillgebees.SIRI.Models.V2_3/Spillgebees.SIRI.Models.V2_3.csproj --configuration Release --no-build --output package
dotnet pack src/siri/Spillgebees.SIRI.Models/Spillgebees.SIRI.Models.csproj --configuration Release --no-build --output package
dotnet pack src/generator/Spillgebees.Transmodel.Generator/Spillgebees.Transmodel.Generator.csproj --configuration Release --no-build --output package
diff --git a/Spillgebees.Transmodel.slnx b/Spillgebees.Transmodel.slnx
index ea20df6..54888ad 100644
--- a/Spillgebees.Transmodel.slnx
+++ b/Spillgebees.Transmodel.slnx
@@ -22,6 +22,7 @@
+
@@ -30,6 +31,8 @@
+
+
diff --git a/src/generator/Spillgebees.Transmodel.Generator/Configuration/NetexDefaults.cs b/src/generator/Spillgebees.Transmodel.Generator/Configuration/NetexDefaults.cs
index 5519f55..a2114d0 100644
--- a/src/generator/Spillgebees.Transmodel.Generator/Configuration/NetexDefaults.cs
+++ b/src/generator/Spillgebees.Transmodel.Generator/Configuration/NetexDefaults.cs
@@ -2,7 +2,7 @@ namespace Spillgebees.Transmodel.Generator.Configuration;
public static class NetexDefaults
{
- public const string DefaultVersion = "v1.3.1";
+ public const string DefaultVersion = "v2.0.0";
public const string DefaultNamespace = "NeTEx.Models";
public const string NetexXmlNamespace = "http://www.netex.org.uk/netex";
diff --git a/src/generator/Spillgebees.Transmodel.Generator/Configuration/SiriDefaults.cs b/src/generator/Spillgebees.Transmodel.Generator/Configuration/SiriDefaults.cs
index dc3bb3e..5f4bfd2 100644
--- a/src/generator/Spillgebees.Transmodel.Generator/Configuration/SiriDefaults.cs
+++ b/src/generator/Spillgebees.Transmodel.Generator/Configuration/SiriDefaults.cs
@@ -2,7 +2,7 @@ namespace Spillgebees.Transmodel.Generator.Configuration;
public static class SiriDefaults
{
- public const string DefaultVersion = "v2.2";
+ public const string DefaultVersion = "v2.3";
public const string DefaultNamespace = "SIRI.Models";
public const string SiriXmlNamespace = "http://www.siri.org.uk/siri";
diff --git a/src/netex/Spillgebees.NeTEx.Models.Tests/Serialization/Netex/ReleaseRegressionTests.cs b/src/netex/Spillgebees.NeTEx.Models.Tests/Serialization/Netex/ReleaseRegressionTests.cs
new file mode 100644
index 0000000..c26f080
--- /dev/null
+++ b/src/netex/Spillgebees.NeTEx.Models.Tests/Serialization/Netex/ReleaseRegressionTests.cs
@@ -0,0 +1,93 @@
+using System.Xml.Linq;
+using System.Xml.Serialization;
+using AwesomeAssertions;
+using Netex132 = Spillgebees.NeTEx.Models.V1_3_2.NeTEx;
+
+namespace Spillgebees.NeTEx.Models.Tests.Serialization.Netex;
+
+public class ReleaseRegressionTests
+{
+ [Test]
+ public void Should_keep_the_netex_namespace_for_v1_3_2()
+ {
+ // arrange
+ var type = typeof(Netex132.StopPlace);
+
+ // act
+ var xmlType = type.GetCustomAttributes(typeof(XmlTypeAttribute), false)
+ .Cast().Single();
+
+ // assert
+ xmlType.Namespace.Should().Be("http://www.netex.org.uk/netex");
+ }
+ [Test]
+ public void Should_round_trip_type_of_frame_reference_attributes()
+ {
+ // arrange
+ var original = new Netex132.ClassInFrameStructure
+ {
+ TypeOfFrameRef = new Netex132.TypeOfFrameRefStructure { Ref = "frame-type:1", Version = "1" },
+ };
+ var serializer = new XmlSerializer(typeof(Netex132.ClassInFrameStructure));
+
+ // act
+ using var writer = new StringWriter();
+ serializer.Serialize(writer, original);
+ using var reader = new StringReader(writer.ToString());
+ var result = (Netex132.ClassInFrameStructure)serializer.Deserialize(reader)!;
+
+ // assert
+ result.TypeOfFrameRef.Should().NotBeNull();
+ result.TypeOfFrameRef.Ref.Should().Be("frame-type:1");
+ result.TypeOfFrameRef.Version.Should().Be("1");
+ XDocument.Parse(writer.ToString()).Descendants()
+ .Single(element => element.Name.LocalName == "TypeOfFrameRef")
+ .Attribute("ref")!.Value.Should().Be("frame-type:1");
+ }
+
+ [Test]
+ public void Should_round_trip_route_instruction_references()
+ {
+ // arrange
+ var original = new Netex132.RouteInstructionsRelStructure
+ {
+ RouteInstructionRef =
+ [
+ new Netex132.RouteInstructionRefStructure { Ref = "instruction:1", Version = "1" },
+ new Netex132.RouteInstructionRefStructure { Ref = "instruction:2", Version = "1" },
+ ],
+ };
+ var serializer = new XmlSerializer(typeof(Netex132.RouteInstructionsRelStructure));
+
+ // act
+ using var writer = new StringWriter();
+ serializer.Serialize(writer, original);
+ using var reader = new StringReader(writer.ToString());
+ var result = (Netex132.RouteInstructionsRelStructure)serializer.Deserialize(reader)!;
+
+ // assert
+ result.RouteInstructionRef.Should().HaveCount(2);
+ result.RouteInstructionRef!.Select(reference => reference.Ref)
+ .Should().Equal("instruction:1", "instruction:2");
+ result.RouteInstruction.Should().BeNullOrEmpty();
+ }
+
+ [Test]
+ public void Should_deserialize_concrete_luggage_locker_equipment()
+ {
+ // arrange
+ const string xml = """
+
+ """;
+ var serializer = new XmlSerializer(typeof(Netex132.LuggageLockerEquipment));
+
+ // act
+ using var reader = new StringReader(xml);
+ var result = (Netex132.LuggageLockerEquipment)serializer.Deserialize(reader)!;
+
+ // assert
+ result.Id.Should().Be("locker:1");
+ result.Version.Should().Be("1");
+ typeof(Netex132.LuggageLockerEquipment).IsAbstract.Should().BeFalse();
+ }
+}
diff --git a/src/netex/Spillgebees.NeTEx.Models.Tests/Spillgebees.NeTEx.Models.Tests.csproj b/src/netex/Spillgebees.NeTEx.Models.Tests/Spillgebees.NeTEx.Models.Tests.csproj
index 97a3dfa..a5c5250 100644
--- a/src/netex/Spillgebees.NeTEx.Models.Tests/Spillgebees.NeTEx.Models.Tests.csproj
+++ b/src/netex/Spillgebees.NeTEx.Models.Tests/Spillgebees.NeTEx.Models.Tests.csproj
@@ -17,6 +17,7 @@
+
diff --git a/src/netex/Spillgebees.NeTEx.Models.V1_3_2/Spillgebees.NeTEx.Models.V1_3_2.csproj b/src/netex/Spillgebees.NeTEx.Models.V1_3_2/Spillgebees.NeTEx.Models.V1_3_2.csproj
new file mode 100644
index 0000000..7bab17f
--- /dev/null
+++ b/src/netex/Spillgebees.NeTEx.Models.V1_3_2/Spillgebees.NeTEx.Models.V1_3_2.csproj
@@ -0,0 +1,9 @@
+
+
+
+
+
+ v1.3.2
+ Pre-generated C# XML bindings for NeTEx (Network Timetable Exchange) $(NetexVersion) and associated SIRI/GML schemas.
+
+
diff --git a/src/netex/Spillgebees.NeTEx.Models/Spillgebees.NeTEx.Models.csproj b/src/netex/Spillgebees.NeTEx.Models/Spillgebees.NeTEx.Models.csproj
index 36ab411..c5af0a4 100644
--- a/src/netex/Spillgebees.NeTEx.Models/Spillgebees.NeTEx.Models.csproj
+++ b/src/netex/Spillgebees.NeTEx.Models/Spillgebees.NeTEx.Models.csproj
@@ -12,6 +12,7 @@
+
diff --git a/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/ReleaseRegressionTests.cs b/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/ReleaseRegressionTests.cs
new file mode 100644
index 0000000..148de38
--- /dev/null
+++ b/src/siri/Spillgebees.SIRI.Models.Tests/Serialization/ReleaseRegressionTests.cs
@@ -0,0 +1,138 @@
+using System.Xml;
+using System.Xml.Linq;
+using System.Xml.Schema;
+using System.Xml.Serialization;
+using AwesomeAssertions;
+using Siri221 = Spillgebees.SIRI.Models.V2_2_1.SIRI;
+using Siri23 = Spillgebees.SIRI.Models.V2_3.SIRI;
+
+namespace Spillgebees.SIRI.Models.Tests.Serialization;
+
+public class ReleaseRegressionTests
+{
+ [Test]
+ public void Should_keep_the_siri_namespace_for_new_releases()
+ {
+ // arrange
+ var types = new[] { typeof(Siri221.Siri), typeof(Siri23.Siri) };
+
+ // act
+ var namespaces = types.Select(type => type.GetCustomAttributes(typeof(XmlTypeAttribute), false)
+ .Cast().Single().Namespace);
+
+ // assert
+ namespaces.Should().OnlyContain(value => value == "http://www.siri.org.uk/siri");
+ }
+ [Test]
+ public void Should_round_trip_corrected_driver_scope_in_v2_2_1()
+ {
+ // arrange
+ const string xml = """
+
+ message:1
+ driver:1
+
+ """;
+ var serializer = new XmlSerializer(typeof(Siri221.DriverMessageStructure), "http://www.siri.org.uk/siri");
+
+ // act
+ using var reader = new StringReader(xml);
+ var message = (Siri221.DriverMessageStructure)serializer.Deserialize(reader)!;
+ using var writer = new StringWriter();
+ serializer.Serialize(writer, message);
+
+ // assert
+ message.DriverScope.EmployeeRef.Should().Be("driver:1");
+ writer.ToString().Should().Contain("").And.NotContain("DiverScope");
+ }
+
+ [Test]
+ [Arguments(true)]
+ [Arguments(false)]
+ public void Should_round_trip_dead_run_flag_in_v2_3(bool isDeadRun)
+ {
+ // arrange
+ var original = new Siri23.TargetedVehicleJourneyStructure
+ {
+ LineRef = new Siri23.LineRefStructure { Value = "line:1" },
+ DirectionRef = new Siri23.DirectionRefStructure { Value = "outbound" },
+ IsDeadRun = isDeadRun,
+ };
+ var serializer = new XmlSerializer(typeof(Siri23.TargetedVehicleJourneyStructure));
+
+ // act
+ using var writer = new StringWriter();
+ serializer.Serialize(writer, original);
+ using var reader = new StringReader(writer.ToString());
+ var result = (Siri23.TargetedVehicleJourneyStructure)serializer.Deserialize(reader)!;
+
+ // assert
+ result.IsDeadRun.Should().Be(isDeadRun);
+ XDocument.Parse(writer.ToString()).Descendants()
+ .Single(element => element.Name.LocalName == "IsDeadRun").Value
+ .Should().Be(XmlConvert.ToString(isDeadRun));
+ }
+
+ [Test]
+ public void Should_serialize_action_keywords_as_one_schema_valid_token_list()
+ {
+ // arrange
+ var original = new Siri23.ParameterisedActionStructure { Keywords = ["delay", "rail"] };
+ var serializer = new XmlSerializer(typeof(Siri23.ParameterisedActionStructure));
+ const string schema = """
+
+
+
+ """;
+ var schemas = new XmlSchemaSet();
+ using var schemaReader = new StringReader(schema);
+ schemas.Add("http://www.siri.org.uk/siri", XmlReader.Create(schemaReader));
+
+ // act
+ using var writer = new StringWriter();
+ serializer.Serialize(writer, original);
+ var keywords = XDocument.Parse(writer.ToString()).Descendants()
+ .Where(element => element.Name.LocalName == "Keywords").ToList();
+
+ // assert
+ keywords.Should().ContainSingle();
+ keywords[0].Value.Should().Be("delay rail");
+ var validationErrors = new List();
+ new XDocument(new XElement(keywords[0])).Validate(schemas, (_, error) => validationErrors.Add(error.Message));
+ validationErrors.Should().BeEmpty();
+ using var reader = new StringReader(writer.ToString());
+ var result = (Siri23.ParameterisedActionStructure)serializer.Deserialize(reader)!;
+ result.Keywords.Should().Equal("delay", "rail");
+ }
+ [Test]
+ public void Should_round_trip_passenger_information_periods_in_v2_3()
+ {
+ // arrange
+ var start = new DateTimeOffset(2026, 10, 3, 8, 0, 0, TimeSpan.Zero);
+ var original = new Siri23.PassengerInformationActionStructure
+ {
+ ActionRef = new Siri23.EntryQualifierStructure { Value = "action:1" },
+ RecordedAtTime = start,
+ Period =
+ [
+ new Siri23.HalfOpenTimestampOutputRangeStructure { StartTime = start, EndTime = start.AddHours(2) },
+ new Siri23.HalfOpenTimestampOutputRangeStructure { StartTime = start.AddDays(1) },
+ ],
+ };
+ var serializer = new XmlSerializer(typeof(Siri23.PassengerInformationActionStructure));
+
+ // act
+ using var writer = new StringWriter();
+ serializer.Serialize(writer, original);
+ using var reader = new StringReader(writer.ToString());
+ var result = (Siri23.PassengerInformationActionStructure)serializer.Deserialize(reader)!;
+
+ // assert
+ result.Period.Should().HaveCount(2);
+ result.Period![0].StartTime.Should().Be(start);
+ result.Period[0].EndTime.Should().Be(start.AddHours(2));
+ result.Period[1].StartTime.Should().Be(start.AddDays(1));
+ result.Period[1].EndTime.Should().BeNull();
+ }
+}
diff --git a/src/siri/Spillgebees.SIRI.Models.Tests/Spillgebees.SIRI.Models.Tests.csproj b/src/siri/Spillgebees.SIRI.Models.Tests/Spillgebees.SIRI.Models.Tests.csproj
index 79eadc2..dcbe706 100644
--- a/src/siri/Spillgebees.SIRI.Models.Tests/Spillgebees.SIRI.Models.Tests.csproj
+++ b/src/siri/Spillgebees.SIRI.Models.Tests/Spillgebees.SIRI.Models.Tests.csproj
@@ -14,5 +14,7 @@
+
+
diff --git a/src/siri/Spillgebees.SIRI.Models.V2_2_1/Spillgebees.SIRI.Models.V2_2_1.csproj b/src/siri/Spillgebees.SIRI.Models.V2_2_1/Spillgebees.SIRI.Models.V2_2_1.csproj
new file mode 100644
index 0000000..6a89f91
--- /dev/null
+++ b/src/siri/Spillgebees.SIRI.Models.V2_2_1/Spillgebees.SIRI.Models.V2_2_1.csproj
@@ -0,0 +1,9 @@
+
+
+
+
+
+ v2.2.1
+ Pre-generated C# XML bindings for SIRI (Service Interface for Real-time Information) $(SiriVersion) including IFOPT, ACSB, DATEX2, and WSDL schemas.
+
+
diff --git a/src/siri/Spillgebees.SIRI.Models.V2_3/Spillgebees.SIRI.Models.V2_3.csproj b/src/siri/Spillgebees.SIRI.Models.V2_3/Spillgebees.SIRI.Models.V2_3.csproj
new file mode 100644
index 0000000..e64c017
--- /dev/null
+++ b/src/siri/Spillgebees.SIRI.Models.V2_3/Spillgebees.SIRI.Models.V2_3.csproj
@@ -0,0 +1,9 @@
+
+
+
+
+
+ v2.3
+ Pre-generated C# XML bindings for SIRI (Service Interface for Real-time Information) $(SiriVersion) including IFOPT, ACSB, DATEX2, and WSDL schemas.
+
+
diff --git a/src/siri/Spillgebees.SIRI.Models/Spillgebees.SIRI.Models.csproj b/src/siri/Spillgebees.SIRI.Models/Spillgebees.SIRI.Models.csproj
index 069ad19..87701fc 100644
--- a/src/siri/Spillgebees.SIRI.Models/Spillgebees.SIRI.Models.csproj
+++ b/src/siri/Spillgebees.SIRI.Models/Spillgebees.SIRI.Models.csproj
@@ -9,5 +9,7 @@
+
+