4

I'm writing a program in Java where the ultimate goal is to create an XML file from user inputs. I'm able to predict where almost all of the elements need to be, with one exception.

Any one sentence the user inputs is put into its own Callout tag:

<Callout>The user entered some text.</Callout>

If the sentence contains the phrase "User Guide", the program needs to automatically surround those two words with this XML tag:

<BookTitle></BookTitle>

For example, the initial tag looks like this:

<Callout>Save the User Guide</Callout>

The end result should be:

<Callout>Save the <BookTitle>User Guide</BookTitle>.</Callout>

Note that the term "User Guide" could appear in any location within the "Callout" tag. I'm not sure how to dynamically add a tag into the middle of a text node. Is that even possible? I tried the solution found here (Convert String XML fragment to Document Node in Java) but to no avail. I'm using org.w3c.dom to create the elements, nodes, etc.

Community
  • 1
  • 1
kregnax
  • 43
  • 2
  • 6

4 Answers4

1

Don't know exactly how you are constructing your XML document, I assumed the use of some kind of DOM.

This is a VERY simple example of a proof-of-concept. It borrows very heavily from library code, so it may be a little bloated in places, but the basic idea should be reasonable...

Basically, it searches a String for a given token (User Guide) in this case and splits the original text around it, appending #text nodes and <BookTitle> nodes appropriately...

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

public class Main {

    public static final String BOOK_TITLE = "User Guide";

