Extra content at the end of the document: příčina a oprava

Za koncem kořenového elementu je v dokumentu ještě něco: druhý kořenový element, text nebo další XML soubor připojený za první. XML dokument smí mít právě jeden kořen. Opravíš to tak, že všechny záznamy obalíš jedním společným elementem, nebo obsah za kořenem odstraníš.

Autor: Aktualizováno

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

libxml2 (PHP, xmllint, lxml) hlásí Extra content at the end of the document, Python a Firefox junk after document element, Java The markup in the document following the root element must be well-formed a .NET There are multiple root elements:

Parser Hláška Kdy
libxml2 2.13 a novější PHP (DOM, SimpleXML), xmllint, Python lxml, validátor na XML.cz Extra content at the end of the document Dva kořenové elementy, Text za kořenovým elementem
XML declaration allowed only at the start of the document Dva soubory spojené za sebe
Java (Xerces) DocumentBuilder, SAXParser, JAXB, Spring, Maven The markup in the document following the root element must be well-formed. Dva kořenové elementy
Content is not allowed in trailing section. Text za kořenovým elementem
The processing instruction target matching "[xX][mM][lL]" is not allowed. Dva soubory spojené za sebe
Python (expat) xml.etree.ElementTree, minidom junk after document element: line 3, column 0 Dva kořenové elementy, Dva soubory spojené za sebe
junk after document element: line 5, column 0 Text za kořenovým elementem
.NET XmlDocument, XDocument, XmlReader There are multiple root elements. Line 3, position 2. Dva kořenové elementy
Data at the root level is invalid. Line 5, position 1. Text za kořenovým elementem
Unexpected XML declaration. The XML declaration must be the first node in the document, and no whitespace characters are allowed to appear before it. Line 3, position 3. Dva soubory spojené za sebe
Chrome 151+ DOMParser, zobrazení XML souboru Unexpected token: < Dva kořenové elementy
Unexpected characters outside the root element: E Text za kořenovým elementem
Invalid processing instruction: <?xml Dva soubory spojené za sebe
Firefox DOMParser, zobrazení XML souboru junk after document element Dva kořenové elementy, Text za kořenovým elementem, Dva soubory spojené za sebe

Pozice v hlášce ukazuje na začátek obsahu navíc, tedy na první znak za uzavírací značkou kořenového elementu. Chyba samotná ale vznikla tam, kde se dokument skládal dohromady.

Příčiny a opravy

Dva kořenové elementy

Chybně
<?xml version="1.0" encoding="UTF-8"?>
<objednavka cislo="2026-117"/>
<objednavka cislo="2026-118"/>
Opraveno
<?xml version="1.0" encoding="UTF-8"?>
<objednavky>
  <objednavka cislo="2026-117"/>
  <objednavka cislo="2026-118"/>
</objednavky>

Typicky vznikne, když program zapisuje záznamy v cyklu a zapomene na obalový element. XML není formát „jeden záznam na řádek“ — seznam musí mít společného rodiče. Pokud opravdu potřebuješ záznamy za sebou bez kořene (například pro postupné připisování do logu), jde o jiný formát a každý záznam musíš parsovat zvlášť.

Text za kořenovým elementem

Chybně
<?xml version="1.0" encoding="UTF-8"?>
<objednavky>
  <objednavka cislo="2026-117"/>
</objednavky>
Export dokončen: 1 záznam
Opraveno
<?xml version="1.0" encoding="UTF-8"?>
<objednavky>
  <objednavka cislo="2026-117"/>
</objednavky>

Za kořenem smí být jen komentáře, instrukce pro zpracování a bílé znaky. Text sem obvykle dostane skript, který po XML vypíše ještě hlášení o průběhu. Java tu hlásí jinou větu (Content is not allowed in trailing section), .NET obecné Data at the root level is invalid.

Dva soubory spojené za sebe

Chybně
<?xml version="1.0" encoding="UTF-8"?>
<objednavka cislo="2026-117"/>
<?xml version="1.0" encoding="UTF-8"?>
<objednavka cislo="2026-118"/>
Opraveno
<?xml version="1.0" encoding="UTF-8"?>
<objednavky>
  <objednavka cislo="2026-117"/>
  <objednavka cislo="2026-118"/>
</objednavky>

Dva celé soubory spojené za sebe (cat a.xml b.xml, export po částech). libxml2 a Java tu nezačnou kořenem, ale druhou deklarací: XML declaration allowed only at the start of the document a The processing instruction target matching "[xX][mM][lL]" is not allowed — stejně jako u obsahu před prologem. Soubory je potřeba sloučit jako XML, ne jako text.

Jak chybu najít

  • Otevři soubor na řádku z hlášky. Řádek nad ním obvykle končí uzavírací značkou kořene.
  • Když ti připadá, že kořen končí až na konci souboru, hledej jeho uzavírací značku uprostřed dokumentu (třeba zkopírovanou s kusem jiného souboru). Ta kořen ukončí a všechno za ní je pro parser obsah navíc.
  • Validátor zvýrazní řádek s chybou a formátování odsadí dokument tak, že je vidět, kde kořen končí.

Oprava v kódu

Soubory neslepuj jako text. Načti každý parserem a jejich kořenové elementy vlož pod nový společný kořen:

Python
import xml.etree.ElementTree as ET

# Více souborů pod jeden společný kořen
merged = ET.Element("objednavky")
for path in ["a.xml", "b.xml"]:
    merged.append(ET.parse(path).getroot())
ET.ElementTree(merged).write("vse.xml", encoding="UTF-8", xml_declaration=True)

# Úryvek bez deklarace (několik elementů za sebou) obal dočasným kořenem
root = ET.fromstring(f"<objednavky>{fragment}</objednavky>")
PHP
$merged = new DOMDocument('1.0', 'UTF-8');
$root = $merged->appendChild($merged->createElement('objednavky'));
foreach (['a.xml', 'b.xml'] as $path) {
    $part = new DOMDocument();
    $part->load($path);
    $root->appendChild($merged->importNode($part->documentElement, true));
}
$merged->save('vse.xml');

Dočasný kořen kolem úryvku funguje jen tehdy, když úryvek nemá vlastní deklaraci <?xml … ?> ani DOCTYPE.

Související