Skip to content

fix(xml): accept the element names XML actually allows - #564

Merged
productdevbook merged 1 commit into
mainfrom
fix/xml-names
Aug 13, 2026
Merged

fix(xml): accept the element names XML actually allows#564
productdevbook merged 1 commit into
mainfrom
fix/xml-names

Conversation

@productdevbook

Copy link
Copy Markdown
Owner

fix(xml): accept the element names XML actually allows

writeXml turns a column heading into an element name and validated it
with

/^[A-Za-z_][\w.-]*(?::[A-Za-z_][\w.-]*)?$/

which is ASCII. XML 1.0 §2.3's NameStartChar runs from #xC0, so every
accented or non-Latin heading was refused:

writeXml([{ Şehir: "İzmir" }])
ParseError: Invalid XML name for element "Şehir": "Şehir"

Şehir, Ünvan, Größe, café, naïve, 名前, Ελλάδα,
Кириллица — all valid XML, all rejected. A spreadsheet whose columns
are not named in English could not be written to XML at all. Not
mangled, not escaped: the format was unavailable, and the error said the
name was invalid when it was the check that was.

Found while testing #563, where a Turkish sheet could go to CSV, TSV,
JSON, NDJSON, HTML and Markdown but not XML.

The productions, minus the colon

NameStartChar and NameChar verbatim, with : pulled out so a name
may carry one prefix rather than a colon wherever it likes — that is
QName from Namespaces in XML §4, and it is what the old regex was
reaching for too.

The u flag is not optional here: #x10000–#xEFFFF is above the BMP, and
without it the surrogate halves match separately.

Nothing new is accepted that XML forbids. A leading digit, a leading
hyphen or dot, a space, <, two colons, the empty string, and a leading
combining mark — NameChar but not NameStartChar — are still refused,
each with a test.

Checked

27 tests, 14 of which fail against the old regex and none of which is
a rejection case: the tests that say what stays refused pass either way,
which is the point of having them.

The reader was never the problem — parseXml reads any well-formed name
— and a non-ASCII name now round-trips through both.

pnpm test green — 10,622 tests, 236 files.

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com

🤖 Generated with Claude Code

`writeXml` turns a column heading into an element name and validated it
with

    /^[A-Za-z_][\w.-]*(?::[A-Za-z_][\w.-]*)?$/

which is ASCII. XML 1.0 §2.3's `NameStartChar` runs from #xC0, so every
accented or non-Latin heading was refused:

    writeXml([{ Şehir: "İzmir" }])
    ParseError: Invalid XML name for element "Şehir": "Şehir"

`Şehir`, `Ünvan`, `Größe`, `café`, `naïve`, `名前`, `Ελλάδα`,
`Кириллица` — all valid XML, all rejected. A spreadsheet whose columns
are not named in English could not be written to XML **at all**. Not
mangled, not escaped: the format was unavailable, and the error said the
name was invalid when it was the check that was.

Found while testing #563, where a Turkish sheet could go to CSV, TSV,
JSON, NDJSON, HTML and Markdown but not XML.

## The productions, minus the colon

`NameStartChar` and `NameChar` verbatim, with `:` pulled out so a name
may carry one prefix rather than a colon wherever it likes — that is
`QName` from Namespaces in XML §4, and it is what the old regex was
reaching for too.

The `u` flag is not optional here: #x10000–#xEFFFF is above the BMP, and
without it the surrogate halves match separately.

Nothing new is accepted that XML forbids. A leading digit, a leading
hyphen or dot, a space, `<`, two colons, the empty string, and a leading
combining mark — `NameChar` but not `NameStartChar` — are still refused,
each with a test.

## Checked

27 tests, **14 of which fail against the old regex** and none of which is
a rejection case: the tests that say what stays refused pass either way,
which is the point of having them.

The reader was never the problem — `parseXml` reads any well-formed name
— and a non-ASCII name now round-trips through both.

`pnpm test` green — 10,622 tests, 236 files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@productdevbook
productdevbook merged commit 15b5c3a into main Aug 13, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant