Last active
March 10, 2020 15:27
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
*/ | |
package com.bmchild.service.driverapp.xml; | |
/** | |
* @author bchild | |
* | |
*/ | |
public interface DriverAppXMLService { | |
void buildXml(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
*/ | |
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
*/ | |
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(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
*/ | |
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
*/ | |
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); | |
} | |
} |
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
excellent work! really need it !