public static String substituteXMLContent( String xml, String xpath, Hashtable values, boolean keepCDATA) throws Exception {
        XPathFactory factory = XPathFactory.newInstance();
        XPath xPath = factory.newXPath();
        xPath.setNamespaceContext(CommonNamespaceContext.getInstance());

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder builder = dbf.newDocumentBuilder();
        Document document = builder.parse(new InputSource(new StringReader(xml)));
        NodeList list = document.getDocumentElement().getChildNodes();
        NodeList s =  (NodeList)xPath.evaluate(xpath, list, XPathConstants.NODESET);
        for (int i = 0; i < s.getLength(); i++) {
            Node n = s.item(i);
            Node sibling = n.getNextSibling();
            boolean isCDATANode = false;
            if (n.getNodeType() == Node.CDATA_SECTION_NODE)
                isCDATANode = true;
            else if (sibling != null && sibling.getNodeType() == Node.CDATA_SECTION_NODE) {
                n = sibling;
                isCDATANode = true;
            }
            String key = n.getTextContent();
            String newValue = (String)values.get(key);
            if (newValue != null) {
                if (isCDATANode && !keepCDATA) {
                    Node newNode = document.createTextNode(newValue);
                    n.getParentNode().replaceChild(newNode, n);
                }
                else
                    n.setNodeValue(newValue);
            }
        }

        return getXMLString(document);
    }