    public static void main(String[] args) {

        Document doc = null;
        try {
            doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
            Element root = doc.createElement("root");
            doc.appendChild(root);

            // Create callout node...
            Element callOut = doc.createElement("CallOut");

            // Get the user input...
            String text = "This is an example of a User Guide for you to read";
            // Does it contain our marker...?
            if (text.contains(BOOK_TITLE)) {
                // While the text contains the mark, continue looping...
                while (text.contains(BOOK_TITLE)) {
                    // Get the text before the marker...
                    String prefix = text.substring(0, text.indexOf(BOOK_TITLE));
                    // Get the text after the marker...
                    text = text.substring(text.indexOf(BOOK_TITLE) + BOOK_TITLE.length());
                    // If there is text before the marker, append it to the call out node
                    if (prefix.length() > 0) {
                        Text textNode = doc.createTextNode(prefix);
                        callOut.appendChild(textNode);
                    }
                    // Append the book title node...
                    Element bookTitle = doc.createElement("BookTitle");
                    bookTitle.setTextContent(BOOK_TITLE);
                    callOut.appendChild(bookTitle);
                }
                // If there is any text remaining, append it to the call out node...
                if (text.length() > 0) {
                    Text textNode = doc.createTextNode(text);
                    callOut.appendChild(textNode);
                }
            } else {
                // No marker, append the text to the call out node..
                Text textNode = doc.createTextNode(text);
                callOut.appendChild(textNode);
            }

            // This will dump the result for you to test....
            root.appendChild(callOut);
            ByteArrayOutputStream baos = null;
            OutputStreamWriter osw = null;

            try {

                baos = new ByteArrayOutputStream();
                osw = new OutputStreamWriter(baos);

                Transformer tf = TransformerFactory.newInstance().newTransformer();
                tf.setOutputProperty(OutputKeys.INDENT, "yes");
                tf.setOutputProperty(OutputKeys.METHOD, "xml");
                tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

                DOMSource domSource = new DOMSource(doc);
                StreamResult sr = new StreamResult(osw);
                tf.transform(domSource, sr);

                osw.flush();
                baos.flush();
                System.out.println(new String(baos.toByteArray()));

            } finally {

                try {
                    baos.close();
                } catch (Exception exp) {
                }

            }

        } catch (IOException | TransformerException | ParserConfigurationException ex) {
            ex.printStackTrace();
        }
    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
1

People will have different approaches to dealing with XML manipulations. The variety of answers to this question is proof enough of this. While using regex and raw text manipulation might be good for one off fixes and hacks if you want a good and maintainable solution you should use an XML API.

My example below does what is asked, however it should be noted that I didn't check for any pathological inputs like ("" as the search string) or deal with XML namespaces. Those things can be added easily enough.

See comments in the code for description of how it works.

Input (test.xml):

<Callouts>
  <Callout>Save the User Guide.</Callout>
</Callouts>

Program

package com.stackoverflow._18774666;

import java.net.URL;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

public class InsertElementInTextNode {

    /**
 * Replace text content of child text nodes of a parent element that
 * matches a search string. The text is replace by an element named with the
 * given name and has it's text content set equal to the search string.
 * 
 * @param parent
 *            The element to search child text nodes of.
 * @param elementName
 *            The name of the element to insert.
 * @param text
 *            The text to replace with an element with same text content.
 */
    public static void replaceTextWithElement(Element parent, String elementName, String text){

        NodeList children = parent.getChildNodes();
        Text cursor;
        Element insertedElement;
        int index;

        /* Iterate children of the given element. */
        for(int i = 0; i < children.getLength(); i++ ){

            /* Check if this child is a text node. Ignore otherwise. */
            if(children.item(i) instanceof Text){
                cursor = (Text) children.item(i);

                /* If the entire text node is equal to the search string,
                 * then we can replace it directly. Else we have split it.*/
                if(text.equals(cursor.getData())){
                    /* Replace the text node with an element */
                    insertedElement = parent.getOwnerDocument().createElement(elementName);
                    insertedElement.setTextContent(text);
                    parent.replaceChild(insertedElement, cursor);
                } else {
                    /* Check to see if the search string exists in this text node. Ignore otherwise.*/
                    index = cursor.getData().indexOf(text);
                    if(index != -1){

                        /* Replace the matched substring with an empty string.*/
                        cursor.replaceData(index, text.length(), "");

                        /* Create element to be inserted, and set the text content. */
                        insertedElement = parent.getOwnerDocument().createElement(elementName);
                        insertedElement.setTextContent(text);

                        /* Split the text node and insert the element in the middle. */
                        parent.insertBefore(insertedElement, cursor.splitText(index));
                    }
                }
            }
        }
    }

    public static void main(String[] args) throws Exception {

        /* Location of our XML document. */
        URL xmlSource = InsertElementInTextNode.class.getResource("test.xml");

        /* Parse with DOM in to a Document */
        Document xmlDoc = DocumentBuilderFactory.newInstance()
                .newDocumentBuilder().parse(xmlSource.openStream());

        /* Find our interesting elements. */
        NodeList nodes = xmlDoc.getElementsByTagName("Callout");


        /* Iterate through our interesting elements and check their content.*/
        Element cursor;
        for(int i = 0; i < nodes.getLength(); i++ ){
            if(nodes.item(i) instanceof Element){
                cursor = (Element) nodes.item(i);
                replaceTextWithElement(cursor, "BookTitle", "User Guide");
            }
        }


        /* Setup to output result. */
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

        /* Printing result to stdout. */
        transformer.transform(new DOMSource(xmlDoc), 
             new StreamResult(System.out));
    }

}

Output (stdout):

<Callouts>
  <Callout>Save the <BookTitle>User Guide</BookTitle>.</Callout>
</Callouts>
Dev
  • 11,919
  • 3
  • 40
  • 53
0

first get the user input and compare it to user guide, if it's true then surround it with title.

String UserInput = null;

UserInput = #getUserType.toString();
if(UserInput.equals("User Guide")){
UserInput = "<BookTitle>"+UserInput+"<BookTitle>";
}else{
//do things if it's false not User Guide
}
Ahmed Ekri
  • 4,601
  • 3
  • 23
  • 42
  • @MadProgrammer - Exactly my problem. If "User Guide" was its own string that wouldn't be a problem, but I can guarantee that it never will be; it's always part of a larger string, such as the one that I used as an example in my question. – kregnax Sep 12 '13 at 22:46
0
public static void main(String[] args) {
    String str = "This is my User Guide dude";
    boolean bTest = str.contains("User Guide");
    if (bTest) {
        int index1 = str.indexOf("User Guide");
        String sub = str.substring(index1, index1 + 10);
        sub = "<BookTitle>" + sub + "</BookTitle>";
        String result = str.replace("User Guide", sub);
        System.out.println(result);
    }
}

OUTPUT:

This is my <BookTitle>User Guide</BookTitle> dude

I think this will point you in the right direction at least.

Mike
  • 13
  • 2