Ingesting NISO JATS 1.3 (ANSI/NISO Z39.96-2021) journal articles into the Crossref deposit engine presents an architectural data impedance mismatch. JATS organizes article metadata within the <front><article-meta> hierarchy, employing highly granular tag structures designed for archival completeness, citation linking, and typographic fidelity. Conversely, Crossref requires batch submission payloads to validate strictly against the crossref5.3.1.xsd schema, which enforces a relational, publication-centric tree structure rooted in <doi_batch version="5.3.1">.
Commercial digital delivery workflows fail when automated ingest pipelines attempt naive structural conversion. The primary technical points of divergence include:
- Header and Administrative Metadata: JATS separates administrative routing into external workflows or publisher tags. Crossref mandates an explicit
<head>block with an incremented<doi_batch_id>, an epoch or ISO 8601<timestamp>, and a<depositor>record matching the organizational Crossref account credentials.
- Contributor Data Structures: JATS permits arbitrary
<contrib-group>organizations containing<contrib contrib-type="author">with individual sub-elements for surnames, given names, and ORCID identifiers formatted as raw URLs. Crossref demands an indexed sequence inside<contributors>, requiring exactly onesequence="first"attribute for the primary author,sequence="additional"for subsequent authors, and strictly isolated<given_name>,<surname>, and<ORCID>nodes (enforcing the[https://orcid.org/0000-0000-0000-0000](https://orcid.org/0000-0000-0000-0000)URI pattern).
- Journal Identity and Date Cardinality: JATS articles can maintain arbitrary combinations of collection dates, print publication dates, and electronic dates (
<pub-date pub-type="epub">,<pub-date pub-type="collection">). Crossref enforces a strict<journal>nesting model containing<journal_metadata>, optional<journal_issue>, and<journal_article publication_type="full_text">. Each publication date must resolve down to valid<month>,<day>, and<year>elements, or legal truncated subsets permitted by the schema.
- Abstract Formatting: JATS retains full semantic inline markup within
<abstract><p>. Crossref permits abstracts under thejats:abstractnamespace extension, but rejects undeclared namespaces or unescaped non-JATS inline entities.
A production-grade transformation pipeline must reliably project JATS <front> data into the target schema without data degradation or manual remediation.
Pipeline Architecture
The transformation architecture consists of a deterministic CLI pipeline that processes raw JATS 1.3 XML documents through an XSLT transformation engine, injects runtime administrative parameters, and passes the output to an automated preflight schema validator.
+-----------------------------------------------------------------------+
| INCOMING INGEST |
| JATS 1.3 Article XML (<article>) |
+-----------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------+
| TRANSFORMATION STAGE |
| Engine: Saxon-HE / xsltproc CLI |
| Stylesheet: jats-to-crossref5.3.1.xsl |
| Injected Params: batch_id, timestamp, depositor_name, depositor_email|
+-----------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------+
| DEPOSIT PAYLOAD |
| Crossref 5.3.1 XML (<doi_batch version="5.3.1">) |
+-----------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------+
| PREFLIGHT ASSERTION |
| Engine: xmllint CLI |
| Validation: crossref5.3.1.xsd (W3C XML Schema) |
| Exit Code: 0 (Pass to Deposit HTTP API) / >0 (Abort Pipeline) |
+-----------------------------------------------------------------------+
Executable Implementation
The following stylesheet (jats-to-crossref5.3.1.xsl) performs a complete extraction and restructuring of JATS 1.3 <front> metadata into a compliant Crossref 5.3.1 deposit payload. It handles contributor sequencing, DOI resolution, dates, titles, and abstract namespace bridging.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jats="http://www.ncbi.nlm.nih.gov/JATS1"
xmlns="http://www.crossref.org/schema/5.3.1"
exclude-result-prefixes="xs jats">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- Operational Runtime Parameters -->
<xsl:param name="batch_id" required="yes"/>
<xsl:param name="timestamp" required="yes"/>
<xsl:param name="depositor_name" required="yes"/>
<xsl:param name="depositor_email" required="yes"/>
<xsl:param name="registrant" required="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="article"/>
</xsl:template>
<xsl:template match="article">
<doi_batch version="5.3.1"
xmlns="http://www.crossref.org/schema/5.3.1"
xmlns:jats="http://www.ncbi.nlm.nih.gov/JATS1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.crossref.org/schema/5.3.1 https://www.crossref.org/schemas/crossref5.3.1.xsd">
<head>
<doi_batch_id><xsl:value-of select="$batch_id"/></doi_batch_id>
<timestamp><xsl:value-of select="$timestamp"/></timestamp>
<depositor>
<depositor_name><xsl:value-of select="$depositor_name"/></depositor_name>
<email_address><xsl:value-of select="$depositor_email"/></email_address>
</depositor>
<registrant><xsl:value-of select="$registrant"/></registrant>
</head>
<body>
<journal>
<journal_metadata>
<full_title>
<xsl:value-of select="front/journal-meta/journal-title-group/journal-title"/>
</full_title>
<xsl:if存在 select="front/journal-meta/journal-id">
<abbrev_title>
<xsl:value-of select="front/journal-meta/journal-title-group/abbrev-journal-title[1]"/>
</abbrev_title>
</xsl:if存在>
<issn media_type="electronic">
<xsl:value-of select="front/journal-meta/issn[@pub-type='epub' or @publication-format='electronic'][1]"/>
</issn>
</journal_metadata>
<journal_issue>
<publication_date media_type="online">
<xsl:call-template name="resolve-date">
<xsl:with-param name="date-node" select="front/article-meta/pub-date[@pub-type='epub' or @date-type='pub'][1]"/>
</xsl:call-template>
</publication_date>
<journal_volume>
<volume><xsl:value-of select="front/article-meta/volume"/></volume>
</journal_volume>
<xsl:if test="front/article-meta/issue">
<issue><xsl:value-of select="front/article-meta/issue"/></issue>
</xsl:if>
</journal_issue>
<journal_article publication_type="full_text">
<titles>
<title>
<xsl:value-of select="normalize-space(front/article-meta/title-group/article-title)"/>
</title>
</titles>
<contributors>
<xsl:for-each select="front/article-meta/contrib-group/contrib[@contrib-type='author']">
<xsl:variable name="pos" select="position()"/>
<person_name contributor_role="author">
<xsl:attribute name="sequence">
<xsl:choose>
<xsl:when test="$pos = 1">first</xsl:when>
<xsl:otherwise>additional</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:if test="name/given-names">
<given_name><xsl:value-of select="name/given-names"/></given_name>
</xsl:if>
<surname><xsl:value-of select="name/surname"/></surname>
<xsl:if test="contrib-id[@contrib-id-type='orcid']">
<ORCID>
<xsl:variable name="raw-orcid" select="normalize-space(contrib-id[@contrib-id-type='orcid'])"/>
<xsl:choose>
<xsl:when test="starts-with($raw-orcid, 'http')">
<xsl:value-of select="$raw-orcid"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('https://orcid.org/', $raw-orcid)"/>
</xsl:otherwise>
</xsl:choose>
</ORCID>
</xsl:if>
</person_name>
</xsl:for-each>
</contributors>
<jats:abstract>
<xsl:for-each select="front/article-meta/abstract/p">
<jats:p><xsl:value-of select="normalize-space(.)"/></jats:p>
</xsl:for-each>
</jats:abstract>
<publication_date media_type="online">
<xsl:call-template name="resolve-date">
<xsl:with-param name="date-node" select="front/article-meta/pub-date[@pub-type='epub' or @date-type='pub'][1]"/>
</xsl:call-template>
</publication_date>
<xsl:if test="front/article-meta/fpage">
<pages>
<first_page><xsl:value-of select="front/article-meta/fpage"/></first_page>
<xsl:if test="front/article-meta/lpage">
<last_page><xsl:value-of select="front/article-meta/lpage"/></last_page>
</xsl:if>
</pages>
</xsl:if>
<doi_data>
<doi><xsl:value-of select="front/article-meta/article-id[@pub-id-type='doi']"/></doi>
<resource>
<xsl:value-of select="front/article-meta/self-uri/@xlink:href | front/article-meta/self-uri"/>
</resource>
</doi_data>
</journal_article>
</journal>
</body>
</doi_batch>
</xsl:template>
<!-- Date Normalization Template -->
<xsl:template name="resolve-date">
<xsl:param name="date-node"/>
<xsl:if test="$date-node/month">
<month><xsl:value-of select="format-number(number($date-node/month), '00')"/></month>
</xsl:if>
<xsl:if test="$date-node/day">
<day><xsl:value-of select="format-number(number($date-node/day), '00')"/></day>
</xsl:if>
<year><xsl:value-of select="$date-node/year"/></year>
</xsl:template>
</xsl:stylesheet>
To execute this transformation consistently, wrap the process in a strict automation script (build_deposit.sh):
#!/usr/bin/env bash
set -euo pipefail
JATS_IN="${1:?Error: Source JATS XML required}"
CROSSREF_OUT="${2:?Error: Target Crossref XML path required}"
SCHEMA_XSD="${3:-crossref5.3.1.xsd}"
BATCH_ID="BATCH-$(date +%s)"
TIMESTAMP="$(date +%Y%m%d%H%M%S)"
DEP_NAME="ZebaAcademicPublishing"
DEP_EMAIL="deposits@zeba.academy"
REGISTRANT="ZebaAcademyPress"
echo "[TRANSFORM] Processing ${JATS_IN} via Saxon-HE..."
saxon-he -s:"${JATS_IN}" \
-xsl:jats-to-crossref5.3.1.xsl \
-o:"${CROSSREF_OUT}" \
batch_id="${BATCH_ID}" \
timestamp="${TIMESTAMP}" \
depositor_name="${DEP_NAME}" \
depositor_email="${DEP_EMAIL}" \
registrant="${REGISTRANT}"
echo "[PREFLIGHT] Validating generated payload against ${SCHEMA_XSD}..."
xmllint --noout --schema "${SCHEMA_XSD}" "${CROSSREF_OUT}"
echo "[STATUS] Deposit payload verified successfully: ${CROSSREF_OUT}"
Automated Preflight Validation
Preflight verification uses xmllint to evaluate the generated output against the authoritative W3C XML Schema file for Crossref 5.3.1.
Execute the preflight validation directly using the following CLI assertions:
# Fetch the authoritative schema definition if absent locally
if [ ! -f "crossref5.3.1.xsd" ]; then
curl -sSL "https://www.crossref.org/schemas/crossref5.3.1.xsd" -o crossref5.3.1.xsd
fi
# Run xmllint schema assertion
xmllint --noout --schema crossref5.3.1.xsd deposit-payload.xml
ASSERTION_EXIT=$?
if [ ${ASSERTION_EXIT} -ne 0 ]; then
echo "CRITICAL: Schema preflight assertion failed with exit code ${ASSERTION_EXIT}." >&2
exit 1
fi
To assert semantic business rules beyond DTD/XSD compliance (such as ensuring that exactly one contributor holds sequence="first"), run an inline XPath assertion via xmllint --xpath:
FIRST_AUTHORS=$(xmllint --xpath "count(//*[local-name()='person_name'][@sequence='first'])" deposit-payload.xml)
if [ "${FIRST_AUTHORS}" -ne 1 ]; then
echo "ERROR: Crossref requires exactly 1 author with sequence='first'. Found: ${FIRST_AUTHORS}" >&2
exit 2
fi
Production Gotchas
- Contributor Sequence Collisions Under Multiple Collaborations: When an article has an institutional consortium (e.g.,
<collab>Consortium Group</collab>) alongside individual authors, using standard JATS<contrib-group>positions blindly causes invalid Crossref payloads. Crossref requires organizations to use<organization sequence="..." contributor_role="...">rather than<person_name>. If an XSLT maps an institutional contributor to<person_name>,<surname>remains empty, causing immediate XSD rejection under theminOccurs="1"constraint on<surname>.
- Inline Formatting Escapes in Title Nodes: JATS frequently embeds typographic tags within titles (e.g.,
<article-title>Synthesis of <italic>E. coli</italic> via <inline-formula><alternatives><mml:math>...</mml:math></alternatives></inline-formula></article-title>). Crossref<title>fields allow only a small subset of markup (<i>,<b>,<sub>,<sup>). Complex MathML or uncaught<ext-link>elements inside a title generate schema violations unless they are serialized into allowed inline tags or stripped using XSLTvalue-ofnormalization.
- Partial Pub-Dates in Online Releases: Articles published ahead of issue assignment frequently supply a JATS
<pub-date>containing only<year>and<month>, omitting<day>. In Crossref 5.3.1,<publication_date>requires elements in strict schema order (month?,day?,year). If<month>contains text strings (e.g.,"September") instead of numeric digits ("09"),crossref5.3.1.xsdfails because<month>is restricted to integer formats between 1 and 12. Ensure format enforcement usingformat-number(number(...), '00')during XSL extraction.



