2

I have an XML file (client_23.xml) as shown below. And I have a String variable out which I need to insert a particular spot in below XML. This is not my full XML as I have lot of other nested stuff under which function tag will be in and it is not consistent since this XML is getting generated through code so I nee

<?xml version="1.0"?>
<clients>
    <!-- some other code here -->

    <function>
    </function>

    <function>
    </function>

    <function>
        <name>data_values</name>
        <variables>
            <variable>
            <name>temp</name>
            <type>double</type>
            </variable>
        </variables>
        <block>
            <opster>temp = 1</opster>   
        </block>
    </function>
</clients>

I need to parse the above XML and find a function whose name is data_values and then insert out string variable in <block> tag. This is not my full XML as I have lot of other nested stuff under which function tag will be in and it is not consistent since this XML is getting generated through code so I need to parse and iterate and find it and then put it.

So final xml will look like this:

<?xml version="1.0"?>
<clients>
    <!-- some other code here -->

    <function>
    </function>

    <function>
    </function>

    <function>
        <name>data_values</name>
        <variables>
            <variable>
            <name>temp</name>
            <type>double</type>
            </variable>
        </variables>
        <block>
            <!-- new stuff added and old things were gone -->
            <opster>hello = world</opster>
            <opster>abc = def</opster>
        </block>
    </function>
</clients>

Below is the code I got but I am not able to understand how can I put out variable inside data_values function in a block tag.

StringBuilder out = new StringBuilder();
// some data in out variable, properly formatted with new lines.

String location = key.getPathName();
String clientIdPath = location + "/" + "client_23.xml";
File fileName = new File(clientIdPath);

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(fileName);

NodeList dataValueFunction = document.getElementsByTagName("function");
// I have started iterating but I am not able to understand how to insert "out"
// variable inside block
for (int i = 0; i < dataValueFunction.getLength(); i++) {
    Node node = dataValueFunction.item(i);
    System.out.println(node.getNodeName());
    NodeList childList = node.getChildNodes();
    for (int j = 0; j < childList.getLength(); j++) {
        Node node1 = childList.item(j);
        if (node1 != null && node1.getNodeName().equalsIgnoreCase("name")
                        && node1.getTextContent().equalsIgnoreCase("data_values")) {
            // now what should I do here?
        }
    }
}
user1950349
  • 4,738
  • 19
  • 67
  • 119

3 Answers3

2

Check this question. You can use XPath expressions to locate correct function tag and update it using Xpath.

How to update XML using XPath and Java

Here is a quick code.

