From f935f6b09299fa9ca19a6439fcde274dc73c8dd0 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Thu, 10 Sep 2026 12:37:12 +0200 Subject: [PATCH 1/4] Pin NeTEx XSD download to entur/NeTEx tag v1.0.16.1 and set netexVersion to 1.16.1 Includes the ContractRef / ResponsibilitySet AssociatedContract change from entur/NeTEx#63. The 1.16 schema is still downloaded for validation. (cherry picked from commit a23f0297fbf4fbc0f136fee86f6c24d26b4e7979) --- README.md | 4 ++-- pom.xml | 22 ++++++++++++++++--- .../netex/validation/NeTExValidator.java | 5 +++-- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4520b5ab..86ffbe40 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Java model classes generated from [NeTEx](https://github.com/entur/NeTEx) (Netwo This library downloads the NeTEx XSD files from the [Entur NeTEx fork](https://github.com/entur/NeTEx) and compiles them into JAXB-annotated Java model classes at build time. The generated classes support full marshalling and unmarshalling of NeTEx XML documents, including schema validation against multiple NeTEx versions. -- **NeTEx version**: 1.16 (model generation), with legacy validation support back to 1.07 +- **NeTEx version**: 1.16.1 (model generation), with legacy validation support back to 1.07 - **JAXB**: Jakarta XML Bind 4.x (Jakarta EE 9+) - **Generated package**: `org.rutebanken.netex.model` - **GML types package**: `net.opengis.gml._3` @@ -103,7 +103,7 @@ The XSD files are downloaded from GitHub during the `generate-sources` phase and - Property name conflict resolution - Package mapping to `org.rutebanken.netex.model` -**Schema validation**: `NeTExValidator` supports validation against NeTEx versions 1.07 through 1.16. +**Schema validation**: `NeTExValidator` supports validation against NeTEx versions 1.07 through 1.16.1. **Note on GML types**: This library generates classes under `net.opengis.gml._3` as part of the NeTEx model. diff --git a/pom.xml b/pom.xml index c3d8dbba..af003bbc 100644 --- a/pom.xml +++ b/pom.xml @@ -69,9 +69,9 @@ 11 NeTEx - master + tags/v1.0.16.1 https://github.com/entur/${netexRepoName} - 1.16 + 1.16.1 4.0.5 @@ -285,7 +285,7 @@ ${netexGithubUrl}/archive/${netexBranch}.zip src/main/resources/xsd/${netexVersion} - NeTEx-${netexBranch}/xsd/* + NeTEx-tags-v1.0.16.1/xsd/* ./bin/netex-download-extract.sh @@ -305,6 +305,22 @@ + + + download-extract-legacy-1.16 + generate-resources + + exec + + + + ${netexGithubUrl}/archive/tags/v1.0.16.zip + src/main/resources/xsd/1.16 + NeTEx-tags-v1.0.16/xsd/* + + ./bin/netex-download-extract.sh + + download-extract-legacy-1.15 diff --git a/src/main/java/org/rutebanken/netex/validation/NeTExValidator.java b/src/main/java/org/rutebanken/netex/validation/NeTExValidator.java index 4b3f4a83..feedffcf 100644 --- a/src/main/java/org/rutebanken/netex/validation/NeTExValidator.java +++ b/src/main/java/org/rutebanken/netex/validation/NeTExValidator.java @@ -44,7 +44,8 @@ public enum NetexVersion { v1_13 ("1.13"), v1_14 ("1.14"), v1_15 ("1.15"), - v1_16 ("1.16"); + v1_16 ("1.16"), + v1_16_1 ("1.16.1"); private final String folderName; @@ -59,7 +60,7 @@ public String toString() { } private final Schema neTExSchema; - public static final NetexVersion LATEST = NetexVersion.v1_16; + public static final NetexVersion LATEST = NetexVersion.v1_16_1; private static final Map VALIDATORS_PER_VERSION = new EnumMap<>(NetexVersion.class); From 3fade5b91f0511fa9c8313996221a3e0333a2c05 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Thu, 10 Sep 2026 12:37:12 +0200 Subject: [PATCH 2/4] Align bindings.xjb schemaLocation paths with netexVersion 1.16.1 (cherry picked from commit 1013129e847d233e3e303b100a564f209d62178e) --- bindings.xjb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bindings.xjb b/bindings.xjb index 089dc6c0..ffcaccde 100644 --- a/bindings.xjb +++ b/bindings.xjb @@ -14,14 +14,14 @@ -| +| + schemaLocation="./src/main/resources/xsd/1.16.1/netex_framework/netex_genericFramework/netex_organisation_version.xsd"> @@ -30,35 +30,35 @@ + schemaLocation="./src/main/resources/xsd/1.16.1/netex_part_3/part3_fares/netex_salesOfferPackage_version.xsd"> + schemaLocation="./src/main/resources/xsd/1.16.1/netex_part_2/part2_journeyTimes/netex_vehicleJourneyFrequency_version.xsd"> + schemaLocation="./src/main/resources/xsd/1.16.1/netex_part_3/part3_salesTransactions/netex_salesContract_version.xsd"> + schemaLocation="./src/main/resources/xsd/1.16.1/netex_part_3/part3_salesTransactions/netex_retailConsortium_version.xsd"> + schemaLocation="./src/main/resources/xsd/1.16.1/netex_framework/netex_responsibility/netex_version_support.xsd"> @@ -71,7 +71,7 @@ + schemaLocation="./src/main/resources/xsd/1.16.1/netex_framework/netex_genericFramework/netex_zone_version.xsd"> From e950c41599737d62464f29cf96d137beb935e4d0 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Thu, 10 Sep 2026 12:45:16 +0200 Subject: [PATCH 3/4] Add tests for ResponsibilityRoleAssignment AssociatedContract ContractRef (NeTEx 1.16.1) (cherry picked from commit 2c677056f3ec6fdd26697ecb705281a5db5159b8) --- .../UnmarshalResourceFrameExtendedTest.java | 47 +++++++++++++++++++ .../netex/validation/NeTExValidatorTest.java | 47 ++++++++++++++++++- 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/rutebanken/netex/model/UnmarshalResourceFrameExtendedTest.java b/src/test/java/org/rutebanken/netex/model/UnmarshalResourceFrameExtendedTest.java index 2ee3ea29..7ca3c6bb 100644 --- a/src/test/java/org/rutebanken/netex/model/UnmarshalResourceFrameExtendedTest.java +++ b/src/test/java/org/rutebanken/netex/model/UnmarshalResourceFrameExtendedTest.java @@ -121,4 +121,51 @@ void unmarshalOperatorWithBrandingRef() throws JAXBException { assertNotNull(operator.getBrandingRef()); assertEquals("TST:Branding:1", operator.getBrandingRef().getRef()); } + + @Test + void unmarshalResponsibilitySetWithAssociatedContract() throws JAXBException { + + String xml = "\n" + + "\n" + + " 2026-09-10T10:00:00\n" + + " TST\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " Contracted operation\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + + @SuppressWarnings("unchecked") + JAXBElement jaxbElement = (JAXBElement) unmarshaller + .unmarshal(new ByteArrayInputStream(xml.getBytes())); + + PublicationDeliveryStructure publicationDeliveryStructure = jaxbElement.getValue(); + ResourceFrame resourceFrame = (ResourceFrame) publicationDeliveryStructure.getDataObjects().getCompositeFrameOrCommonFrame().get(0).getValue(); + + ResponsibilitySet responsibilitySet = resourceFrame.getResponsibilitySets().getResponsibilitySet().get(0); + assertEquals("Contracted operation", responsibilitySet.getName().getValue()); + + ResponsibilityRoleAssignment_VersionedChildStructure roleAssignment = responsibilitySet.getRoles().getResponsibilityRoleAssignment().get(0); + + ContractRef_RelStructure associatedContract = roleAssignment.getAssociatedContract(); + assertNotNull(associatedContract); + ContractRefStructure contractRef = associatedContract.getContractRef(); + assertNotNull(contractRef); + assertEquals("TST:Contract:1", contractRef.getRef()); + assertEquals("1", contractRef.getVersion()); + } } diff --git a/src/test/java/org/rutebanken/netex/validation/NeTExValidatorTest.java b/src/test/java/org/rutebanken/netex/validation/NeTExValidatorTest.java index 1d9b00c7..9e6e8c42 100644 --- a/src/test/java/org/rutebanken/netex/validation/NeTExValidatorTest.java +++ b/src/test/java/org/rutebanken/netex/validation/NeTExValidatorTest.java @@ -24,6 +24,7 @@ import java.io.StringReader; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; class NeTExValidatorTest { @@ -52,4 +53,48 @@ void validatePublicationDeliveryWithLatestVersion() throws IOException, SAXExcep } -} \ No newline at end of file + /** + * AssociatedContract / ContractRef on a ResponsibilityRoleAssignment was added in NeTEx 1.16.1 + * (entur/NeTEx#63) and is therefore valid against the latest schema but not against 1.16. + */ + private static final String RESPONSIBILITY_SET_WITH_ASSOCIATED_CONTRACT = "\n" + + "\n" + + " 2026-09-10T10:00:00\n" + + " TST\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " Contracted operation\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + @Test + void latestVersionIs1_16_1() { + assertEquals(NeTExValidator.NetexVersion.v1_16_1, NeTExValidator.LATEST); + } + + @Test + void validateResponsibilitySetWithAssociatedContractAgainstLatestVersion() throws IOException, SAXException { + neTExValidator.validate(new StreamSource(new StringReader(RESPONSIBILITY_SET_WITH_ASSOCIATED_CONTRACT))); + } + + @Test + void validationOfAssociatedContractFailsAgainstVersion1_16() throws IOException, SAXException { + NeTExValidator validator1_16 = NeTExValidator.getNeTExValidator(NeTExValidator.NetexVersion.v1_16); + assertThatThrownBy(() -> validator1_16.validate(new StreamSource(new StringReader(RESPONSIBILITY_SET_WITH_ASSOCIATED_CONTRACT)))) + .isInstanceOf(SAXParseException.class) + .hasMessageContaining("AssociatedContract"); + } + +} From 26ba5ca91011f0f79901167c3428a517e6da8f5e Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Mon, 28 Sep 2026 12:37:59 +0200 Subject: [PATCH 4/4] Validate NeTEx 1.16 against the 1.16.1 schema 2.0.16.1 generated the model from the 1.16.1 schema but kept validating NetexVersion.v1_16 against the original 1.16 schema, and added v1_16_1 for the new one. Documents only ever declare the minor version (1.16:NO-NeTEx-networktimetable:1.6), so every consumer maps them to v1_16, and a document the model itself produces is rejected: an AssociatedContract on a ResponsibilityRoleAssignment fails with cvc-complex-type.2.4.a. Getting the 1.16.1 schema meant declaring a 1.16.1 version that no client should have to write. 1.16.1 is purely additive over 1.16: stripped of annotations, the only differences are the optional AssociatedContract element and the ContractRef types it needs. Anything valid against 1.16 stays valid. - v1_16 now loads its schema from the 1.16.1 folder. toString still returns "1.16": consumers derive the declared version from it, and returning "1.16.1" would stop them accepting 1.16. - v1_16_1 is kept as a deprecated alias for the same schema, so code compiled against 2.0.16.1 keeps working. - LATEST is v1_16. - The 1.16 schema is no longer downloaded or shipped. (cherry picked from commit 815e14008e4fa16ca1b7e5b1a0aced503fc82a27) --- README.md | 2 +- pom.xml | 16 ----------- .../netex/validation/NeTExValidator.java | 28 +++++++++++++++---- .../netex/validation/NeTExValidatorTest.java | 24 +++++++++++----- 4 files changed, 41 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 86ffbe40..6cec8b98 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ The XSD files are downloaded from GitHub during the `generate-sources` phase and - Property name conflict resolution - Package mapping to `org.rutebanken.netex.model` -**Schema validation**: `NeTExValidator` supports validation against NeTEx versions 1.07 through 1.16.1. +**Schema validation**: `NeTExValidator` supports validation against NeTEx versions 1.07 through 1.16. A version is validated against its latest patch schema: documents declaring 1.16 are validated against the 1.16.1 schema, which is backward compatible with 1.16. **Note on GML types**: This library generates classes under `net.opengis.gml._3` as part of the NeTEx model. diff --git a/pom.xml b/pom.xml index af003bbc..f6aa7eb9 100644 --- a/pom.xml +++ b/pom.xml @@ -305,22 +305,6 @@ - - - download-extract-legacy-1.16 - generate-resources - - exec - - - - ${netexGithubUrl}/archive/tags/v1.0.16.zip - src/main/resources/xsd/1.16 - NeTEx-tags-v1.0.16/xsd/* - - ./bin/netex-download-extract.sh - - download-extract-legacy-1.15 diff --git a/src/main/java/org/rutebanken/netex/validation/NeTExValidator.java b/src/main/java/org/rutebanken/netex/validation/NeTExValidator.java index feedffcf..7cd69393 100644 --- a/src/main/java/org/rutebanken/netex/validation/NeTExValidator.java +++ b/src/main/java/org/rutebanken/netex/validation/NeTExValidator.java @@ -44,23 +44,41 @@ public enum NetexVersion { v1_13 ("1.13"), v1_14 ("1.14"), v1_15 ("1.15"), - v1_16 ("1.16"), + /** + * NeTEx 1.16, validated against the latest 1.16 patch schema. Patch releases of the schema + * are backward compatible, and documents only ever declare the minor version, so a document + * declaring 1.16 is validated against the same schema the model is generated from. + */ + v1_16 ("1.16", "1.16.1"), + /** + * @deprecated the 1.16.1 schema is what {@link #v1_16} validates against. Use {@link #v1_16}. + */ + @Deprecated v1_16_1 ("1.16.1"); + private final String version; private final String folderName; - NetexVersion(String folderName) { + NetexVersion(String version) { + this(version, version); + } + + NetexVersion(String version, String folderName) { + this.version = version; this.folderName = folderName; } + /** + * @return the NeTEx version, which is not always the name of the folder holding its schema. + */ @Override public String toString() { - return folderName; + return version; } } private final Schema neTExSchema; - public static final NetexVersion LATEST = NetexVersion.v1_16_1; + public static final NetexVersion LATEST = NetexVersion.v1_16; private static final Map VALIDATORS_PER_VERSION = new EnumMap<>(NetexVersion.class); @@ -103,7 +121,7 @@ public NeTExValidator() throws IOException, SAXException { public NeTExValidator(NetexVersion version) throws IOException, SAXException { SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - String resourceName = "xsd/"+ version +"/NeTEx_publication.xsd"; + String resourceName = "xsd/"+ version.folderName +"/NeTEx_publication.xsd"; LOGGER.info("Loading resource: {}", resourceName); URL resource = getClass().getClassLoader().getResource(resourceName); if(resource == null) { diff --git a/src/test/java/org/rutebanken/netex/validation/NeTExValidatorTest.java b/src/test/java/org/rutebanken/netex/validation/NeTExValidatorTest.java index 9e6e8c42..74168092 100644 --- a/src/test/java/org/rutebanken/netex/validation/NeTExValidatorTest.java +++ b/src/test/java/org/rutebanken/netex/validation/NeTExValidatorTest.java @@ -55,7 +55,7 @@ void validatePublicationDeliveryWithLatestVersion() throws IOException, SAXExcep /** * AssociatedContract / ContractRef on a ResponsibilityRoleAssignment was added in NeTEx 1.16.1 - * (entur/NeTEx#63) and is therefore valid against the latest schema but not against 1.16. + * (entur/NeTEx#63). 1.16 is validated against the 1.16.1 schema, so it is valid against 1.16 too. */ private static final String RESPONSIBILITY_SET_WITH_ASSOCIATED_CONTRACT = "\n" + "\n" + @@ -80,8 +80,13 @@ void validatePublicationDeliveryWithLatestVersion() throws IOException, SAXExcep ""; @Test - void latestVersionIs1_16_1() { - assertEquals(NeTExValidator.NetexVersion.v1_16_1, NeTExValidator.LATEST); + void latestVersionIs1_16() { + assertEquals(NeTExValidator.NetexVersion.v1_16, NeTExValidator.LATEST); + } + + @Test + void version1_16IsNamed1_16() { + assertEquals("1.16", NeTExValidator.NetexVersion.v1_16.toString()); } @Test @@ -90,11 +95,16 @@ void validateResponsibilitySetWithAssociatedContractAgainstLatestVersion() throw } @Test - void validationOfAssociatedContractFailsAgainstVersion1_16() throws IOException, SAXException { + void validateResponsibilitySetWithAssociatedContractAgainstVersion1_16() throws IOException, SAXException { NeTExValidator validator1_16 = NeTExValidator.getNeTExValidator(NeTExValidator.NetexVersion.v1_16); - assertThatThrownBy(() -> validator1_16.validate(new StreamSource(new StringReader(RESPONSIBILITY_SET_WITH_ASSOCIATED_CONTRACT)))) - .isInstanceOf(SAXParseException.class) - .hasMessageContaining("AssociatedContract"); + validator1_16.validate(new StreamSource(new StringReader(RESPONSIBILITY_SET_WITH_ASSOCIATED_CONTRACT))); + } + + @Test + @SuppressWarnings("deprecation") + void validateResponsibilitySetWithAssociatedContractAgainstDeprecatedVersion1_16_1() throws IOException, SAXException { + NeTExValidator validator1_16_1 = NeTExValidator.getNeTExValidator(NeTExValidator.NetexVersion.v1_16_1); + validator1_16_1.validate(new StreamSource(new StringReader(RESPONSIBILITY_SET_WITH_ASSOCIATED_CONTRACT))); } }