Commit f97dada
Satisfy the CCMM requirements the XSD cannot enforce
Every record already validated against dataset/schema.xsd, which is why none of this showed up
as invalid. The CCMM specification states requirements the XSD has no way to express - they live
in en/dsv.ttl as scope notes and cardinality - and a conformance check reads those, not the XSD.
All 89 scope notes and 92 cardinality statements were extracted and checked against the output of
all 2628 live LINDAT records.
The one the specification calls mandatory:
- FRASCATI FORD. Dataset.hasSubject: "At least one subject must be a value from FRASCATI FORD
vocabulary. For that subject use reference codelist .../SubjectCategory/ as the Subject scheme."
No record carried one. The field is read off dc.type: a tool or service is computer science
(10201), a newsreel clip is History (60101) - on this corpus every dc.type "clip" is a segment
of a digitised 1943 newsreel held as a historical source, not a language resource - and
everything else is Linguistics (60203), which is what the repository collects. Codes and both
prefLabels come from the live register, and FORD_DEFAULT_CODE is the single value another
deployment changes. 1161 History, 1136 Linguistics, 331 Computer sciences.
Statements the output was making that the source does not support:
- access_url pointed at the bitstream. accessUrl: "The resource at the access URL contains
information about how to get the Dataset. It shall be the web page (not a document file)", and
the schema's own Czech documentation says to use it "in all cases except a downloadable file
URL". It is now the item's landing page; download_url keeps the file.
- identifier/value carried the resolved URL. CCMM types value as skos:notation - the identifier
WITHIN its scheme - and keeps the resolvable form in iri, the split its own samples use. One
emitter now serves every source, so scheme, notation and IRI cannot disagree between branches.
- The dataset had no iri of its own, so the published record had no resolvable identity.
- license is 1..1 and was serialised as <license/> on 584 records. A void mandatory element tells
a harvester nothing; those records now carry rightsstatements.org UND, which is the standard
statement for rights not stated.
- Language labels were DSpace's own strings, not the register's, and glued words together
("UpperSorbian", "AncientGreek (to 1453)") on 537 elements; their positional pairing could also
slip once duplicate codes were removed. The scope note asks for the register's "label and/or
its 3-letter code" and CCMM's own dataset-mini sample writes primary_language with an iri and
no label, so the IRI alone is published.
- media_type published IANA IRIs for six types RFC 6838 reserves as unregistered (application/
x-gzip and friends); those IRIs 404. They are suppressed, and the mandatory EU format element
carries the file type - which is the scope note's own "otherwise Format MAY be used" branch.
- format carried the IANA media type where Format.label asks for a label from the EU file-type
codelist. format is now the EU concept, media_type stays IANA. All 9125 distributions map to a
real EU concept; every code was verified by content, because that register answers 200 for any
path.
Values that were not what they claimed to be:
- 203 byte-identical sibling elements across 161 records, because DSpace stores one value under
several language wrappers and two sources can carry the same value. Deduplication now happens
where each block is assembled, so it also catches the cross-source repeats.
- "Germany||United States" was published as one place name; it is two.
- 20 alternate titles said nothing about how they differ from the title.
- dc.relation.uri was listed as a qualifier but had no term, so those related resources came out
untyped; the register has Other for exactly that.
- An iri only had to start with a URL, so "http://a/ Book and enhanced publication" was published
as an identifier. The whole value now has to be one URI.
- A packed contact field put an affiliation in the email slot.
- A second dc.title never became an alternate title. The predicate was
doc:field[@name='value'][position() > 1], but position() counts within each parent and every
XOAI language wrapper holds exactly one field, so it was always 1. Two titles only worked when
both sat in the SAME wrapper, which XOAI does not do for values differing in language. Latent
on this corpus - no record has two dc.title - but wrong, and the new fixture exercises it.
Measured after the change over all 2628 records: 2628 valid, every counter zero, 95 tests and
187 assertions green, 67 of 68 stylesheet mutants killed, 23 portability inputs clean. The
surviving mutant edits a FormatDate branch its callers make unreachable by pre-filtering.
Not changed, and why: eight records carry U+FFFD in title and description text. That is in the
source data - eight records in, eight records out - so guessing the intended character would be
inventing content. It belongs in the repository's metadata.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent 23aca5e commit f97dada
4 files changed
Lines changed: 805 additions & 80 deletions
File tree
- dspace-oai/src/test
- java/org/dspace/xoai/tests/stylesheets
- resources
- dspace/config/crosswalks/oai/metadataFormats
0 commit comments