UPDATE Added additional tests, security config, and fix format issues (#7421)
This commit is contained in:
parent
a56d3b1e58
commit
a02fcfbb06
@ -3,6 +3,7 @@ package com.baeldung.xml.attribute;
|
||||
import org.dom4j.*;
|
||||
import org.dom4j.io.DocumentSource;
|
||||
import org.dom4j.io.SAXReader;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
@ -17,9 +18,12 @@ import java.util.List;
|
||||
public class Dom4jTransformer {
|
||||
private final Document input;
|
||||
|
||||
public Dom4jTransformer(String resourcePath) throws DocumentException {
|
||||
public Dom4jTransformer(String resourcePath) throws DocumentException, SAXException {
|
||||
// 1- Build the doc from the XML file
|
||||
SAXReader xmlReader = new SAXReader();
|
||||
xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
this.input = xmlReader.read(resourcePath);
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,8 @@ public class JaxpTransformer {
|
||||
// 1- Build the doc from the XML file
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
input = factory.newDocumentBuilder()
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
this.input = factory.newDocumentBuilder()
|
||||
.parse(resourcePath);
|
||||
}
|
||||
|
||||
@ -40,24 +41,24 @@ public class JaxpTransformer {
|
||||
// 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);
|
||||
NodeList nodes = (NodeList) xpath.evaluate(String.format("//*[contains(@%s, '%s')]", attribute, oldValue), this.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));
|
||||
// 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));
|
||||
xformer.transform(new DOMSource(this.input), new StreamResult(output));
|
||||
return output.toString();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,19 @@
|
||||
package com.baeldung.xml.attribute.jmh;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
import org.openjdk.jmh.runner.options.Options;
|
||||
@ -12,30 +24,23 @@ 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();
|
||||
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();
|
||||
public String dom4jBenchmark() throws DocumentException, TransformerException, SAXException {
|
||||
String path = this.getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
Dom4jTransformer transformer = new Dom4jTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
@ -46,9 +51,9 @@ public class AttributeBenchMark {
|
||||
|
||||
@Benchmark
|
||||
public String jooxBenchmark() throws IOException, SAXException {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
String path = this.getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
JooxTransformer transformer = new JooxTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
@ -59,9 +64,9 @@ public class AttributeBenchMark {
|
||||
|
||||
@Benchmark
|
||||
public String jaxpBenchmark() throws TransformerException, ParserConfigurationException, SAXException, IOException, XPathExpressionException {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
String path = this.getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
JaxpTransformer transformer = new JaxpTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
|
@ -2,14 +2,19 @@ package com.baeldung.xml.attribute;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactoryConfigurationError;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.xmlunit.assertj.XmlAssert.assertThat;
|
||||
|
||||
/**
|
||||
@ -18,10 +23,9 @@ import static org.xmlunit.assertj.XmlAssert.assertThat;
|
||||
public class Dom4jProcessorUnitTest {
|
||||
|
||||
@Test
|
||||
public void givenXmlWithAttributes_whenModifyAttribute_thenGetXmlUpdated() throws TransformerFactoryConfigurationError, TransformerException, DocumentException {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
public void givenXmlWithAttributes_whenModifyAttribute_thenGetXmlUpdated() throws TransformerFactoryConfigurationError, TransformerException, DocumentException, SAXException {
|
||||
String path = getClass().getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
Dom4jTransformer transformer = new Dom4jTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
@ -33,23 +37,32 @@ public class Dom4jProcessorUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenTwoXml_whenModifyAttribute_thenGetSimilarXml() throws IOException, TransformerFactoryConfigurationError, TransformerException, URISyntaxException, DocumentException {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
public void givenTwoXml_whenModifyAttribute_thenGetSimilarXml() throws IOException, TransformerFactoryConfigurationError, TransformerException, URISyntaxException, DocumentException, SAXException {
|
||||
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 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();
|
||||
assertThat(result).and(expectedXml)
|
||||
.areSimilar();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenXmlXee_whenInit_thenThrowException() throws IOException, SAXException, ParserConfigurationException, XPathExpressionException, TransformerFactoryConfigurationError, TransformerException {
|
||||
String path = getClass().getResource("/xml/xee_attribute.xml")
|
||||
.toString();
|
||||
|
||||
assertThatThrownBy(() -> {
|
||||
|
||||
new Dom4jTransformer(path);
|
||||
|
||||
}).isInstanceOf(DocumentException.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.baeldung.xml.attribute;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.xmlunit.assertj.XmlAssert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -11,6 +12,7 @@ import javax.xml.xpath.XPathExpressionException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
/**
|
||||
* Unit test for {@link JaxpTransformer}.
|
||||
@ -31,4 +33,16 @@ public class JaxpProcessorUnitTest {
|
||||
assertThat(result).hasXPath("//*[contains(@customer, 'false')]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenXmlXee_whenInit_thenThrowException() throws IOException, SAXException, ParserConfigurationException, XPathExpressionException, TransformerFactoryConfigurationError, TransformerException {
|
||||
String path = getClass().getResource("/xml/xee_attribute.xml")
|
||||
.toString();
|
||||
|
||||
assertThatThrownBy(() -> {
|
||||
|
||||
new JaxpTransformer(path);
|
||||
|
||||
}).isInstanceOf(SAXParseException.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,13 +2,19 @@ package com.baeldung.xml.attribute;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactoryConfigurationError;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.xmlunit.assertj.XmlAssert.assertThat;
|
||||
|
||||
/**
|
||||
@ -18,9 +24,8 @@ public class JooxProcessorUnitTest {
|
||||
|
||||
@Test
|
||||
public void givenXmlWithAttributes_whenModifyAttribute_thenGetXmlUpdated() throws IOException, SAXException, TransformerFactoryConfigurationError {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
String path = getClass().getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
JooxTransformer transformer = new JooxTransformer(path);
|
||||
String attribute = "customer";
|
||||
String oldValue = "true";
|
||||
@ -33,22 +38,31 @@ public class JooxProcessorUnitTest {
|
||||
|
||||
@Test
|
||||
public void givenTwoXml_whenModifyAttribute_thenGetSimilarXml() throws IOException, TransformerFactoryConfigurationError, URISyntaxException, SAXException {
|
||||
String path = getClass()
|
||||
.getResource("/xml/attribute.xml")
|
||||
.toString();
|
||||
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 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();
|
||||
assertThat(result).and(expectedXml)
|
||||
.areSimilar();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenXmlXee_whenInit_thenThrowException() throws IOException, SAXException, ParserConfigurationException, XPathExpressionException, TransformerFactoryConfigurationError, TransformerException {
|
||||
String path = getClass().getResource("/xml/xee_attribute.xml")
|
||||
.toString();
|
||||
|
||||
assertThatThrownBy(() -> {
|
||||
|
||||
new JooxTransformer(path);
|
||||
|
||||
}).isInstanceOf(SAXParseException.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
9
xml/src/test/resources/xml/xee_attribute.xml
Normal file
9
xml/src/test/resources/xml/xee_attribute.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE notification [
|
||||
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
|
||||
<notification id="5">
|
||||
<bad_entry>&xxe;
|
||||
</bad_entry>
|
||||
<to customer="true">john@email.com</to>
|
||||
<from>mary@email.com</from>
|
||||
</notification>
|
Loading…
x
Reference in New Issue
Block a user