7

I use REST and i was wondering if i can tell jaxb to insert a string field "as-it-is" into the outgoing xml. Certainly i count unpack it before returning, but i would like to save this step.

@XmlRootElement(name="unnestedResponse")
public class Response{
 @Insert annotation here ;-)
 private String alreadyXml;
 private int otherDate; ...
}

Is there a possability to tell JAXB to just use the String as it is without escapting? I want that the client does not have to parse my response and then parse this field.

greetings, m

mkuff
  • 1,620
  • 6
  • 27
  • 39
  • possible duplicate of [Using JAXB to extract inner text of XML element](http://stackoverflow.com/questions/5537416/using-jaxb-to-extract-inner-text-of-xml-element) – bdoughan Oct 29 '12 at 14:18

2 Answers2

11

You can use the @XmlAnyElement and specify a DomHandler to keep a portion of the XML document as a String.

Customer

import javax.xml.bind.annotation.*;

@XmlRootElement
public class Customer {

    private String bio;

    @XmlAnyElement(BioHandler.class)
    public String getBio() {
        return bio;
    }

    public void setBio(String bio) {
        this.bio = bio;
    }

}

BioHandler

import java.io.*;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.annotation.DomHandler;
import javax.xml.transform.Source;
import javax.xml.transform.stream.*;

public class BioHandler implements DomHandler<String, StreamResult> {

    private static final String BIO_START_TAG = "<bio>";
    private static final String BIO_END_TAG = "</bio>";

    private StringWriter xmlWriter = new StringWriter();

    public StreamResult createUnmarshaller(ValidationEventHandler errorHandler) {
        return new StreamResult(xmlWriter);
    }

    public String getElement(StreamResult rt) {
        String xml = rt.getWriter().toString();
        int beginIndex = xml.indexOf(BIO_START_TAG) + BIO_START_TAG.length();
        int endIndex = xml.indexOf(BIO_END_TAG);
        return xml.substring(beginIndex, endIndex);
    }

    public Source marshal(String n, ValidationEventHandler errorHandler) {
        try {
            String xml = BIO_START_TAG + n.trim() + BIO_END_TAG;
            StringReader xmlReader = new StringReader(xml);
            return new StreamSource(xmlReader);
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

}

For More Information

bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • One important note if you're using this for marshaling and using standard JAXB: instead of passing in a String as in this example, you HAVE to pass in an implementation of the org.w3c.dom.Element interface. Otherwise, JAXB will not use the DOMHandler for marshaling. The implementation doesn't have to actually do anything useful, so you can store a String in it and then get it out in the marshal method and follow the example above the rest of the way, but it does have to be an instance of an object implementing Element. – Gabriel Jiva Jul 12 '13 at 20:44
  • @Gabriel - This is standard JAXB. Its possible that there is a bug in the JAXB reference implementation. – bdoughan Jul 12 '13 at 20:53
  • @BlaiseDoughan You should use `int endIndex = xml.lastIndexOf(BIO_END_TAG);` to allow for tags inside the xml string. – cschuff Oct 25 '18 at 09:01
2

Following bdoughan's answer did not work for me as I encountered errors during marshalling when the text contained the '& character (e.g. in URLs or when using HTML entities such as e.g. " ").

I was able to resolve this by changing the custom DomHandler's marshal method to

public Source marshal(String et, ValidationEventHandler veh) {
    Node node = new SimpleTextNode(et);
    return new DOMSource(node);
}

where SimpleTextNode implements the Node interface as follows:

class SimpleTextNode implements Node {
    
    String nodeValue = "";
    
    @Override    
    public SimpleTextNode(String nodeValue) {
        this.nodeValue = nodeValue;
    }
    
    @Override
    public short getNodeType() {
        return TEXT_NODE;
    }

    // the remaining methods of the Node interface are not needed during marshalling
    // you can just use the code template of your IDE...

    ...
}

PS: I would have loved to leave this as a comment to bdoughan's answer, but unfortunately I have way too little reputation :-(

Jay
  • 37
  • 8