Skip to content

Instantly share code, notes, and snippets.

@bmchild
Last active March 10, 2020 15:27
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save bmchild/6285705 to your computer and use it in GitHub Desktop.
Save bmchild/6285705 to your computer and use it in GitHub Desktop.
Dom4J example of creating an XML document from xpath and value information. Dependencies: Dom4J, Jaxen, Apache Commons, Log4J
<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<data>
<discharge_status>ACTIVE</discharge_status>
<suspension_rec>
<start>0815</start>
<end>0115</end>
</suspension_rec>
<suspension_rec>
<start>0915</start>
</suspension_rec>
<suspension_rec>
<start>0115</start>
</suspension_rec>
</data>
</root>
/**
*
*/
package com.bmchild.service.driverapp.xml;
/**
* @author bchild
*
*/
public interface DriverAppXMLService {
void buildXml();
}
/**
*
*/
package com.bmchild.service.driverapp.xml;
import java.io.IOException;
import java.io.StringWriter;
import java.util.List;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.stereotype.Service;
/**
* @author bchild
*
*/
@Service
public class DriverAppXmlServiceImpl implements DriverAppXMLService {
private static final Logger LOGGER = Logger.getLogger(DriverAppXmlServiceImpl.class.getName());
private static final String ROOT = "root";
private static final String BASE_XPATH = "/" + ROOT + "/data";
/* (non-Javadoc)
* @see com.bmchild.service.driverapp.xml.DriverAppXMLService#buildXml()
*/
@Override
public void buildXml() {
Document document = DocumentHelper.createDocument(DocumentHelper.createElement(ROOT));
addElementToParent(document, prependBase("/discharge_status"), "ACTIVE");
addElementToParent(document, prependBase("/suspension_rec[2]/start"), "0915");
addElementToParent(document, prependBase("/suspension_rec[1]/start"), "0815");
addElementToParent(document, prependBase("/suspension_rec[3]/start"), "0115");
addElementToParent(document, prependBase("/suspension_rec[1]/end"), "0115");
printDoc(document);
}
/**
* @param string
* @return
*/
private String prependBase(String string) {
return BASE_XPATH + string;
}
private void printDoc(Document document) {
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("ISO-8859-1");
StringWriter writer = new StringWriter();
XMLWriter xmlwriter = new XMLWriter(writer, format);
try {
xmlwriter.write( document );
LOGGER.debug(writer.getBuffer().toString());
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
}
}
/**
* Recursive method to create an element and, if necessary, its parents and siblings
* @param document
* @param xpath to single element
* @param value if null an empty element will be created
* @return the created Node
*/
private Node addElementToParent(Document document, String xpath, String value) {
if(LOGGER.isDebugEnabled()) {
LOGGER.debug("adding Element: " + xpath + " -> " + value);
}
String elementName = XPathUtils.getChildElementName(xpath);
String parentXPath = XPathUtils.getParentXPath(xpath);
Node parentNode = document.selectSingleNode(parentXPath);
if(parentNode == null) {
parentNode = addElementToParent(document, parentXPath, null);
}
// create younger siblings if needed
Integer childIndex = XPathUtils.getChildElementIndex(xpath);
if(childIndex > 1) {
List<?> nodelist = document.selectNodes(XPathUtils.createPositionXpath(xpath, childIndex));
// how many to create = (index wanted - existing - 1 to account for the new element we will create)
int nodesToCreate = childIndex - nodelist.size() - 1;
for(int i = 0; i < nodesToCreate; i++) {
((Element)parentNode).addElement(elementName);
}
}
// create requested element
Element created = ((Element)parentNode).addElement(elementName);
if(null != value) {
created.addText(value);
}
return created;
}
}
/**
*
*/
package com.bmchild.service.driverapp.xml;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.runners.MockitoJUnitRunner;
/**
* @author bchild
*
*/
@RunWith(MockitoJUnitRunner.class)
public class DriverAppXmlServiceImplTest {
@InjectMocks
private DriverAppXMLService driverAppXMLService = new DriverAppXmlServiceImpl();
@Test
public void testBuildXml() throws Exception {
driverAppXMLService.buildXml();
}
}
/**
*
*/
package com.bmchild.service.driverapp.xml;
import org.apache.commons.lang3.StringUtils;
/**
* @author bchild
*
*/
public class XPathUtils {
private static final String SLASH = "/";
private static final String R_BRACKET = "]";
private static final String L_BRACKET = "[";
/**
* Looks for the last '/' and returns the name of the last element
* @param xpath
* @return the child element name or null
*/
public static final String getChildElementName(String xpath) {
if(StringUtils.isEmpty(xpath)) {
return null;
}
String childName = xpath.substring(xpath.lastIndexOf(SLASH) + 1);
return stripIndex(childName);
}
/**
* returns the xpath if traversing up the tree one node
* i.e. /root/suspension_rec returns /root
* @param xpath
* @return
*/
public static final String getParentXPath(String xpath) {
if(StringUtils.isEmpty(xpath) || xpath.lastIndexOf(SLASH) <= 0) {
return null;
}
return xpath.substring(0, xpath.lastIndexOf(SLASH));
}
/**
* returns the index of the child element xpath
* i.e. /suspension_rec[3] returns 3. /suspension_rec defaults to 1
* @param xpath
* @return 1, the index, or null if the provided xpath is empty
*/
public static Integer getChildElementIndex(String xpath) {
if(StringUtils.isEmpty(xpath)) {
return null;
}
if(xpath.endsWith(R_BRACKET)) {
String value = xpath.substring(xpath.lastIndexOf(L_BRACKET) + 1, xpath.lastIndexOf(R_BRACKET));
if(StringUtils.isNumeric(value)) {
return Integer.valueOf(value);
}
}
return 1;
}
/**
* @param xpath
* @param childIndex
* @return
*/
public static String createPositionXpath(String xpath, Integer childIndex) {
if(StringUtils.isEmpty(xpath)) {
return null;
}
return stripIndex(xpath) + "[position()<" + childIndex + "]";
}
/**
* @param childName
* @return
*/
private static String stripIndex(String childName) {
if(childName.endsWith(R_BRACKET)) {
return childName.substring(0, childName.lastIndexOf(L_BRACKET));
} else {
return childName;
}
}
}
/**
*
*/
package com.bmchild.service.driverapp.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Test;
/**
* @author bchild
*
*/
public class XPathUtilsTest {
@Test
public void testGetChildElementName() throws Exception {
String childElement = XPathUtils.getChildElementName("/root/child[3]");
assertEquals("child", childElement);
assertNull(XPathUtils.getChildElementName(""));
assertNull(XPathUtils.getChildElementName(null));
}
@Test
public void testGetParentXPath() throws Exception {
String parentPath = XPathUtils.getParentXPath("/root[1]/child");
assertEquals("/root[1]", parentPath);
assertNull(XPathUtils.getParentXPath("/root"));
assertNull(XPathUtils.getParentXPath(""));
assertNull(XPathUtils.getParentXPath(null));
}
@Test
public void testGetChildElementIndex() throws Exception {
Integer childIndex = XPathUtils.getChildElementIndex("/root/child[3]");
assertEquals(new Integer(3), childIndex);
childIndex = XPathUtils.getChildElementIndex("/root/child");
assertEquals(new Integer(1), childIndex);
assertNull(XPathUtils.getChildElementIndex(""));
assertNull(XPathUtils.getChildElementIndex(null));
}
@Test
public void testCreatePositionXpath() throws Exception {
String positionXPath = XPathUtils.createPositionXpath("/root/child", 5);
assertEquals("/root/child[position()<5]", positionXPath);
positionXPath = XPathUtils.createPositionXpath("/root/anotherChild[6]", 6);
assertEquals("/root/anotherChild[position()<6]", positionXPath);
}
}
@alphafeng
Copy link

excellent work! really need it !

@manojjenago
Copy link

Very nice work , do you have code to address the attributes as well ?? Or can you share some highlights on this ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment