import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
-
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
-
import org.apache.commons.lang3.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
*/
public final class XmlTool {
- private static final Map<String, Integer> ENTITIES = new HashMap<>();
- private static final Logger logger = LoggerFactory.getLogger(XmlTool.class);
- static {
- ENTITIES.put("amp", 38);
- ENTITIES.put("quot", 34);
- ENTITIES.put("lt", 60);
- ENTITIES.put("gt", 62);
- }
+ private static final Map<String, Integer> ENTITIES = new HashMap<>();
+ private static final Logger logger = LoggerFactory.getLogger(XmlTool.class);
+ static {
+ ENTITIES.put("amp", 38);
+ ENTITIES.put("quot", 34);
+ ENTITIES.put("lt", 60);
+ ENTITIES.put("gt", 62);
+ }
- /**
+ /**
* Instantiation is not allowed.
*/
- private XmlTool() {
+ private XmlTool() {}
+
+ /**
+ * Normalizes and formats XML. This method consolidates and moves all namespace declarations to the root element.
+ * The result will not have an XML prolog or a trailing newline.
+ *
+ * @param xml the XML to normalize
+ * @throws IOException
+ * @throws TransformerException
+ * @throws ParserConfigurationException
+ * @throws SAXException
+ * @throws XPathExpressionException
+ */
+ public static String normalize(Object xml) throws IOException, TransformerException, ParserConfigurationException,
+ SAXException, XPathExpressionException {
+
+ if (xml == null) {
+ return null;
+ }
+
+ Source xsltSource = new StreamSource(new StringReader(readResourceFile("normalize-namespaces.xsl")));
+
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ dbFactory.setNamespaceAware(true);
+ dbFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ dbFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ DocumentBuilder db = dbFactory.newDocumentBuilder();
+ InputSource source = new InputSource(new StringReader(String.valueOf(xml)));
+ Document doc = db.parse(source);
+
+ // Start of code to remove whitespace outside of tags
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ NodeList nodeList = (NodeList) xPath.evaluate("//text()[normalize-space()='']", doc, XPathConstants.NODESET);
+
+ for (int i = 0; i < nodeList.getLength(); ++i) {
+ Node node = nodeList.item(i);
+ node.getParentNode().removeChild(node);
+ }
+ // End of code to remove whitespace outside of tags
+
+ // the factory pattern supports different XSLT processors
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ Transformer transformer = transformerFactory.newTransformer(xsltSource);
+
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
+
+ StringWriter writer = new StringWriter();
+ transformer.transform(new DOMSource(doc), new StreamResult(writer));
+ return writer.toString().trim();
}
-
- /**
- * Normalizes and formats XML. This method consolidates and moves all namespace
- * declarations to the root element. The result will not have an XML prolog or
- * a trailing newline.
- * @param xml the XML to normalize
- * @throws IOException
- * @throws TransformerException
- * @throws ParserConfigurationException
- * @throws SAXException
- * @throws XPathExpressionException
- */
- public static String normalize(Object xml) throws IOException, TransformerException,
- ParserConfigurationException, SAXException, XPathExpressionException {
-
- if (xml == null) {
- return null;
- }
-
- Source xsltSource = new StreamSource(new StringReader(
- readResourceFile("normalize-namespaces.xsl")));
-
- DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
- dbFactory.setNamespaceAware(true);
- dbFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
- dbFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- DocumentBuilder db = dbFactory.newDocumentBuilder();
- InputSource source = new InputSource(new StringReader(String.valueOf(xml)));
- Document doc = db.parse(source);
-
- // Start of code to remove whitespace outside of tags
- XPath xPath = XPathFactory.newInstance().newXPath();
- NodeList nodeList = (NodeList) xPath.evaluate(
- "//text()[normalize-space()='']", doc, XPathConstants.NODESET);
-
- for (int i = 0; i < nodeList.getLength(); ++i) {
- Node node = nodeList.item(i);
- node.getParentNode().removeChild(node);
- }
- // End of code to remove whitespace outside of tags
-
- // the factory pattern supports different XSLT processors
- TransformerFactory transformerFactory = TransformerFactory.newInstance();
- transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
- Transformer transformer = transformerFactory.newTransformer(xsltSource);
-
- transformer.setOutputProperty(OutputKeys.INDENT, "yes");
- transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
- transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
- transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
-
- StringWriter writer = new StringWriter();
- transformer.transform(new DOMSource(doc), new StreamResult(writer));
- return writer.toString().trim();
- }
-
- /**
- * Encodes a value so it can be used inside an XML text element.
- * @param value the string to encode
- * @return the encoded string
- */
- public static String encode(Object value) {
- if (value == null) {
- return null;
- }
- return StringEscapeUtils.escapeXml11(value.toString());
- }
-
- /**
- * Removes the preamble, if present, from an XML document.
- * @param xml the XML document
- * @return a possibly modified document
- */
- public static String removePreamble(Object xml) {
- if (xml == null) {
- return null;
- }
-
- return String.valueOf(xml).replaceAll("(<\\?[^<]*\\?>\\s*[\\r\\n]*)?", "");
- }
- /**
- * Removes namespaces and namespace declarations from an XML document.
- * @param xml the XML document
- * @return a possibly modified document
- */
- public static String removeNamespaces(Object xml) {
- if (xml == null) {
- logger.debug("removeNamespaces input object is null , returning null");
- return null;
- }
-
- String text = String.valueOf(xml);
-
- // remove xmlns declaration
- text = text.replaceAll("xmlns.*?(\"|\').*?(\"|\')", "");
- // remove opening tag prefix
- text = text.replaceAll("(<)(\\w+:)(.*?>)", "$1$3");
- // remove closing tags prefix
- text = text.replaceAll("(</)(\\w+:)(.*?>)", "$1$3");
- // remove extra spaces left when xmlns declarations are removed
- text = text.replaceAll("\\s+>", ">");
-
- return text;
- }
+ /**
+ * Encodes a value so it can be used inside an XML text element.
+ *
+ * @param value the string to encode
+ * @return the encoded string
+ */
+ public static String encode(Object value) {
+ if (value == null) {
+ return null;
+ }
+ return StringEscapeUtils.escapeXml11(value.toString());
+ }
+ /**
+ * Removes the preamble, if present, from an XML document.
+ *
+ * @param xml the XML document
+ * @return a possibly modified document
+ */
+ public static String removePreamble(Object xml) {
+ if (xml == null) {
+ return null;
+ }
- /**
- * Reads the specified resource file and return the contents as a string.
- * @param file Name of the resource file
- * @return the contents of the resource file as a String
- * @throws IOException if there is a problem reading the file
- */
- private static String readResourceFile(String file) throws IOException {
+ return String.valueOf(xml).replaceAll("(<\\?[^<]*\\?>\\s*[\\r\\n]*)?", "");
+ }
- try (InputStream stream = XmlTool.class.getClassLoader().getResourceAsStream(file);
- Reader reader = new InputStreamReader(stream, "UTF-8")) {
+ /**
+ * Removes namespaces and namespace declarations from an XML document.
+ *
+ * @param xml the XML document
+ * @return a possibly modified document
+ */
+ public static String removeNamespaces(Object xml) {
+ if (xml == null) {
+ logger.debug("removeNamespaces input object is null , returning null");
+ return null;
+ }
+
+ String text = String.valueOf(xml);
+
+ // remove xmlns declaration
+ text = text.replaceAll("xmlns.*?(\"|\').*?(\"|\')", "");
+ // remove opening tag prefix
+ text = text.replaceAll("(<)(\\w+:)(.*?>)", "$1$3");
+ // remove closing tags prefix
+ text = text.replaceAll("(</)(\\w+:)(.*?>)", "$1$3");
+ // remove extra spaces left when xmlns declarations are removed
+ text = text.replaceAll("\\s+>", ">");
+
+ return text;
+ }
- StringBuilder out = new StringBuilder();
- char[] buf = new char[1024];
- int n;
- while ((n = reader.read(buf)) >= 0) {
- out.append(buf, 0, n);
- }
- return out.toString();
- } catch (Exception e) {
- logger.debug("Exception at readResourceFile stream: " + e);
- return null;
- }
- }
-
- /**
- * Parses the XML document String for the first occurrence of the specified element tag.
- * If found, the value associated with that element tag is replaced with the new value
- * and a String containing the modified XML document is returned. If the XML passed is
- * null or the element tag is not found in the document, null will be returned.
- * @param xml String containing the original XML document.
- * @param elementTag String containing the tag of the element to be modified.
- * @param newValue String containing the new value to be used to modify the corresponding element.
- * @return the contents of the modified XML document as a String or null/empty if the modification failed.
- * @throws IOException, TransformerException, ParserConfigurationException, SAXException
- */
- public static Optional<String> modifyElement(String xml, String elementTag, String newValue) throws IOException, TransformerException,
- ParserConfigurationException, SAXException {
+ /**
+ * Reads the specified resource file and return the contents as a string.
+ *
+ * @param file Name of the resource file
+ * @return the contents of the resource file as a String
+ * @throws IOException if there is a problem reading the file
+ */
+ private static String readResourceFile(String file) throws IOException {
+
+ try (InputStream stream = XmlTool.class.getClassLoader().getResourceAsStream(file);
+ Reader reader = new InputStreamReader(stream, "UTF-8")) {
+
+ StringBuilder out = new StringBuilder();
+ char[] buf = new char[1024];
+ int n;
+
+ while ((n = reader.read(buf)) >= 0) {
+ out.append(buf, 0, n);
+ }
+ return out.toString();
+ } catch (Exception e) {
+ logger.debug("Exception at readResourceFile stream: " + e);
+ return null;
+ }
+ }
- if (xml == null || xml.isEmpty()) {
- // no XML content to be modified, return empty
- return Optional.empty();
- }
-
- DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
- dbFactory.setNamespaceAware(true);
- dbFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
- dbFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- DocumentBuilder db = dbFactory.newDocumentBuilder();
- InputSource source = new InputSource(new StringReader(xml));
- Document doc = db.parse(source);
-
- Node modNode = doc.getElementsByTagName(elementTag).item(0);
- if (modNode == null) {
- // did not find the specified element to be modified, return empty
- //System.out.println("Did not find element tag " + elementTag + " in XML");
- return Optional.empty();
- } else {
- modNode.setTextContent(newValue);
- }
-
- TransformerFactory transformerFactory = TransformerFactory.newInstance();
- Transformer transformer = transformerFactory.newTransformer();
- StringWriter writer = new StringWriter();
- transformer.transform(new DOMSource(doc), new StreamResult(writer));
- // return the modified String representation of the XML
- return Optional.of(writer.toString().trim());
- }
+ /**
+ * Parses the XML document String for the first occurrence of the specified element tag. If found, the value
+ * associated with that element tag is replaced with the new value and a String containing the modified XML document
+ * is returned. If the XML passed is null or the element tag is not found in the document, null will be returned.
+ *
+ * @param xml String containing the original XML document.
+ * @param elementTag String containing the tag of the element to be modified.
+ * @param newValue String containing the new value to be used to modify the corresponding element.
+ * @return the contents of the modified XML document as a String or null/empty if the modification failed.
+ * @throws IOException, TransformerException, ParserConfigurationException, SAXException
+ */
+ public static Optional<String> modifyElement(String xml, String elementTag, String newValue)
+ throws IOException, TransformerException, ParserConfigurationException, SAXException {
+
+ if (xml == null || xml.isEmpty()) {
+ // no XML content to be modified, return empty
+ return Optional.empty();
+ }
+
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ dbFactory.setNamespaceAware(true);
+ dbFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ dbFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ DocumentBuilder db = dbFactory.newDocumentBuilder();
+ InputSource source = new InputSource(new StringReader(xml));
+ Document doc = db.parse(source);
+
+ Node modNode = doc.getElementsByTagName(elementTag).item(0);
+ if (modNode == null) {
+ // did not find the specified element to be modified, return empty
+ // System.out.println("Did not find element tag " + elementTag + " in XML");
+ return Optional.empty();
+ } else {
+ modNode.setTextContent(newValue);
+ }
+
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer = transformerFactory.newTransformer();
+ StringWriter writer = new StringWriter();
+ transformer.transform(new DOMSource(doc), new StreamResult(writer));
+ // return the modified String representation of the XML
+ return Optional.of(writer.toString().trim());
+ }
}