From 396ed270467a0b7a0423928e5263d53e42af4bca Mon Sep 17 00:00:00 2001 From: Clemens Portele Date: Fri, 19 Jun 2026 12:12:56 +0200 Subject: [PATCH] gml-decoder: cover ISO 19139 codelist value decoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A codelist-valued property whose value is wrapped in a gmd codelist element (e.g. processor) carries its value in the element text, not in an xlink:href. Add a focused decoder test asserting such a property decodes from the text content even when its schema declares a codelist constraint — which on its own would route an xlink:href as the value — and add that codelist constraint to the role property of the existing full-tree decode fixture so the broader test guards the same path. --- .../domain/FeatureTokenDecoderGmlSpec.groovy | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/xtraplatform-features-gml/src/test/groovy/de/ii/xtraplatform/features/gml/domain/FeatureTokenDecoderGmlSpec.groovy b/xtraplatform-features-gml/src/test/groovy/de/ii/xtraplatform/features/gml/domain/FeatureTokenDecoderGmlSpec.groovy index 0c3c6c5f2..6489d5179 100644 --- a/xtraplatform-features-gml/src/test/groovy/de/ii/xtraplatform/features/gml/domain/FeatureTokenDecoderGmlSpec.groovy +++ b/xtraplatform-features-gml/src/test/groovy/de/ii/xtraplatform/features/gml/domain/FeatureTokenDecoderGmlSpec.groovy @@ -1853,7 +1853,10 @@ class FeatureTokenDecoderGmlSpec extends Specification { .putProperties2("rol", new ImmutableFeatureSchema.Builder() .sourcePath("qag__dpl_prs_pro_resp_rol_cdv") .type(SchemaBase.Type.STRING) - .alias("role"))) + .alias("role") + .constraints(new ImmutableSchemaConstraints.Builder() + .codelist("CI_RoleCode") + .build()))) .putProperties2("src", new ImmutableFeatureSchema.Builder() .type(SchemaBase.Type.OBJECT) .objectType("LI_Source") @@ -1952,6 +1955,7 @@ class FeatureTokenDecoderGmlSpec extends Specification { // - dateTime wraps — gmd/gco auto-detected // - organisationName wraps — auto-detected // - role wraps — auto-detected; text content used + // (rol has a codelist constraint, but the gmd text path wins; see the focused test below) // - source/description wraps — explicit valueWrap // The sibling at AX_DQPunktort level exercises a scalar that // sits next to the deep dpl subtree. @@ -2012,6 +2016,48 @@ class FeatureTokenDecoderGmlSpec extends Specification { valueAtPath(tokens, ["qag", "gst"]) == "2100" } + def 'a codelist-constrained role still decodes from the gmd:CI_RoleCode text, not as an xlink:href'() { + given: + // Regression guard for the ISO 19139 codelist encoding (GmlConfiguration#codeListUriTemplateIso19139). + // The `rol` property carries a `codelist` constraint, which makes the decoder treat it as a + // codelist property eligible for xlink:href routing. The ISO 19139 wire, however, carries the + // value in the gmd:CI_RoleCode text content (and the codeListValue attribute), with no + // xlink:href on the property element — so the codelist routing finds no href and the gmd + // value-wrapper text is used. The codeList URI must not leak into the decoded value. + def decoder = newPunktortAuDecoder(nasNamespaceProfile()) + def xml = """ + + + + + + + + + + processor + + + + + + + + + + """ + + when: + def tokens = runDecoder(decoder, xml) + + then: + valueAtPath(tokens, ["qag", "dpl", "prs", "pro", "rol"]) == "processor" + !tokens.contains("https://schemas.isotc211.org/19139/resources/codelists/gmxCodelists.xml/gmxCodelists.xml#CI_RoleCode") + } + def 'LI_ProcessStep children written in the wrong namespace are skipped'() { given: // Sanity check: with objectTypeNamespaces[LI_ProcessStep] = gmd, an LI_ProcessStep