public static void main(String[] args) {
    try {
        FileInputStream file = new FileInputStream(new File("data.xml"));
        List<String> outs = Arrays.asList(new String[] { "hello = world", "abc = def" });
        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();

        DocumentBuilder builder = builderFactory.newDocumentBuilder();

        Document xmlDocument = builder.parse(file);

        XPath xPath = XPathFactory.newInstance().newXPath();

        System.out.println("*************************");
        String expression = "//clients/function/name[text()='data_values']";
        System.out.println(expression);
        Node nameTag = (Node) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODE);

        for (int i = 0; i < nameTag.getParentNode().getChildNodes().getLength(); i++) {
            if (nameTag.getParentNode().getChildNodes().item(i).getNodeName().equals("block")) {
                System.out.println("GOT BLOCK");
                nameTag.getParentNode().removeChild(nameTag.getParentNode().getChildNodes().item(i));
                Node node = xmlDocument.createElement("block");



                nameTag.getParentNode().appendChild(node);
                for (String out : outs) {
                    Node newNode = xmlDocument.createElement("opster");
                    newNode.setTextContent(out);

                    node.appendChild(newNode);
                }

            }
        }
        TransformerFactory tFactory = TransformerFactory.newInstance();
        Transformer transformer = tFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
        DOMSource source = new DOMSource(xmlDocument);
        StreamResult result = new StreamResult(System.out);
        transformer.transform(source, result);

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (SAXException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ParserConfigurationException e) {
        e.printStackTrace();
    } catch (XPathExpressionException e) {
        e.printStackTrace();
    } catch (TransformerConfigurationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (TransformerException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
Community
  • 1
  • 1
DhruvG
  • 394
  • 1
  • 2
  • 10
  • For Xpath, we need to have proper XML tag names at right place. In my case, XML tags can be up and down. I was thinking to iterate and then find it and then put. – user1950349 Sep 17 '15 at 23:11
  • With Xpath you can find tags with specific text. And I think thats what you want to do. Check this link http://stackoverflow.com/questions/1982624/using-xpath-how-do-i-select-a-node-based-on-its-text-content-and-value-of-an-at – DhruvG Sep 17 '15 at 23:21
  • hmm, I am not much familiar with Xpath. can you provide an example for my use case and then I can try it out to see if it works? – user1950349 Sep 17 '15 at 23:27
  • what is your expected function tag result? – DhruvG Sep 18 '15 at 00:01
  • well that's what I am not aware of since it is generated by code so we can have multiple nested stuff and that's why Xpath may not work. – user1950349 Sep 18 '15 at 00:02
  • wait let me figure this out. – user1950349 Sep 18 '15 at 00:04
  • I guess you are right and that's what my xpath is after I looked at the code. But I want add my `out` variable inside block tag. – user1950349 Sep 18 '15 at 00:09
  • check quick and dirty solution. I only used xpath to quickly find correct function tag. This can be change a little bit if you are expecting multiple function tag with name->data_values – DhruvG Sep 18 '15 at 00:10
  • Do you want to remove existing tags from Block tag or just want to append one more tag. Appending will be more simpler. – DhruvG Sep 18 '15 at 00:11
1

EDITED: adding the value of "out" to the previously existing "block" element

As sugested by @user489732, use XPath to localise the node, then create a new node with your value and then insert it to the function element. I'd use a different approach though:

public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException, XPathExpressionException, TransformerException {

    // Your initial input
    String myXML = "<?xml version=\"1.0\"?>\n" +
            "<clients>\n" +
            "    <!-- some other code here -->\n" +
            "\n" +
            "    <function>\n" +
            "    </function>\n" +
            "\n" +
            "    <function>\n" +
            "    </function>\n" +
            "\n" +
            "    <function>\n" +
            "        <name>data_values</name>\n" +
            "        <variables>\n" +
            "            <variable>\n" +
            "            <name>temp</name>\n" +
            "            <type>double</type>\n" +
            "            </variable>\n" +
            "        </variables>\n" +
            "        <block>\n" +
            "            <!-- new stuff added and old things were gone -->\n" +
            "            <opster>hello = world</opster>\n" +
            "            <opster>abc = def</opster>\n" +
            "        </block>\n" +
            "    </function>\n" +
            "</clients>";

    // you will have to create an input stream from your XML file,
    // in this case I'm using a ByteArrayInputStream, you'll probably
    // need a FileInputStream
    InputStream myXMLStream = new ByteArrayInputStream(myXML.getBytes(Charset.forName("UTF-8")));

    String out = "My value whatever it is";

    // Create xpath expression
    XPathFactory xPathfactory = XPathFactory.newInstance();
    XPath xpath = xPathfactory.newXPath();
    XPathExpression expr = xpath.compile("/clients/function/block[../name/text() = 'data_values']");

    // Load document
    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
    Document document = documentBuilder.parse(myXMLStream);

    // Search for the node
    NodeList blockList = (NodeList) expr.evaluate(document, XPathConstants.NODESET);
    Element blockElement = (Element)blockList.item(0);

    // Insert your value inside element "block"
    // CAVEAT: if the value of "out" contains "tags" (xml elements)
    // then "createTextNode" won't work, you need to create elements
    // with the different methods of "document" (document.createXXXX())
    Node textNode = document.createTextNode(out);
    blockElement.insertBefore(textNode, blockElement.getFirstChild());

    // Write the document to its final destination
    TransformerFactory transformerFactory = TransformerFactory.newInstance();
    Transformer transformer = transformerFactory.newTransformer();
    DOMSource source = new DOMSource(document);

    // instead of System.out, use a more appropriate stream:
     StreamResult result = new StreamResult(System.out);

    transformer.transform(source, result);
}
morgano
  • 17,210
  • 10
  • 45
  • 56
0

If anyone thinks the answers offered so far are too complicated, here is another one, and it is with vtd-xml... here is why vtd-xml is much better than DOM

import com.ximpleware.*;

public class replaceContent {
    public static void main(String[] s) throws VTDException, Exception{
        VTDGen vg = new VTDGen();
        AutoPilot ap = new AutoPilot();
        XMLModifier xm = new XMLModifier();
        if (vg.parseFile("d:\\xml\\input2.xml", true)){
            VTDNav vn = vg.getNav();
            ap.bind(vn);
            xm.bind(vn);
            ap.selectXPath("/clients/function/block");
            int i=-1;
            byte[] s1 = ("\r\n\t\t<opster>hello = world</opster>\r\n\t\t"+
                        "<opster>abc = def</opster>\r\n\t").getBytes();
            while((i=ap.evalXPath())!=-1){
                xm.insertAfterHead(s1);
                long l= vn.getContentFragment(); // add new stuff after the starting tag
                xm.removeContent((int)l, (int)(l>>32)); // remove old stuff
            }
            xm.output("d:\\xml\\new.xml");
        }
        }
}
vtd-xml-author
  • 3,319
  • 4
  • 22
  • 30