2

I am trying to add a comment to a JAXB generated XML @GET result, which doesn't seem to be a straight forward task. I'm using Spring, and I don't have direct access to the marshalling and DOM.

By adding public void beforeMarshal(Marshaller m) to my @XmlElement, I should be able to access the DOM with Marshaller#getNode(Object), and thus be able to add a comment.

The problem is AbstractMarshallerImpl#getNode(Object):

By default, the getNode method is unsupported and throw an java.lang.UnsupportedOperationException. Implementations that choose to support this method must override this method.

Is getNode(Object) implemented by any JAXB implementations out there?

Community
  • 1
  • 1
neu242
  • 15,796
  • 20
  • 79
  • 114

1 Answers1

2

Note: I lead EclipseLink JAXB (MOXy) and am a member of the JAXB 2 (JSR-222) expert group.


MOXy does not currently support the optional getNode method see (enhancement request https://bugs.eclipse.org/332762). However, JAXB's Binder may be helpful with this use case:

Demo

import javax.xml.bind.Binder;
import javax.xml.bind.JAXBContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
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.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

public class Demo {

    public static void main(String[] args) throws Exception {
        Customer customer = new Customer();
        customer.setName("Jane Doe");

        PhoneNumber homePhoneNumber = new PhoneNumber();
        customer.getPhoneNumbers().add(homePhoneNumber);

        PhoneNumber workPhoneNumber = new PhoneNumber();
        customer.getPhoneNumbers().add(workPhoneNumber);

        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        Binder<Node> binder = jc.createBinder();
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.newDocument();

        binder.marshal(customer, document);
        Node homePhoneNumberElement = binder.getXMLNode(homePhoneNumber);
        Comment comment = document.createComment("My Comment");
        homePhoneNumberElement.appendChild(comment);

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer t = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        StreamResult result = new StreamResult(System.out);
        t.transform(source, result);
    }

}

Customer

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {

    private String name;

    private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @XmlElement(name="phone-number")
    public List<PhoneNumber> getPhoneNumbers() {
        return phoneNumbers;
    }

    public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
        this.phoneNumbers = phoneNumbers;
    }

}

PhoneNumber

public class PhoneNumber {

}
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • Thanks, Blaise! The problem is, I don't use the JAXBContext directly. I just annotate my method with @GET and return a Response.ok(entity).build(). – neu242 Jun 07 '11 at 06:10
  • @neu242 - You could leverage the JAX-RS concept of MessageBodyReader/MessageBodyWriter for this use case. For an example of how it can be used with JAXB see: http://stackoverflow.com/questions/3428273/validate-jaxbelement-in-jpa-jax-rs-web-service/3440388#3440388 – bdoughan Jun 07 '11 at 14:42
  • OK, I have a @Path("/myApi") @Component @Scope("request") @Produces({"application/json", "application/xml"}) public class MyResource, with a method @GET public Response myMethod() { MyEntity entity = getEntity(); return Response.ok(entity).build();}. Now, I created an additional @Provider @Consumes({"application/xml", "application/json"}) public class ValidatingReader implements MessageBodyReader { ... } like in your example. The MessageBodyReader doesn't seem to be triggered, what am I missing? – neu242 Jun 08 '11 at 06:52