METS identifiers
Every element in a METS file the platform writes carries an ID attribute, and the structural elements
cross-reference each other by those IDs. This page explains, in plain terms, how those IDs are made, what
you can and cannot rely on, and what is changing.
It complements The METS files we write (the full document shape) and What the METS parser can read (how third-party METS is read).
The scheme: a prefix plus the path
Section titled “The scheme: a prefix plus the path”An ID is a fixed prefix telling you what kind of thing it identifies, followed by the resource’s path within the deposit:
| Prefix | Identifies | Example |
|---|---|---|
PHYS_ |
a div in the physical structMap | PHYS_objects/photo.tif |
FILE_ |
a file in the fileSec | FILE_objects/photo.tif |
ADM_ |
an amdSec (administrative metadata) | ADM_objects/photo.tif |
TECH_ |
the techMD inside that amdSec | TECH_objects/photo.tif |
DMD_ |
a dmdSec (descriptive metadata) | DMD_objects/photo.tif |
digiprovMD_ClamAV_ |
a virus-scan event record | digiprovMD_ClamAV_ADM_objects/photo.tif |
The point of this scheme is human readability: open the XML and every element tells you what it is
and what it belongs to. There is one authority for spelling them in code:
MetsIds.
The catch: these are not valid XML IDs
Section titled “The catch: these are not valid XML IDs”The METS schema types ID attributes as xs:ID, which must be an NCName — and an NCName may not
contain /, spaces, and various other characters that file paths contain freely. So
PHYS_objects/my file.pdf is doubly invalid (a slash and a space), and a validating parser will reject
the document. This is issue #188.
A space in an ID causes a second, subtler problem. Reference attributes like ADMID and DMDID are typed
IDREFS — a whitespace-separated list of IDs. An ID that contains a space is therefore
indistinguishable, to standard tooling, from two references. Typed deserialisers split it in half.
The fix, and the promise underneath it
Section titled “The fix, and the promise underneath it”The fix is sequenced so that every METS file already written stays readable and editable, unchanged, indefinitely. That promise shapes everything:
-
Step 1: IDs stopped being load-bearing. The platform no longer navigates by ID text. It builds a path cache from the metadata that genuinely records where things are —
premis:originalNamefor directories,FLocat/@xlink:hreffor files — and treats the ID as an opaque label. Reference attributes are resolved tolerantly on both the writing and parsing stacks: the whole attribute value is tried first (which is exactly how a legacy space-containing ID matches), then each whitespace-separated token (which is how a genuine multi-reference list resolves). -
Step 2: new IDs are schema-valid. Newly written entries encode the path part with
XmlConvert.EncodeLocalName—/becomes_x002F_, a space becomes_x0020_— soPHYS_objects/my file.pdfis now minted asPHYS_objects_x002F_my_x0020_file.pdf. The encoding is reversible, but nothing decodes it: the path is read from the metadata, never from the ID. Existing entries keep the IDs they were born with, so a document edited after step 2 can legitimately contain both forms side by side. Because of step 1, nothing cares.Two consequences worth knowing. A new ID is a single IDREFS token, so the split-in-half problem above simply does not arise for anything written from now on. And when the platform writes a reference into an older document — an
fptr, an smLink — it looks up the target element’s real ID and copies it, rather than minting what the ID “should” be; otherwise it would write references to IDs that document does not contain. -
Step 3: deliberate migration. A normalisation rewrites every illegal ID — and every reference to it, including whitespace-split IDREFS tokens and the xlink attributes that carry IDs — into the encoded form, producing character-for-character what the platform would mint today. IDs that are already legal are never touched, whoever minted them, which is what makes the operation safe to run repeatedly and keeps it away from IDs that are not ours to renumber. A document that already carries the same ID on two elements is refused rather than normalised, so that a visible corruption is not turned into an invisible one. It runs three ways: a UI action on a deposit, the
POST /deposits/{id}/mets/normaliseendpoint, and a bulk migration tool (src/mets-id-migration/) that works through the preserved corpus creating a METS-only new OCFL version per Archival Group, with the Activity Stream event suppressed — the version changes how the object is recorded, not what it holds, so downstream IIIF rebuilds are deliberately not triggered. While theFeatureFlags:NormaliseMetsIdsOnWriteflag is enabled, writing a managed METS also normalises any illegal IDs still in the document. Once the migration campaigns complete, the legacy forms are gone.
Opaque to code, legible to people
Section titled “Opaque to code, legible to people”An ID like PHYS_objects_x002F_annual_x0020_report.pdf still carries the file’s path, and that is
deliberate. Two audiences want different things from an identifier, and this scheme serves both:
- For a person reading the METS — now, or in fifty years, with no access to this platform — an ID
that says which file it is beats an opaque
PHYS_7or a GUID. Preservation metadata is meant to be readable without the software that wrote it. - For the platform it also comes free of charge: a deposit-relative path is already unique within a document, so deriving the ID from the path makes it unique without any allocation, counter or registry. A GUID scheme would need the minting sites to coordinate; this one does not.
But no code may infer anything from the string value of an ID. Not the path, not the file it belongs
to, not its position. Paths are read from FLocat/@xlink:href and premis:originalName, which are the
ground truth in every ID era and in third-party METS alike; references are followed by matching the raw
string against ID attributes.
Note what that means: because the ID genuinely does contain the path, this is a discipline, not a
property the format enforces. Nothing stops a reader parsing it. The rule binds the platform’s own code
exactly as much as it binds anyone else’s — and it is the reason the platform navigates by a path cache
built from originalName/FLocat, rather than by reconstructing the ID it thinks a path ought to have.
Two places in the platform still break the rule, both knowingly, and both scoped to legacy content:
navigation falls back to the pre-step-2 PHYS_ + raw-path convention when a document’s own path metadata
will not resolve, and the virus-scan reader falls back to matching digiprovMD IDs by convention when a
file’s ADMID resolves to no usable technical metadata. Both exist only to serve documents written before
step 2, and both become removable once the step 3 migration campaigns have run everywhere.
The rules, if you consume or produce this METS
Section titled “The rules, if you consume or produce this METS”- Treat IDs as opaque. Never parse a path out of an ID, however clearly it appears to contain one —
see Opaque to code, legible to people. The path lives in
FLocat/@xlink:href(files) andpremis:originalName(directories) — those are the ground truth, in both ID eras. - Resolve references by the raw string. To follow an
ADMID/DMDID/FILEID/smLink reference, match the attribute value againstIDattributes exactly. If the whole value matches nothing and contains whitespace, try each token. (This is what the platform’s own reader does.) - Copy IDs verbatim when writing references. A reference must be the target element’s actual
ID, character for character — never reconstructed from a path or a naming convention. This is the rule the platform follows internally too — it is the single most likely way to corrupt a mixed-era document. - Don’t assume uniqueness of shape. Legacy IDs contain slashes and spaces; new ones contain
_x002F_/_x0020_escapes; third-party METS (Goobi, Archivematica, EPrints) follows entirely different conventions (AMD_0001,file-{uuid}, …). All are just strings. - Don’t assume one ID per thing, either. Some records accumulate: a file scanned twice has two
digiprovMDevents, the second identifieddigiprovMD_ClamAV_2_ADM_…— the occurrence number sits between the prefix and the identifier, precisely so a numbered ID can never be confused with the plain ID of a differently-named file. Read the last one, and see what the platform writes.
Status
Section titled “Status”All three steps are in the platform: step 1, the IDREFS resolution work and step 2 landed in digital-preservation PRs #211, #213 and #214 (with follow-up fixes in #218 and #220), and step 3 — the normalisation machinery and the bulk migration tool — in #232. The migration campaigns themselves (development, then production) are operational runs of the tool rather than code changes; until they have completed in every environment, documents in both ID forms remain in circulation and all the mixed-form rules on this page stay load-bearing.