diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitXml.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitXml.java index a8453bbc86..6d5b30aacb 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitXml.java +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitXml.java @@ -21,8 +21,10 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map.Entry; import java.util.Set; import javax.xml.parsers.ParserConfigurationException; @@ -193,6 +195,7 @@ public class SplitXml extends AbstractProcessor { private final int splitDepth; private final StringBuilder sb = new StringBuilder(XML_PROLOGUE); private int depth = 0; + private HashMap prefixMap = new HashMap<>(); public XmlSplitterSaxParser(XmlElementNotifier notifier, int splitDepth) { this.notifier = notifier; @@ -261,6 +264,7 @@ public class SplitXml extends AbstractProcessor { @Override public void endPrefixMapping(String prefix) throws SAXException { + prefixMap.remove(prefixToNamespace(prefix)); } @Override @@ -293,19 +297,49 @@ public class SplitXml extends AbstractProcessor { sb.append("<"); sb.append(qName); + final Set attributeNames = new HashSet<>(); int attCount = atts.getLength(); for (int i = 0; i < attCount; i++) { String attName = atts.getQName(i); + attributeNames.add(attName); String attValue = StringEscapeUtils.escapeXml10(atts.getValue(i)); sb.append(" ").append(attName).append("=").append("\"").append(attValue).append("\""); } + // If this is the first node we're outputting write out + // any additional namespace declarations that are required + if (splitDepth == newDepth - 1) { + for (Entry entry : prefixMap.entrySet()) { + // If we've already added this namespace as an attribute then continue + if (attributeNames.contains(entry.getKey())) { + continue; + } + sb.append(" "); + sb.append(entry.getKey()); + sb.append("=\""); + sb.append(entry.getValue()); + sb.append("\" "); + } + } + sb.append(">"); } } @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { + final String ns = prefixToNamespace(prefix); + prefixMap.put(ns, uri); + } + + private String prefixToNamespace(String prefix) { + final String ns; + if (prefix.length() == 0) { + ns = "xmlns"; + } else { + ns="xmlns:"+prefix; + } + return ns; } } diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestSplitXml.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestSplitXml.java index a84e031bbd..e0a2e0910e 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestSplitXml.java +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestSplitXml.java @@ -92,6 +92,25 @@ public class TestSplitXml { parseFlowFiles(runner.getFlowFilesForRelationship(SplitXml.REL_SPLIT)); } + @Test + public void testNamespaceDeclarations() throws Exception { + // Configure a namespace aware parser to ensure namespace + // declarations are handled correctly. + factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + saxParser = factory.newSAXParser( ); + + final TestRunner runner = TestRunners.newTestRunner(new SplitXml()); + runner.setProperty(SplitXml.SPLIT_DEPTH, "3"); + runner.enqueue(Paths.get("src/test/resources/TestXml/namespace.xml")); + runner.run(); + runner.assertTransferCount(SplitXml.REL_ORIGINAL, 1); + runner.assertTransferCount(SplitXml.REL_SPLIT, 2); + + parseFlowFiles(runner.getFlowFilesForRelationship(SplitXml.REL_ORIGINAL)); + parseFlowFiles(runner.getFlowFilesForRelationship(SplitXml.REL_SPLIT)); + } + public void parseFlowFiles(List flowfiles) throws Exception, SAXException { for (MockFlowFile out : flowfiles) { final byte[] outData = out.toByteArray(); @@ -99,4 +118,5 @@ public class TestSplitXml { saxParser.parse(new InputSource(new StringReader(outXml)), new DefaultHandler()); } } + } diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestXml/namespace.xml b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestXml/namespace.xml new file mode 100755 index 0000000000..315cc851c8 --- /dev/null +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestXml/namespace.xml @@ -0,0 +1,25 @@ + + + + + + Hello & Goodbye + + + World + + +