This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 1e72b804794b CAMEL-25078: camel-xml-jaxp - XML type converters: fix
bugs found in a deep review (#26968)
1e72b804794b is described below
commit 1e72b804794b2b850567efdb5ec9709d37703b89
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 28 22:41:11 2026 +0200
CAMEL-25078: camel-xml-jaxp - XML type converters: fix bugs found in a deep
review (#26968)
- XMLStreamReader to InputStream/Reader: a read of 0 bytes returns 0
(readAllBytes was cut at 16 KB),
the writer is flushed (another charset than utf-8 was empty with jdk
stax), attributes without a
namespace work in the Reader variant, and a reader positioned at an
element keeps that element
- a text node in mixed content converts to String (was ClassCastException),
and a list of text nodes
is not repeated
- an attribute node converts to its value
- a file to XMLStreamReader/XMLEventReader uses the encoding of the xml
declaration
- XmlLineNumberParser with root names handles elements after the root (also
in camel-route-parser)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../camel/parser/helper/XmlLineNumberParser.java | 13 +-
.../converter/jaxp/XmlConvertersEdgeCasesTest.java | 133 +++++++++++++++++++++
.../apache/camel/converter/jaxp/DomConverter.java | 15 ++-
.../apache/camel/converter/jaxp/StaxConverter.java | 18 +--
.../converter/jaxp/XMLStreamReaderInputStream.java | 93 ++++++++------
.../converter/jaxp/XMLStreamReaderReader.java | 90 ++++++++------
.../apache/camel/util/xml/XmlLineNumberParser.java | 13 +-
7 files changed, 281 insertions(+), 94 deletions(-)
diff --git
a/catalog/camel-route-parser/src/main/java/org/apache/camel/parser/helper/XmlLineNumberParser.java
b/catalog/camel-route-parser/src/main/java/org/apache/camel/parser/helper/XmlLineNumberParser.java
index 537c6d6f710f..9ae73d7f77d3 100644
---
a/catalog/camel-route-parser/src/main/java/org/apache/camel/parser/helper/XmlLineNumberParser.java
+++
b/catalog/camel-route-parser/src/main/java/org/apache/camel/parser/helper/XmlLineNumberParser.java
@@ -137,6 +137,7 @@ public final class XmlLineNumberParser {
return new DefaultHandler() {
private Locator locator;
private boolean found;
+ private boolean done;
private final Map<String, String> localNs = new HashMap<>();
private final Map<String, String> anonymousNs = new
LinkedHashMap<>();
@@ -160,6 +161,10 @@ public final class XmlLineNumberParser {
final String uri, final String localName, final String
qName, final Attributes attributes) {
addTextIfNeeded();
+ if (done) {
+ // only the first root element is parsed (there can only
be one document element)
+ return;
+ }
if (rootNames != null && !found) {
if (isRootName(qName)) {
found = true;
@@ -232,6 +237,11 @@ public final class XmlLineNumberParser {
if (elementStack.isEmpty()) {
// Is this the root element?
doc.appendChild(closedEl);
+ if (rootNames != null) {
+ // the root element is done, so elements after it
are not parsed
+ found = false;
+ done = true;
+ }
} else {
final Element parentEl = elementStack.peek();
parentEl.appendChild(closedEl);
@@ -264,8 +274,9 @@ public final class XmlLineNumberParser {
if (el != null) {
final Node textNode =
doc.createTextNode(textBuffer.toString());
el.appendChild(textNode);
- textBuffer.delete(0, textBuffer.length());
}
+ // text outside the elements that are parsed is not added
to the next element
+ textBuffer.delete(0, textBuffer.length());
}
}
};
diff --git
a/core/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConvertersEdgeCasesTest.java
b/core/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConvertersEdgeCasesTest.java
new file mode 100644
index 000000000000..24c92924dac2
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConvertersEdgeCasesTest.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.converter.jaxp;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.apache.camel.util.xml.XmlLineNumberParser;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class XmlConvertersEdgeCasesTest {
+
+ // the jdk implementation of stax (camel-core tests have woodstox on the
classpath)
+ private static final XMLInputFactory JDK_INPUT =
XMLInputFactory.newDefaultFactory();
+ private static final XMLOutputFactory JDK_OUTPUT =
XMLOutputFactory.newDefaultFactory();
+
+ @TempDir
+ File tempDir;
+
+ private static XMLStreamReader reader(String xml) throws Exception {
+ return JDK_INPUT.createXMLStreamReader(new StringReader(xml));
+ }
+
+ @Test
+ public void testReadAllBytesOfLargeDocument() throws Exception {
+ StringBuilder sb = new StringBuilder("<a>");
+ for (int i = 0; i < 2000; i++) {
+ sb.append("<b>").append(i).append("</b>");
+ }
+ sb.append("</a>");
+
+ InputStream is = new XMLStreamReaderInputStream(reader(sb.toString()),
"utf-8", JDK_OUTPUT);
+ assertThat(is.read(new byte[1], 0, 0)).isZero();
+ String out = new String(is.readAllBytes(), StandardCharsets.UTF_8);
+ assertThat(out).endsWith("</a>").contains("<b>1999</b>");
+ }
+
+ @Test
+ public void testInputStreamWithOtherCharset() throws Exception {
+ InputStream is = new XMLStreamReaderInputStream(reader("<a>café</a>"),
"ISO-8859-1", JDK_OUTPUT);
+ String out = new String(is.readAllBytes(),
StandardCharsets.ISO_8859_1);
+ assertThat(out).contains("<a>café</a>");
+ }
+
+ @Test
+ public void testReaderWithUnqualifiedAttribute() throws Exception {
+ StringWriter sw = new StringWriter();
+ new XMLStreamReaderReader(reader("<a x=\"1\"><b>t</b></a>"),
JDK_OUTPUT).transferTo(sw);
+ assertThat(sw.toString()).contains("<a x=\"1\"><b>t</b></a>");
+ }
+
+ @Test
+ public void testReaderPositionedAtElement() throws Exception {
+ XMLStreamReader r = reader("<a><b>t</b></a>");
+ r.nextTag();
+ InputStream is = new XMLStreamReaderInputStream(r, "utf-8",
JDK_OUTPUT);
+ assertThat(new String(is.readAllBytes(),
StandardCharsets.UTF_8)).contains("<a><b>t</b></a>");
+ }
+
+ @Test
+ public void testTextNodesInMixedContent() throws Exception {
+ Document doc = new XmlConverter().toDOMDocument("<a>foo<b/>bar</a>",
null);
+ NodeList nl = (NodeList)
XPathFactory.newInstance().newXPath().evaluate("/a/text()", doc,
XPathConstants.NODESET);
+
+ DomConverter dom = new DomConverter();
+ assertThat(dom.toString(nl, null)).isEqualTo("foobar");
+ assertThat(dom.toString(nl.item(0), null)).isEqualTo("foo");
+ }
+
+ @Test
+ public void testAttrToString() throws Exception {
+ Document doc = new XmlConverter().toDOMDocument("<a x=\"1\"/>", null);
+ Attr attr = doc.getDocumentElement().getAttributeNode("x");
+ assertThat(new DomConverter().toString((Node) attr,
null)).isEqualTo("1");
+ }
+
+ @Test
+ public void testStreamReaderFromFileUsesDeclaredEncoding() throws
Exception {
+ File file = new File(tempDir, "latin1.xml");
+ Files.write(file.toPath(), "<?xml version=\"1.0\"
encoding=\"ISO-8859-1\"?><a>café</a>"
+ .getBytes(StandardCharsets.ISO_8859_1));
+
+ XMLStreamReader r = new StaxConverter().createXMLStreamReader(file,
null);
+ try {
+ r.nextTag();
+ assertThat(r.getElementText()).isEqualTo("café");
+ } finally {
+ r.close();
+ }
+ }
+
+ @Test
+ public void testLineNumberParserWithElementsAfterRoot() throws Exception {
+ String xml = "<beans>\n <camelContext><route/></camelContext>\n <bean
id=\"b\">\n hello\n </bean>\n</beans>";
+ Document doc = XmlLineNumberParser.parseXml(
+ new
ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)), null,
"camelContext", null);
+
assertThat(doc.getDocumentElement().getNodeName()).isEqualTo("camelContext");
+
assertThat(doc.getDocumentElement().getTextContent()).doesNotContain("hello");
+ }
+}
diff --git
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/DomConverter.java
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/DomConverter.java
index dda5c8265dc4..7cfc31a26416 100644
---
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/DomConverter.java
+++
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/DomConverter.java
@@ -69,7 +69,9 @@ public final class DomConverter {
int size = nodeList.getLength();
for (int i = 0; i < size; i++) {
Node node = nodeList.item(i);
- String s = toString(node, exchange);
+ // the text nodes of the list are appended each (and not
joined with their siblings, which may be
+ // in the list as well)
+ String s = node instanceof Text ? node.getNodeValue() :
toString(node, exchange);
if (org.apache.camel.util.ObjectHelper.isNotEmpty(s)) {
found = true;
buffer.append(s);
@@ -90,14 +92,17 @@ public final class DomConverter {
public String toString(Node node, Exchange exchange) throws
TransformerException {
String s;
if (node instanceof Text textNode) {
+ // join the adjacent text nodes (such as text and cdata), but not
the text after an element or comment
StringBuilder b = new StringBuilder(128);
b.append(textNode.getNodeValue());
- textNode = (Text) textNode.getNextSibling();
- while (textNode != null) {
- b.append(textNode.getNodeValue());
- textNode = (Text) textNode.getNextSibling();
+ Node sibling = textNode.getNextSibling();
+ while (sibling instanceof Text) {
+ b.append(sibling.getNodeValue());
+ sibling = sibling.getNextSibling();
}
s = b.toString();
+ } else if (node instanceof Attr attr) {
+ s = attr.getValue();
} else {
s = xml.toString(new DOMSource(node), exchange);
}
diff --git
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/StaxConverter.java
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/StaxConverter.java
index 294a8f1e38de..13307bf2a47f 100644
---
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/StaxConverter.java
+++
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/StaxConverter.java
@@ -153,13 +153,8 @@ public class StaxConverter {
@Converter(order = 8)
public XMLStreamReader createXMLStreamReader(File file, Exchange exchange)
throws XMLStreamException, FileNotFoundException {
- XMLInputFactory factory = getInputFactory();
- try {
- return factory.createXMLStreamReader(IOHelper.buffered(new
FileInputStream(file)),
- ExchangeHelper.getCharsetName(exchange));
- } finally {
- returnXMLInputFactory(factory);
- }
+ // same as from a stream (the encoding of the xml declaration is used
unless the exchange has a charset)
+ return createXMLStreamReader(new FileInputStream(file), exchange);
}
@Converter(order = 9)
@@ -209,13 +204,8 @@ public class StaxConverter {
@Converter(order = 13)
public XMLEventReader createXMLEventReader(File file, Exchange exchange)
throws XMLStreamException, FileNotFoundException {
- XMLInputFactory factory = getInputFactory();
- try {
- return factory.createXMLEventReader(IOHelper.buffered(new
FileInputStream(file)),
- ExchangeHelper.getCharsetName(exchange));
- } finally {
- returnXMLInputFactory(factory);
- }
+ // same as from a stream (the encoding of the xml declaration is used
unless the exchange has a charset)
+ return createXMLEventReader(new FileInputStream(file), exchange);
}
@Converter(order = 14)
diff --git
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/XMLStreamReaderInputStream.java
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/XMLStreamReaderInputStream.java
index 583a74d2b027..f548b68e624b 100644
---
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/XMLStreamReaderInputStream.java
+++
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/XMLStreamReaderInputStream.java
@@ -38,6 +38,7 @@ class XMLStreamReaderInputStream extends InputStream {
private final byte[] buffer;
private final String charset;
private int bpos;
+ private boolean started;
XMLStreamReaderInputStream(XMLStreamReader reader, String charset,
XMLOutputFactory outfactory) {
this.reader = reader;
@@ -63,6 +64,9 @@ class XMLStreamReaderInputStream extends InputStream {
@Override
public int read(byte[] b, int off, int len) throws IOException {
+ if (len == 0) {
+ return 0;
+ }
int tlen = 0;
while (len > 0) {
int n = ensureBuffering(len);
@@ -89,47 +93,22 @@ class XMLStreamReaderInputStream extends InputStream {
try {
// very first event
- if (XMLStreamConstants.START_DOCUMENT == reader.getEventType()) {
- writer.writeStartDocument(charset, "1.0");
+ if (!started) {
+ started = true;
+ int event = reader.getEventType();
+ if (XMLStreamConstants.START_DOCUMENT == event) {
+ writer.writeStartDocument(charset, "1.0");
+ } else if (XMLStreamConstants.START_ELEMENT == event) {
+ // the reader is already positioned at an element (such as
by nextTag)
+ writeEvent(event);
+ }
+ writer.flush();
}
if (chunk.size() < buffer.length) {
while (reader.hasNext()) {
- int code = reader.next();
- switch (code) {
- case XMLStreamConstants.END_DOCUMENT:
- writer.writeEndDocument();
- break;
- case XMLStreamConstants.START_ELEMENT:
- QName qname = reader.getName();
- writer.writeStartElement(qname.getPrefix(),
qname.getLocalPart(), qname.getNamespaceURI());
- for (int i = 0; i < reader.getAttributeCount();
i++) {
- String namespaceUri =
reader.getAttributeNamespace(i);
-
writer.writeAttribute(reader.getAttributePrefix(i), namespaceUri == null ? "" :
namespaceUri,
- reader.getAttributeLocalName(i),
- reader.getAttributeValue(i));
- }
- for (int i = 0; i < reader.getNamespaceCount();
i++) {
- String namespacePrefix =
reader.getNamespacePrefix(i);
- String namespaceURI =
reader.getNamespaceURI(i);
- writer.writeNamespace(namespacePrefix == null
? "" : namespacePrefix,
- namespaceURI == null ? "" :
namespaceURI);
- }
- break;
- case XMLStreamConstants.END_ELEMENT:
- writer.writeEndElement();
- break;
- case XMLStreamConstants.CHARACTERS:
- writer.writeCharacters(reader.getText());
- break;
- case XMLStreamConstants.COMMENT:
- writer.writeComment(reader.getText());
- break;
- case XMLStreamConstants.CDATA:
- writer.writeCData(reader.getText());
- break;
- default:
- break;
- }
+ writeEvent(reader.next());
+ // the writer may buffer (such as for another charset than
utf-8)
+ writer.flush();
// check if the chunk is full
final int csize = buffer.length - bpos;
@@ -155,6 +134,44 @@ class XMLStreamReaderInputStream extends InputStream {
}
}
+ private void writeEvent(int code) throws XMLStreamException {
+ switch (code) {
+ case XMLStreamConstants.END_DOCUMENT:
+ writer.writeEndDocument();
+ break;
+ case XMLStreamConstants.START_ELEMENT:
+ QName qname = reader.getName();
+ writer.writeStartElement(qname.getPrefix(),
qname.getLocalPart(), qname.getNamespaceURI());
+ for (int i = 0; i < reader.getAttributeCount(); i++) {
+ String namespaceUri = reader.getAttributeNamespace(i);
+ String prefix = reader.getAttributePrefix(i);
+ writer.writeAttribute(prefix == null ? "" : prefix,
namespaceUri == null ? "" : namespaceUri,
+ reader.getAttributeLocalName(i),
reader.getAttributeValue(i));
+ }
+ for (int i = 0; i < reader.getNamespaceCount(); i++) {
+ String namespacePrefix = reader.getNamespacePrefix(i);
+ String namespaceURI = reader.getNamespaceURI(i);
+ writer.writeNamespace(namespacePrefix == null ? "" :
namespacePrefix,
+ namespaceURI == null ? "" : namespaceURI);
+ }
+ break;
+ case XMLStreamConstants.END_ELEMENT:
+ writer.writeEndElement();
+ break;
+ case XMLStreamConstants.CHARACTERS:
+ writer.writeCharacters(reader.getText());
+ break;
+ case XMLStreamConstants.COMMENT:
+ writer.writeComment(reader.getText());
+ break;
+ case XMLStreamConstants.CDATA:
+ writer.writeCData(reader.getText());
+ break;
+ default:
+ break;
+ }
+ }
+
static class TrimmableByteArrayOutputStream extends ByteArrayOutputStream {
public void trim(int head, int tail) {
System.arraycopy(buf, head, buf, 0, count - head - tail);
diff --git
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/XMLStreamReaderReader.java
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/XMLStreamReaderReader.java
index c112a7c32515..97b46cb4101e 100644
---
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/XMLStreamReaderReader.java
+++
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/XMLStreamReaderReader.java
@@ -37,6 +37,7 @@ class XMLStreamReaderReader extends Reader {
private final TrimmableCharArrayWriter chunk;
private final char[] buffer;
private int bpos;
+ private boolean started;
XMLStreamReaderReader(XMLStreamReader reader, XMLOutputFactory outfactory)
{
this.reader = reader;
@@ -56,6 +57,9 @@ class XMLStreamReaderReader extends Reader {
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
+ if (len == 0) {
+ return 0;
+ }
int tlen = 0;
while (len > 0) {
int n = ensureBuffering(len);
@@ -81,44 +85,22 @@ class XMLStreamReaderReader extends Reader {
try {
// very first event
- if (XMLStreamConstants.START_DOCUMENT == reader.getEventType()) {
- writer.writeStartDocument("utf-8", "1.0");
+ if (!started) {
+ started = true;
+ int event = reader.getEventType();
+ if (XMLStreamConstants.START_DOCUMENT == event) {
+ writer.writeStartDocument("utf-8", "1.0");
+ } else if (XMLStreamConstants.START_ELEMENT == event) {
+ // the reader is already positioned at an element (such as
by nextTag)
+ writeEvent(event);
+ }
+ writer.flush();
}
if (chunk.size() < buffer.length) {
while (reader.hasNext()) {
- int code = reader.next();
- switch (code) {
- case XMLStreamConstants.END_DOCUMENT:
- writer.writeEndDocument();
- break;
- case XMLStreamConstants.START_ELEMENT:
- QName qname = reader.getName();
- writer.writeStartElement(qname.getPrefix(),
qname.getLocalPart(), qname.getNamespaceURI());
- for (int i = 0; i < reader.getAttributeCount();
i++) {
- writer.writeAttribute(
- reader.getAttributePrefix(i),
reader.getAttributeNamespace(i),
- reader.getAttributeLocalName(i),
- reader.getAttributeValue(i));
- }
- for (int i = 0; i < reader.getNamespaceCount();
i++) {
-
writer.writeNamespace(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
- }
- break;
- case XMLStreamConstants.END_ELEMENT:
- writer.writeEndElement();
- break;
- case XMLStreamConstants.CHARACTERS:
- writer.writeCharacters(reader.getText());
- break;
- case XMLStreamConstants.COMMENT:
- writer.writeComment(reader.getText());
- break;
- case XMLStreamConstants.CDATA:
- writer.writeCData(reader.getText());
- break;
- default:
- break;
- }
+ writeEvent(reader.next());
+ // the writer may buffer (such as for another charset than
utf-8)
+ writer.flush();
// check if the chunk is full
final int csize = buffer.length - bpos;
@@ -144,6 +126,44 @@ class XMLStreamReaderReader extends Reader {
}
}
+ private void writeEvent(int code) throws XMLStreamException {
+ switch (code) {
+ case XMLStreamConstants.END_DOCUMENT:
+ writer.writeEndDocument();
+ break;
+ case XMLStreamConstants.START_ELEMENT:
+ QName qname = reader.getName();
+ writer.writeStartElement(qname.getPrefix(),
qname.getLocalPart(), qname.getNamespaceURI());
+ for (int i = 0; i < reader.getAttributeCount(); i++) {
+ String namespaceUri = reader.getAttributeNamespace(i);
+ String prefix = reader.getAttributePrefix(i);
+ writer.writeAttribute(prefix == null ? "" : prefix,
namespaceUri == null ? "" : namespaceUri,
+ reader.getAttributeLocalName(i),
reader.getAttributeValue(i));
+ }
+ for (int i = 0; i < reader.getNamespaceCount(); i++) {
+ String namespacePrefix = reader.getNamespacePrefix(i);
+ String namespaceURI = reader.getNamespaceURI(i);
+ writer.writeNamespace(namespacePrefix == null ? "" :
namespacePrefix,
+ namespaceURI == null ? "" : namespaceURI);
+ }
+ break;
+ case XMLStreamConstants.END_ELEMENT:
+ writer.writeEndElement();
+ break;
+ case XMLStreamConstants.CHARACTERS:
+ writer.writeCharacters(reader.getText());
+ break;
+ case XMLStreamConstants.COMMENT:
+ writer.writeComment(reader.getText());
+ break;
+ case XMLStreamConstants.CDATA:
+ writer.writeCData(reader.getText());
+ break;
+ default:
+ break;
+ }
+ }
+
static class TrimmableCharArrayWriter extends CharArrayWriter {
public void trim(int head, int tail) {
System.arraycopy(buf, head, buf, 0, count - head - tail);
diff --git
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/util/xml/XmlLineNumberParser.java
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/util/xml/XmlLineNumberParser.java
index 813c881ad27f..8cbb1526c717 100644
---
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/util/xml/XmlLineNumberParser.java
+++
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/util/xml/XmlLineNumberParser.java
@@ -146,6 +146,7 @@ public final class XmlLineNumberParser {
final DefaultHandler handler = new DefaultHandler() {
private Locator locator;
private boolean found;
+ private boolean done;
@Override
public void setDocumentLocator(final Locator locator) {
@@ -167,6 +168,10 @@ public final class XmlLineNumberParser {
throws SAXException {
addTextIfNeeded();
+ if (done) {
+ // only the first root element is parsed (there can only
be one document element)
+ return;
+ }
if (rootNames != null && !found) {
if (isRootName(qName)) {
found = true;
@@ -205,6 +210,11 @@ public final class XmlLineNumberParser {
if (elementStack.isEmpty()) {
// Is this the root element?
doc.appendChild(closedEl);
+ if (rootNames != null) {
+ // the root element is done, so elements after it
are not parsed
+ found = false;
+ done = true;
+ }
} else {
final Element parentEl = elementStack.peek();
parentEl.appendChild(closedEl);
@@ -237,8 +247,9 @@ public final class XmlLineNumberParser {
if (el != null) {
final Node textNode =
doc.createTextNode(textBuffer.toString());
el.appendChild(textNode);
- textBuffer.delete(0, textBuffer.length());
}
+ // text outside the elements that are parsed is not added
to the next element
+ textBuffer.delete(0, textBuffer.length());
}
}
};