Neplatný znak v XML: An invalid XML character

Dokument obsahuje znak, který XML 1.0 nepovoluje: nejčastěji řídicí znak jako U+0000 nebo U+0001, přímo v textu nebo jako číselný odkaz �. Java hlásí An invalid XML character (Unicode: 0x1) was found, libxml2 (PHP, xmllint) PCDATA invalid Char value 1, .NET hexadecimal value 0x01, is an invalid character. Escapování tu nepomůže: zakázaný znak nelegalizuje ani číselný odkaz.

Autor: Aktualizováno

Znění hlášky v jednotlivých parserech

Parser Hláška Kdy
libxml2 2.13 a novější PHP (DOM, SimpleXML), xmllint, Python lxml, validátor na XML.cz xmlParseCharRef: invalid xmlChar value 0 Číselný odkaz na zakázaný znak
PCDATA invalid Char value 1 Řídicí znak přímo v textu
Java (Xerces) DocumentBuilder, SAXParser, JAXB, Spring, Maven Character reference "&#0" is an invalid XML character. Číselný odkaz na zakázaný znak
An invalid XML character (Unicode: 0x1) was found in the element content of the document. Řídicí znak přímo v textu
Python (expat) xml.etree.ElementTree, minidom reference to invalid character number: line 1, column 6 Číselný odkaz na zakázaný znak
not well-formed (invalid token): line 1, column 7 Řídicí znak přímo v textu
.NET XmlDocument, XDocument, XmlReader hexadecimal value 0x00, is an invalid character. Line 1, position 9. Číselný odkaz na zakázaný znak
hexadecimal value 0x01, is an invalid character. Line 1, position 8. Řídicí znak přímo v textu
Chrome 151+ DOMParser, zobrazení XML souboru Invalid character U+0000 Číselný odkaz na zakázaný znak
Invalid character U+0001 Řídicí znak přímo v textu
Firefox (česky) DOMParser, zobrazení XML souboru v české verzi Chyba parsování XML: Reference na neplatné číslo znaku Číselný odkaz na zakázaný znak
Chyba parsování XML: Dokument není dobře zformátován Řídicí znak přímo v textu

XML 1.0 povoluje tabulátor (U+0009), konec řádku (U+000A), návrat vozíku (U+000D) a znaky od U+0020 výš kromě U+D800–U+DFFF, U+FFFE a U+FFFF (produkce Char). Ostatní řídicí znaky U+0000–U+001F nejsou povolené v textu, atributech ani jako číselný odkaz. XML 1.1 povoluje U+0001–U+001F jako číselný odkaz, U+0000 ani tam ne, a v praxi se skoro nepoužívá (viz XML 1.0 vs 1.1).

Příčiny a opravy

Číselný odkaz na zakázaný znak

Chybně
<text>&#0;</text>
Opraveno
<text>bez NUL</text>

Číselný odkaz nepovoluje nic, co nepovoluje samotný text. U+0000 je zakázaný vždy. U běžného ampersandu nebo menší než jde naopak o syntaxi, kterou vyřeší &amp; nebo &lt; (viz znak & v XML).

Řídicí znak přímo v textu

Chybně: mezi a a b je neviditelný znak U+0001
<text>ab</text>
Opraveno
<text>ab</text>

Řídicí znaky se do XML obvykle dostanou z databáze, z textu zkopírovaného z jiné aplikace nebo z binárních dat. V editoru nejsou vidět. Najdi je podle kódového bodu:

Python: řádek, sloupec a kód zakázaného znaku
import re

# Řídicí znaky, které XML 1.0 nepovoluje (tabulátor, LF a CR povolené jsou).
zakazane = re.compile(r"[\x00-\x08\x0B\x0C\x0E-\x1F]")

with open("data.xml", encoding="utf-8") as soubor:
    for radek, text in enumerate(soubor, 1):
        for m in zakazane.finditer(text):
            print(radek, m.start() + 1, hex(ord(m.group())))  # 1 8 0x1

Jak chybu opravit

Odstraň znak jen podle pravidel aplikace, nebo binární data přenes jako Base64. Automatické mazání všech netisknutelných znaků může zničit tabulátory a nové řádky. Nejlépe znak odfiltruj už tam, kde XML vzniká, a zjisti, proč se do dat dostal.

Odliš zakázaný znak od chyby kódování

Číselný odkaz &#0; označuje skutečně zakázaný znak U+0000. Jde o jiný problém než nesprávné UTF-8 bajty, které ještě nelze převést na platné znaky. Samotná změna XML deklarace zakázaný znak neopraví.

Pokud jsou zdrojem data, která mohou obsahovat libovolné bajty, přenes je jako Base64 v poli určeném pro binární obsah. Nezahazuj znaky bez rozhodnutí aplikace: tabulátory a konce řádků jsou v XML 1.0 povolené a mohou být součástí významu. Zápis znaků popisují základy XML, diagnostiku bajtů chyby kódování.