Now I probably tried 30+ times to validate this task but I don't get it and in the meanwhile I think I may misunderstand the questions.
- What I thought is, that I have an object that I should marshall into xml.
- If there are tags matching a certain name, I need to add a comment before that tag.
- If there are tags matching that very name I should check the tag's content if it needs instead to be added as CDATA (if it contains one of the escape chars <>&'")
I did that and I get output that looks OK to me (but I'm not sure as I'm even not sure if I understand what CG wants from me)?
😏😩🥺 hmmmm, mmmm
package com.codegym.task.task33.task3309;
/*
Comments inside XML
*/
/*
approaches:
1. (probably easy) marshall into a String, search the string for the tag, replace tag with comment + tag
-> coded this, but I think it does not meet all requirements as it does not add CDATA
==> dropped that approach
2. from the comments to this task: marshall into a DOM, loop over the nodes, change to CDATA and add comments
if necessary
*/
import org.w3c.dom.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
public class Solution {
public static String toXmlWithComment(Object obj, String tagName, String comment) {
Document document = null;
try {
// first create the document and read the object as XML into it
document = createXMLDocument();
marshalXMLIntoDocument(obj, document);
// now find the nodes matching tagName using Document.getElementsByTagName()
// then loop through the resulting NodeList to get Nodes and add comments to that nodes
NodeList nodeList = document.getElementsByTagName(tagName);
for (int i = 0; i < nodeList.getLength(); i++) {
// currentNode is the current iteration's node matching tagName, before that we insert the comment
Node currentNode = nodeList.item(i);
currentNode.getParentNode().insertBefore(document.createComment(comment), currentNode);
// requirement: The XML serialization of obj may contain CDATA sections with the specified tag. Don't add comments before them.
// So I walked through the DOM and found that there is no CDATA inside, just TEXT. Having eg.
// <![CDATA[<div>My text</div>]]> inside a String to marshall just adds TEXT to the DOM.
// at this point, when adding the comments I already found the tagNames in question. Now I check the current
// tags TEXT if it contains escape chars <>&'" and if so i create a CDATA before the current node with TEXT as content
String nodeText = currentNode.getTextContent();
if (nodeText.matches(".*[<>&'\"].*")) { // contains escape char, then resave as CDATA
currentNode.setTextContent(""); // needs to be set to empty before appending the CDATA, otherwise no CDATA gets added
currentNode.appendChild(document.createCDATASection(nodeText));
// as a result I get a CDATA field wrapped around the former tag's text instead of the text
}
}
return transformDocumentToString(document);
} catch (JAXBException | ParserConfigurationException | TransformerException e) {
throw new RuntimeException(e);
}
}
/**
* uses Transformer to convert the Document into a String ()
*
* @param document
* @return
* @throws TransformerException
*/
public static String transformDocumentToString(Document document) throws TransformerException {
StringWriter sw = new StringWriter();
// create a Transformer and pass it a DomSource, the document and a Result, the StringWriter
Transformer tf = TransformerFactory.newInstance().newTransformer();
// set indentation to true
tf.setOutputProperty(OutputKeys.INDENT, "yes");
tf.transform(new DOMSource(document), new StreamResult(sw));
return sw.toString();
}
/**
* Used to read obj data into a Document as XML using JAXB marshalling
*
* @param obj
* @param document
* @throws JAXBException
*/
public static void marshalXMLIntoDocument(Object obj, Document document) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
// marshall directly into the document
marshaller.marshal(obj, document);
}
/**
* creates an empty w3c Document using DocumentBuilderFactory and DocumentBuilder to hold HTML or XML DOMs
*
* @return
* @throws ParserConfigurationException
*/
public static Document createXMLDocument() throws ParserConfigurationException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
return db.newDocument();
}
/**
* main method to test toXmlWithComment()
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
SampleData sampleData = new SampleData();
System.out.println(toXmlWithComment(sampleData, "list", "sample comment"));
System.out.println(toXmlWithComment(sampleData, "notExistingTag", "sample comment"));
}
/**
* inner class used to convert to xml
*/
@XmlType(name = "sampleData")
@XmlRootElement
public static class SampleData {
// first elements contains escape chars, so this should be added as CDATA not TEXT
// second element is empty, don't display any content
// third element contains no escape chars, display as TEXT
public String[] list = new String[]{"add as CDATA, <&'\">", "", "noCdata here"};
}
}