Sample code for BAEL-2539 (#7238)
* UPDATE Added junit5 & assertJ configuration in order to run tests * FEAT Added JaxpTransformer basic implementation * UPDATE Added JAXP polish version * UPDATE Polish jaxp implementation * FEAT Added complete JAXP Sample * FIX typo * Added additional samples and JMH benchmark * FIX indent in Joox sample
This commit is contained in:
committed by
KevinGilmore
parent
79a50387d1
commit
4bf6b77067
@@ -0,0 +1,45 @@
|
||||
package com.baeldung.xml.attribute;
|
||||
|
||||
import org.dom4j.*;
|
||||
import org.dom4j.io.DocumentSource;
|
||||
import org.dom4j.io.SAXReader;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.List;
|
||||
|
||||
public class Dom4jTransformer {
|
||||
private final Document input;
|
||||
|
||||
public Dom4jTransformer(String resourcePath) throws DocumentException {
|
||||
// 1- Build the doc from the XML file
|
||||
SAXReader xmlReader = new SAXReader();
|
||||
this.input = xmlReader.read(resourcePath);
|
||||
}
|
||||
|
||||
public String modifyAttribute(String attribute, String oldValue, String newValue) throws TransformerException {
|
||||
// 2- Locate the node(s) with xpath, we can use index and iterator too.
|
||||
String expr = String.format("//*[contains(@%s, '%s')]", attribute, oldValue);
|
||||
XPath xpath = DocumentHelper.createXPath(expr);
|
||||
List<Node> nodes = xpath.selectNodes(input);
|
||||
// 3- Make the change on the selected nodes
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
Element element = (Element) nodes.get(i);
|
||||
element.addAttribute(attribute, newValue);
|
||||
}
|
||||
// 4- Save the result to a new XML doc
|
||||
TransformerFactory factory = TransformerFactory.newInstance();
|
||||
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
Transformer xformer = factory.newTransformer();
|
||||
xformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
Writer output = new StringWriter();
|
||||
xformer.transform(new DocumentSource(input), new StreamResult(output));
|
||||
return output.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.baeldung.xml.attribute;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.TransformerFactoryConfigurationError;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
public class JaxpTransformer {
|
||||
|
||||
private Document input;
|
||||
|
||||
public JaxpTransformer(String resourcePath) throws SAXException, IOException, ParserConfigurationException {
|
||||
// 1- Build the doc from the XML file
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
input = factory.newDocumentBuilder()
|
||||
.parse(resourcePath);
|
||||
}
|
||||
|
||||
public String modifyAttribute(String attribute, String oldValue, String newValue) throws XPathExpressionException, TransformerFactoryConfigurationError, TransformerException {
|
||||
// 2- Locate the node(s) with xpath
|
||||
XPath xpath = XPathFactory.newInstance()
|
||||
.newXPath();
|
||||
NodeList nodes = (NodeList) xpath.evaluate(String.format("//*[contains(@%s, '%s')]", attribute, oldValue), input, XPathConstants.NODESET);
|
||||
// 3- Make the change on the selected nodes
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
Element value = (Element) nodes.item(i);
|
||||
value.setAttribute(attribute, newValue);
|
||||
}
|
||||
//Stream api syntax
|
||||
// IntStream
|
||||
// .range(0, nodes.getLength())
|
||||
// .mapToObj(i -> (Element) nodes.item(i))
|
||||
// .forEach(value -> value.setAttribute(attribute, newValue));
|
||||
// 4- Save the result to a new XML doc
|
||||
TransformerFactory factory = TransformerFactory.newInstance();
|
||||
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
Transformer xformer = factory.newTransformer();
|
||||
xformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
Writer output = new StringWriter();
|
||||
xformer.transform(new DOMSource(input), new StreamResult(output));
|
||||
return output.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.baeldung.xml.attribute;
|
||||
|
||||
import org.joox.JOOX;
|
||||
import org.joox.Match;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.transform.TransformerFactoryConfigurationError;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.joox.JOOX.$;
|
||||
|
||||
public class JooxTransformer {
|
||||
|
||||
private final Document input;
|
||||
|
||||
public JooxTransformer(String resourcePath) throws SAXException, IOException {
|
||||
// 1- Build the doc from the XML file
|
||||
DocumentBuilder builder = JOOX.builder();
|
||||
input = builder.parse(resourcePath);
|
||||
}
|
||||
|
||||
public String modifyAttribute(String attribute, String oldValue, String newValue) throws TransformerFactoryConfigurationError {
|
||||
// 2- Select the document
|
||||
Match $ = $(input);
|
||||
// 3 - Find node to modify
|
||||
String expr = String.format("//*[contains(@%s, '%s')]", attribute, oldValue);
|
||||
$
|
||||
// .find("to") or with xpath
|
||||
.xpath(expr)
|
||||
.get()
|
||||
.stream()
|
||||
.forEach(e -> e.setAttribute(attribute, newValue));
|
||||
// 4- Return result as String
|
||||
return $.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.baeldung.xml.attribute.jmh;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
import org.openjdk.jmh.runner.options.Options;
|
||||
import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import com.baeldung.xml.attribute.Dom4jTransformer;
|
||||
import com.baeldung.xml.attribute.JaxpTransformer;
|
||||
import com.baeldung.xml.attribute.JooxTransformer;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
public class AttributeBenchMark {
|
||||
|
||||
public static void main(String[] args) throws RunnerException {
|
||||
Options opt = new OptionsBuilder()
|
||||
.include(AttributeBenchMark.class.getSimpleName())
|
||||
.forks(1)
|
||||
.build();
|
||||
new Runner(opt).run();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String dom4jBenchmark() throws DocumentException, TransformerException {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
Dom4jTransformer transformer = new Dom4jTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
String newValue = "false";
|
||||
|
||||
return transformer.modifyAttribute(attribute, oldValue, newValue);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String jooxBenchmark() throws IOException, SAXException {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
JooxTransformer transformer = new JooxTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
String newValue = "false";
|
||||
|
||||
return transformer.modifyAttribute(attribute, oldValue, newValue);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String jaxpBenchmark() throws TransformerException, ParserConfigurationException, SAXException, IOException, XPathExpressionException {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
JaxpTransformer transformer = new JaxpTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
String newValue = "false";
|
||||
|
||||
return transformer.modifyAttribute(attribute, oldValue, newValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<notification id="5">
|
||||
<to customer="true">john@email.com</to>
|
||||
<from>mary@email.com</from>
|
||||
</notification>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<notification id="5">
|
||||
<to customer="false">john@email.com</to>
|
||||
<from>mary@email.com</from>
|
||||
</notification>
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.baeldung.xml.attribute;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactoryConfigurationError;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.xmlunit.assertj.XmlAssert.assertThat;
|
||||
|
||||
/**
|
||||
* Unit test for {@link Dom4jTransformer}.
|
||||
*/
|
||||
public class Dom4jProcessorUnitTest {
|
||||
|
||||
@Test
|
||||
public void givenXmlWithAttributes_whenModifyAttribute_thenGetXmlUpdated() throws TransformerFactoryConfigurationError, TransformerException, DocumentException {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
Dom4jTransformer transformer = new Dom4jTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
String newValue = "false";
|
||||
|
||||
String result = transformer.modifyAttribute(attribute, oldValue, newValue);
|
||||
|
||||
assertThat(result).hasXPath("//*[contains(@customer, 'false')]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenTwoXml_whenModifyAttribute_thenGetSimilarXml() throws IOException, TransformerFactoryConfigurationError, TransformerException, URISyntaxException, DocumentException {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
Dom4jTransformer transformer = new Dom4jTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
String newValue = "false";
|
||||
String expectedXml = new String(Files.readAllBytes((Paths.get(getClass()
|
||||
.getResource("/xml/attribute_expected.xml")
|
||||
.toURI()))));
|
||||
|
||||
String result = transformer.modifyAttribute(attribute, oldValue, newValue);
|
||||
|
||||
assertThat(result)
|
||||
.and(expectedXml)
|
||||
.areSimilar();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.baeldung.xml.attribute;
|
||||
|
||||
import static org.xmlunit.assertj.XmlAssert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactoryConfigurationError;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Unit test for {@link JaxpTransformer}.
|
||||
*/
|
||||
public class JaxpProcessorUnitTest {
|
||||
|
||||
@Test
|
||||
public void givenXmlWithAttributes_whenModifyAttribute_thenGetXmlUpdated() throws IOException, SAXException, ParserConfigurationException, XPathExpressionException, TransformerFactoryConfigurationError, TransformerException {
|
||||
String path = getClass().getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
JaxpTransformer transformer = new JaxpTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
String newValue = "false";
|
||||
|
||||
String result = transformer.modifyAttribute(attribute, oldValue, newValue);
|
||||
|
||||
assertThat(result).hasXPath("//*[contains(@customer, 'false')]");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.baeldung.xml.attribute;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.transform.TransformerFactoryConfigurationError;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.xmlunit.assertj.XmlAssert.assertThat;
|
||||
|
||||
/**
|
||||
* Unit test for {@link JooxTransformer}.
|
||||
*/
|
||||
public class JooxProcessorUnitTest {
|
||||
|
||||
@Test
|
||||
public void givenXmlWithAttributes_whenModifyAttribute_thenGetXmlUpdated() throws IOException, SAXException, TransformerFactoryConfigurationError {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
JooxTransformer transformer = new JooxTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
String newValue = "false";
|
||||
|
||||
String result = transformer.modifyAttribute(attribute, oldValue, newValue);
|
||||
|
||||
assertThat(result).hasXPath("//*[contains(@customer, 'false')]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenTwoXml_whenModifyAttribute_thenGetSimilarXml() throws IOException, TransformerFactoryConfigurationError, URISyntaxException, SAXException {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
JooxTransformer transformer = new JooxTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
String newValue = "false";
|
||||
String expectedXml = new String(Files.readAllBytes((Paths.get(getClass()
|
||||
.getResource("/xml/attribute_expected.xml")
|
||||
.toURI()))));
|
||||
|
||||
String result = transformer.modifyAttribute(attribute, oldValue, newValue);
|
||||
|
||||
assertThat(result)
|
||||
.and(expectedXml)
|
||||
.areSimilar();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<notification id="5">
|
||||
<to customer="true">john@email.com</to>
|
||||
<from>mary@email.com</from>
|
||||
</notification>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<notification id="5">
|
||||
<to customer="false">john@email.com</to>
|
||||
<from>mary@email.com</from>
|
||||
</notification>
|
||||
Reference in New Issue
Block a user