Skip to content

Upgrade to NeTEx 2.0 schema - #270

Merged
vpaturet merged 27 commits into
masterfrom
upgrade-netex-2.0
Oct 15, 2026
Merged

vpaturet merged 27 commits into
masterfrom
upgrade-netex-2.0

Conversation

@vpaturet

@vpaturet vpaturet commented Feb 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Upgrade the NeTEx schema from version 1.16 to 2.0 and publish the result as a new artifact, org.entur:netex2-java-model, with a new base package, org.entur.netex2, so that it can live side by side with netex-java-model 2.x in the same application. The jar is also a JPMS module (merged in from #301), and xs:date is now mapped to java.time.LocalDate.

This lands as 3.0.0 (3.0.0-SNAPSHOT), continuing the version numbering of netex-java-model even though the artifactId changes.

Breaking changes

  1. Maven coordinates: org.entur:netex-java-model → org.entur:netex2-java-model. Release and hotfix workflow summaries, the README badges and the usage snippets point to the new artifact.
  2. Base package: org.rutebanken.* → org.entur.netex2.*:
    • generated model: org.rutebanken.netex.model → org.entur.netex2.model
    • XML adapters: org.rutebanken.util → org.entur.netex2.util (LocalDateTimeISO8601XmlAdapter, LocalDateXmlAdapter, LocalTimeISO8601XmlAdapter, VersionXmlAdapter)
    • org.rutebanken.netex.OmitNullsToStringStyle, org.rutebanken.netex.client.PublicationDeliveryClient, org.rutebanken.netex.validation.NeTExValidator → org.entur.netex2.{,client,validation}
    • The GML and SIRI packages are unchanged: net.opengis.gml._3, uk.org.siri.siri (see "Side-by-side use" below).
  3. JPMS module name: org.entur.netex.java.model (the Automatic-Module-Name of 2.x) → org.entur.netex2, now declared in module-info.java. Consumers with a module-info.java must update their requires.
  4. Java baseline: 11 → 21 (jdk.version; CI builds and publishes with Java 21).
  5. xs:date is mapped to java.time.LocalDate instead of LocalDateTime at midnight. This changes the type of 28 properties across 18 generated classes, e.g. OperatingDay.calendarDate, DayTypeAssignment.date, ServiceCalendar.fromDate/toDate, UsageValidityPeriod.startDate/endDate, Vehicle.registrationDate. xs:dateTime is still LocalDateTime. The new adapter accepts and ignores an optional time zone offset on input (2026-05-03+02:00, 2026-05-03Z), always writes yyyy-MM-dd, and no longer depends on the JVM's default offset.
  6. Generated model API follows NeTEx 2.0 (see "Test Updates" for the main differences callers will hit).
  7. Enumeration-typed properties are no longer generated as String. CXF XJC is upgraded from 4.0.2 to 4.2.0. 4.1.x moved to JAXB RI 4.0.6+ which fixes JAXB RI #1819; 4.2.0 is a dependency-bump release with the same JAXB RI 4.0.7 as 4.1.3 and generates an identical model. The 2.x line pinned 4.0.x to avoid this in a patch release. Affected properties:
    • PropertyOfDayStructure.weeksOfMonth: List<String> → List<WeekOfMonthEnumeration>
    • RechargingEquipmentProfile_VersionStructure.currentType and VehicleChargingEquipment_VersionStructure.currentType: String → CurrentTypeEnumeration
    • New enums CurrentTypeEnumeration, WeekOfMonthEnumeration and WeeksOfMonthEnumeration. No other generated field or method signature changes.
  8. NeTExValidator can no longer be constructed directly. Both constructors are private; use NeTExValidator.getNeTExValidator() or getNeTExValidator(NetexVersion), which cache one validator per version.
  9. org.entur.netex2.util is exported only to the JAXB modules (jakarta.xml.bind, org.glassfish.jaxb.core, org.glassfish.jaxb.runtime) and org.entur.netex2 is not exported at all. The XML adapters and OmitNullsToStringStyle are implementation details of the generated model. Consumers on the classpath are unaffected; consumers on the module path can no longer import them. OmitNullsToStringStyle is also final with a private constructor; use OmitNullsToStringStyle.INSTANCE.
  10. The cxf-xjc-runtime transitive dependency is gone. The generated code never referenced it (the toString plugin uses this library's own OmitNullsToStringStyle), so it was only leaked to consumers. The remaining runtime dependencies are jakarta.xml.bind-api, threeten-jaxb-core, jaxb-runtime, slf4j-api and commons-lang3.

Side-by-side use with netex-java-model 2.x

  • Classpath: works. Artifact, Java packages and module name are all distinct, so both versions can be on the classpath without conflicts in the NeTEx packages.
  • Module path: does not work. Both jars export net.opengis.gml._3 and uk.org.siri.siri, which is a split package and is rejected at module resolution. See open questions.

Changes

NeTEx 2.0 schema

  • The 2.0 XSD is downloaded from the TransmodelEcosystem/NeTEx repository (tag v2.0, previously entur/NeTEx master). The legacy schemas used for validation (1.08 - 1.15) are still downloaded from the Entur fork.

JAXB Bindings (bindings.xjb)

  • xs:date → java.time.LocalDate via LocalDateXmlAdapter (rewritten; see breaking change 5)
  • Package mapping and adapter references updated to org.entur.netex2.*
  • Updated XPath for TariffZone_ → TariffZone_Dummy (renamed in 2.0)
  • Removed obsolete HeadwayJourneyGroupGroup/Description binding (element removed in 2.0)
  • Added bindings to resolve ObjectFactory name collisions for bookingArrangements vs BookingArrangements in multiple XSD files
  • Removed a stray | character

JPMS module (from #301)

  • module org.entur.netex2 with requires transitive on jakarta.xml.bind, java.xml and org.apache.commons.lang3, because their types are part of the public API (JAXBException, QName/XMLGregorianCalendar on NeTExValidator, OmitNullsToStringStyle extends ToStringStyle).
  • The generated packages (org.entur.netex2.model, net.opengis.gml._3, uk.org.siri.siri) are opened to jakarta.xml.bind, org.glassfish.jaxb.runtime and org.apache.commons.lang3. JAXB field access and the reflective toString() both need this.
  • org.entur.netex2.util is exported only to the JAXB modules and org.entur.netex2 is not exported (breaking change 9). Surefire runs the tests on the module path, so this is covered by the test suite.
  • The Automatic-Module-Name manifest entry is removed.

@vpaturet
vpaturet force-pushed the upgrade-netex-2.0 branch 2 times, most recently from 11e1aef to 0e84185 Compare February 3, 2026 00:45
@sonarqubecloud

sonarqubecloud Bot commented Feb 3, 2026

Copy link
Copy Markdown

vpaturet added a commit to entur/OpenTripPlanner that referenced this pull request Feb 3, 2026
This update adapts OTP to the breaking changes in netex-java-model 3.0.0-SNAPSHOT
based on the NeTEx 2.0 schema. Key changes include:

Breaking API changes addressed:
- MultilingualString: Uses mixed content model - getContent() returns
  List<Serializable> instead of getValue() returning String. Added
  MultilingualStringMapper.getStringValue() helper method.
- Enum rename: AllVehicleModesOfTransportEnumeration renamed to
  AllPublicTransportModesEnumeration.
- Collection getter naming: Some methods renamed with _Dummy suffix
  (e.g., getLine_Dummy(), getRoute_Dummy()).
- PublicCode/PrivateCode: Now return structure types requiring .getValue()
  to get String.
- StopPlacesInFrame_RelStructure: Now returns List<StopPlace> directly
  instead of wrapped JAXBElements.
- DatedServiceJourney: getJourneyRef() returns single element instead of
  List, uses getReplacedJourneys() for replacements.
- ServiceAlterationEnumeration: New PROVISIONAL value added, mapped to
  PLANNED.

See: entur/netex-java-model#270
erlendnils1 pushed a commit to entur/netex-protobuf that referenced this pull request Feb 4, 2026
vpaturet added a commit to entur/OpenTripPlanner that referenced this pull request Feb 25, 2026
This update adapts OTP to the breaking changes in netex-java-model 3.0.0-SNAPSHOT
based on the NeTEx 2.0 schema. Key changes include:

Breaking API changes addressed:
- MultilingualString: Uses mixed content model - getContent() returns
  List<Serializable> instead of getValue() returning String. Added
  MultilingualStringMapper.getStringValue() helper method.
- Enum rename: AllVehicleModesOfTransportEnumeration renamed to
  AllPublicTransportModesEnumeration.
- Collection getter naming: Some methods renamed with _Dummy suffix
  (e.g., getLine_Dummy(), getRoute_Dummy()).
- PublicCode/PrivateCode: Now return structure types requiring .getValue()
  to get String.
- StopPlacesInFrame_RelStructure: Now returns List<StopPlace> directly
  instead of wrapped JAXBElements.
- DatedServiceJourney: getJourneyRef() returns single element instead of
  List, uses getReplacedJourneys() for replacements.
- ServiceAlterationEnumeration: New PROVISIONAL value added, mapped to
  PLANNED.

See: entur/netex-java-model#270
@vpaturet
vpaturet force-pushed the upgrade-netex-2.0 branch 2 times, most recently from f687f07 to 2c96d99 Compare September 24, 2026 12:50
vpaturet added a commit to entur/OpenTripPlanner that referenced this pull request Sep 25, 2026
This update adapts OTP to the breaking changes in netex-java-model 3.0.0-SNAPSHOT
based on the NeTEx 2.0 schema. Key changes include:

Breaking API changes addressed:
- MultilingualString: Uses mixed content model - getContent() returns
  List<Serializable> instead of getValue() returning String. Added
  MultilingualStringMapper.getStringValue() helper method.
- Enum rename: AllVehicleModesOfTransportEnumeration renamed to
  AllPublicTransportModesEnumeration.
- Collection getter naming: Some methods renamed with _Dummy suffix
  (e.g., getLine_Dummy(), getRoute_Dummy()).
- PublicCode/PrivateCode: Now return structure types requiring .getValue()
  to get String.
- StopPlacesInFrame_RelStructure: Now returns List<StopPlace> directly
  instead of wrapped JAXBElements.
- DatedServiceJourney: getJourneyRef() returns single element instead of
  List, uses getReplacedJourneys() for replacements.
- ServiceAlterationEnumeration: New PROVISIONAL value added, mapped to
  PLANNED.

See: entur/netex-java-model#270
vpaturet added a commit to entur/OpenTripPlanner that referenced this pull request Oct 13, 2026
The NeTEx 2.0 model is now published under new Maven coordinates,
org.entur:netex2-java-model, with the generated model relocated from
org.rutebanken.netex.model to org.entur.netex2.model.

Adapt OTP to the additional breaking changes in the latest snapshot:
- Maven coordinates and the version property renamed to netex2-java-model.
- All imports moved to org.entur.netex2.model.
- xs:date properties are now java.time.LocalDate instead of LocalDateTime
  at midnight: OperatingDay.calendarDate and DayTypeAssignment.date are
  consumed as LocalDate in OperatingDayMapper, TripPatternMapper and
  DayTypeAssignmentMapper. OperatingPeriod and ValidBetween keep
  LocalDateTime (xs:dateTime).

See: entur/netex-java-model#270
vpaturet added a commit to entur/antu that referenced this pull request Oct 13, 2026
…r xs:date

netex-java-model 3.0.0 is published as org.entur:netex2-java-model with the base package
org.entur.netex2 (entur/netex-java-model#270), and the netex-parser-java 5.0.0 and
netex-validator-java 13.0.0 snapshots now depend on it. Pin that artifact instead of the old one
and move every import from org.rutebanken.netex.* to org.entur.netex2.*.

The same release maps xs:date to LocalDate. OperatingDay.calendarDate and DayTypeAssignment.date
are the two such fields antu reads; they are widened to midnight in ServiceJourneyActiveDatesCollector
because NetexDataRepository in netex-validator-java and the report-scoped Redis caches keep
LocalDateTime, so neither the repository contract nor the cache wire format changes. The test
factory drops the opposite conversion it did for the 1.x model. OperatingPeriod from/to dates are
xs:dateTime and are unchanged.

The other breaking changes in that release (JPMS module name, typed enumeration properties,
private NeTExValidator constructors, non-exported util package) touch nothing antu uses.
vpaturet added a commit to entur/OpenTripPlanner that referenced this pull request Oct 15, 2026
This update adapts OTP to the breaking changes in netex-java-model 3.0.0-SNAPSHOT
based on the NeTEx 2.0 schema. Key changes include:

Breaking API changes addressed:
- MultilingualString: Uses mixed content model - getContent() returns
  List<Serializable> instead of getValue() returning String. Added
  MultilingualStringMapper.getStringValue() helper method.
- Enum rename: AllVehicleModesOfTransportEnumeration renamed to
  AllPublicTransportModesEnumeration.
- Collection getter naming: Some methods renamed with _Dummy suffix
  (e.g., getLine_Dummy(), getRoute_Dummy()).
- PublicCode/PrivateCode: Now return structure types requiring .getValue()
  to get String.
- StopPlacesInFrame_RelStructure: Now returns List<StopPlace> directly
  instead of wrapped JAXBElements.
- DatedServiceJourney: getJourneyRef() returns single element instead of
  List, uses getReplacedJourneys() for replacements.
- ServiceAlterationEnumeration: New PROVISIONAL value added, mapped to
  PLANNED.

See: entur/netex-java-model#270
vpaturet added a commit to entur/OpenTripPlanner that referenced this pull request Oct 15, 2026
The NeTEx 2.0 model is now published under new Maven coordinates,
org.entur:netex2-java-model, with the generated model relocated from
org.rutebanken.netex.model to org.entur.netex2.model.

Adapt OTP to the additional breaking changes in the latest snapshot:
- Maven coordinates and the version property renamed to netex2-java-model.
- All imports moved to org.entur.netex2.model.
- xs:date properties are now java.time.LocalDate instead of LocalDateTime
  at midnight: OperatingDay.calendarDate and DayTypeAssignment.date are
  consumed as LocalDate in OperatingDayMapper, TripPatternMapper and
  DayTypeAssignmentMapper. OperatingPeriod and ValidBetween keep
  LocalDateTime (xs:dateTime).

See: entur/netex-java-model#270
vpaturet and others added 3 commits October 15, 2026 14:23
- Update bindings.xjb for NeTEx 2.0 schema changes:
  - TariffZone_ renamed to TariffZone_Dummy
  - Remove obsolete HeadwayJourneyGroupGroup/Description binding
  - Add bindings to resolve ObjectFactory collisions for bookingArrangements

- Update tests for NeTEx 2.0 API changes, including the regression tests
  added by #278 (Chouette/OTP/netex-validator-java use cases):
  - MultilingualString now uses mixed content (withContent/getContent)
  - Collection getters renamed with _Dummy suffix
  - StopPlacesInFrame returns List<StopPlace> directly
  - AllVehicleModesOfTransportEnumeration -> AllPublicTransportModesEnumeration
  - PublicCode/PrivateCode now use structure types
  - Replace removed DatedCall tests with AvailabilityCondition tests
  - Add helper method getStringValue() for MultilingualString

- Build and publish snapshots with Java 21 in CI

- Update the README to state NeTEx 2.0, Java 21 and the CEN schema source
The NeTEx GitHub repository moved from NeTEx-CEN/NeTEx to
TransmodelEcosystem/NeTEx.
Adds module-info.java declaring org.entur.netex.java.model, replacing
the Automatic-Module-Name manifest entry. requires transitive is used
for jakarta.xml.bind, java.xml and org.apache.commons.lang3 since their
types leak into the public API (JAXBException, QName/XMLGregorianCalendar,
OmitNullsToStringStyle extends ToStringStyle).

The generated model packages (org.rutebanken.netex.model, net.opengis.gml._3,
uk.org.siri.siri) use field-level JAXB access, so they're opened to
jakarta.xml.bind, org.glassfish.jaxb.runtime and org.apache.commons.lang3 -
verified by running with each opens target removed until `mvn clean test`
passed cleanly on the module path (Surefire auto-detects module-info.java
and runs on the module path).

Also renames org.rutebanken.util to org.rutebanken.netex.util for
consistency with the other org.rutebanken.netex.* packages.
The NeTEx 2.0 model is published as a separate artifact so that it can
coexist with the NeTEx 1.x model (org.entur:netex-java-model) on the same
classpath.
The generated model now lives in org.entur.netex2.model and the hand-written
utilities in org.entur.netex2, org.entur.netex2.client, org.entur.netex2.util
and org.entur.netex2.validation. The JPMS module is renamed accordingly to
org.entur.netex2 so that the module name matches its root package.
xsd:date elements (OperatingDay/CalendarDate, DayTypeAssignment/Date,
ServiceCalendar/FromDate and ToDate, ...) were previously bound to
LocalDateTime at midnight. They are now bound to LocalDate.

LocalDateXmlAdapter accepts an optional trailing zone offset when
unmarshalling, as permitted by xsd:date, and ignores it. Dates are always
marshalled as yyyy-MM-dd. xsd:dateTime elements are still bound to
LocalDateTime.
The 1.16.1 schema is only reachable through v1_16, which validates against it.
The deprecated constant was added for the 2.0.16.x hotfixes and is not needed
in the 3.0 API.
The manual release and hotfix workflows still set up Java 11, but the
enforcer plugin now requires Java 21.
The pin existed because JUnit 6 requires Java 17 and the library
targeted Java 11. The baseline is now Java 21.
Use the new artifact name as title, state the actual range of legacy
validation schemas, credit threeten-jaxb-core only for Duration, and
mention the SIRI package and the JPMS module name.
CXF XJC 4.1.x bundles JAXB RI 4.0.6, which generates xsd:list and
simple-typed properties with an enumeration type as that enumeration
instead of String (JAXB RI #1819). This was pinned on the 2.x line to
avoid a source-incompatible change in a patch release; 3.0 is a
breaking release, so the type-safe enums are adopted here. 4.2.0 is a
dependency-bump release over 4.1.3 with the same JAXB RI 4.0.7 and
generates the same model.

Generated API changes:
- PropertyOfDayStructure.weeksOfMonth: List<String> -> List<WeekOfMonthEnumeration>
- RechargingEquipmentProfile_VersionStructure.currentType: String -> CurrentTypeEnumeration
- VehicleChargingEquipment_VersionStructure.currentType: String -> CurrentTypeEnumeration
- New enums CurrentTypeEnumeration, WeekOfMonthEnumeration, WeeksOfMonthEnumeration
The generated code references nothing under org.apache.cxf: the
toString plugin uses OmitNullsToStringStyle from this library. The
runtime-scoped jar was only leaked to consumers.
The adapters in this package are referenced by the generated model and
instantiated reflectively by the JAXB runtime; nothing else needs them.
Instances are cached per version and obtained through the static
getNeTExValidator methods, which the constructor Javadoc already asked
callers to use.
OmitNullsToStringStyle is only passed by the generated toString()
methods to commons-lang3, which calls it through the ToStringStyle
type. No module needs the package exported.
Releases and snapshots are published by JReleaser through the GitHub
workflows. The jfrog snapshot repository, the wagon-ssh-external
extension, the maven-release-plugin configuration (which referenced
profiles that no longer exist) and the unused nexus-staging, gpg and
dependency plugin version properties are left over from the previous
release process. The sonar profile no longer redeclares jdk.version.
The build only removes the schema folders it is about to download
again, so folders from earlier builds (1.15.x, 1.16) ended up in
locally built jars.
- OmitNullsToStringStyle: annotate serialVersionUID with @serial
- LocalTimeISO8601XmlAdapter: Javadoc paragraph break instead of a
  blank line, which Javadoc ignores
- FastLocalDateTimeParser: readDate always started at 0, so the start
  parameter is dropped; hasValidZoneSuffix was only ever called negated
  and is inverted into hasInvalidZoneSuffix
The style is only meant to be used through INSTANCE. This also removes
the javac this-escape warning about the overridable setters called
from the constructor.
- Replace the 32 string-literal concatenations of XML documents with
  text blocks (S6126). Trailing spaces inside the documents are kept
  with \s so the values are unchanged.
- Stream.toList() instead of collect(Collectors.toList()) (S6204)
- Drop redundant public modifiers from JUnit 5 test classes and
  methods (S5786)
- NeTExValidatorTest: initialise the validator in @beforeeach instead
  of an empty constructor (S1186), name the XML constant in upper case
  (S115) and the local validator variable without digits (S117)
- MarshalUnmarshalTest: remove an unused import (S1128) and a
  String.format with constant arguments (S9397)
parseDefaulting(OFFSET_SECONDS, <JVM default offset at class load>)
had no effect: LocalDateTime.from and LocalTime.from never read the
offset, so an offset in the input is ignored whether it is present,
absent or defaulted. Parsing and formatting results are unchanged.
The adapters now document that behaviour, as LocalDateXmlAdapter
already did.
maven-release-plugin, buildnumber-maven-plugin and maven-shade-plugin
are not used, so their backup and output files never appear.
Java 21 SequencedCollection accessor, as suggested by IntelliJ.
NeTEx 2.0 names its abstract substitution-group heads with a _Dummy
suffix (DayType_Dummy is the head of DayType), where 1.x used a trailing
underscore. xjc leaked the suffix into the generated API as
getDayType_Dummy(), getDayTypeRefOrDayType_Dummy(), createDayType_Dummy()
and classes such as MediumAccessDevice_Dummy.

Add bin/generate-dummy-bindings.py, which derives one JAXB binding per
leak from the schema and writes them into a marked block of bindings.xjb:
properties drop the suffix (getDayType(), getDayTypeRefOrDayType()),
while factory methods and the few classes generated for heads keep a
trailing underscore as in netex-java-model 2.x (createDayType_(),
MediumAccessDevice_), since dropping it there collides with the factory
method of the concrete element.

The hand-written TariffZone binding is covered by the generated block.
Tests and docs are updated accordingly.
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
71.2% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@vpaturet
vpaturet merged commit c42e231 into master Oct 15, 2026
3 of 4 checks passed
@vpaturet
vpaturet deleted the upgrade-netex-2.0 branch October 15, 2026 12:32
vpaturet added a commit to entur/OpenTripPlanner that referenced this pull request Oct 16, 2026
This update adapts OTP to the breaking changes in netex-java-model 3.0.0-SNAPSHOT
based on the NeTEx 2.0 schema. Key changes include:

Breaking API changes addressed:
- MultilingualString: Uses mixed content model - getContent() returns
  List<Serializable> instead of getValue() returning String. Added
  MultilingualStringMapper.getStringValue() helper method.
- Enum rename: AllVehicleModesOfTransportEnumeration renamed to
  AllPublicTransportModesEnumeration.
- Collection getter naming: Some methods renamed with _Dummy suffix
  (e.g., getLine_Dummy(), getRoute_Dummy()).
- PublicCode/PrivateCode: Now return structure types requiring .getValue()
  to get String.
- StopPlacesInFrame_RelStructure: Now returns List<StopPlace> directly
  instead of wrapped JAXBElements.
- DatedServiceJourney: getJourneyRef() returns single element instead of
  List, uses getReplacedJourneys() for replacements.
- ServiceAlterationEnumeration: New PROVISIONAL value added, mapped to
  PLANNED.

See: entur/netex-java-model#270
vpaturet added a commit to entur/OpenTripPlanner that referenced this pull request Oct 16, 2026
The NeTEx 2.0 model is now published under new Maven coordinates,
org.entur:netex2-java-model, with the generated model relocated from
org.rutebanken.netex.model to org.entur.netex2.model.

Adapt OTP to the additional breaking changes in the latest snapshot:
- Maven coordinates and the version property renamed to netex2-java-model.
- All imports moved to org.entur.netex2.model.
- xs:date properties are now java.time.LocalDate instead of LocalDateTime
  at midnight: OperatingDay.calendarDate and DayTypeAssignment.date are
  consumed as LocalDate in OperatingDayMapper, TripPatternMapper and
  DayTypeAssignmentMapper. OperatingPeriod and ValidBetween keep
  LocalDateTime (xs:dateTime).

See: entur/netex-java-model#270
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants