JAXB: how to add a subtree to an object and create a full tree

I have one subtree that I would like to add to the object and make JAXB marshall the whole thing as a single tree (and with the corresponding tags). But at present, the root tag of the subtree is being replaced by the tag of another object

Unfortunately, I am not allowed to publish the source code here, so I reproduced my problem in the test code (so bear me if you find this dumb).

The idea is that I would like to derive the following structure:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ns2:Root xmlns:ns2="urn:my:foo:bar:1.0" xmlns:ns3="urn:other:foo:bar:1.1"> <Content> <Header> <ns3:Leaf/> </Header> </Content> </ns2:Root> 

but for now, all I get is:

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ns2:Root xmlns:ns2="urn:my:foo:bar:1.0" xmlns:ns3="urn:other:foo:bar:1.1"> <Content> <Header/> </Content> </ns2:Root> 

I have two XSDs to create all the necessary classes, so I am fine on this side (but since these classes are generated, I cannot change them).

Here is an example of code that creates the second XML (incorrect):

 package foo.bar; import java.io.OutputStream; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Node; public class Test { private JAXBContext context; public Test() throws JAXBException { context = JAXBContext.newInstance(RootElement.class, LeafElement.class); } @XmlRootElement(name = "Root", namespace = "urn:my:foo:bar:1.0") @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "Root", propOrder = { "content" }) public static class RootElement { @XmlElement(name = "Content") protected ContentElement content; public ContentElement getContent() { return content; } public void setContent(ContentElement content) { this.content = content; } } @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "Content", propOrder = { "dummy" }) public static class ContentElement { @XmlElement(name = "Header") protected Object dummy; public Object getDummy() { return dummy; } public void setDummy(Object dummy) { this.dummy = dummy; } } @XmlRootElement(name = "Leaf", namespace = "urn:other:foo:bar:1.1") @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "Leaf") public static class LeafElement { } public Node marshal(Object obj) throws JAXBException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); Document doc = null; try { DocumentBuilder db = dbf.newDocumentBuilder(); doc = db.newDocument(); } catch (ParserConfigurationException ex) { throw new JAXBException(ex); } Marshaller m = context.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); m.marshal(obj, doc); return doc.getDocumentElement(); } public void marshal(Object obj, OutputStream stream) throws JAXBException { Marshaller m = context.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); m.marshal(obj, stream); } public void test() throws JAXBException { RootElement root = new RootElement(); ContentElement content = new ContentElement(); root.setContent(content); LeafElement leaf = new LeafElement(); content.setDummy(marshal(leaf)); marshal(root, System.out); } public static void main(String[] args) throws JAXBException { new Test().test(); } } 

In this code you will find 3 โ€œsortableโ€ classes:

  • RootElement
  • ContentElement and
  • LeafElement .

The first two classes belong to one XSD (with a given namespace), and the last from another XSD (with a different namespace), as shown in the code example.

So far, all I have found to fix this has been to create an additional class that will be set as dummy in the ContentElement and itself will hold the LeafElement so that JAXB creates the appropriate intermediate Node. But I find this solution pretty ugly, not really supported, and was hoping JAXB had a way to handle such cases.

If you need more information, or you need me to re-formulate my question, do not be shy. Itโ€™s hard for me to explain my problem in simple words.

The limitations are as follows:

  • I cannot change RootElement, ContentElement and LeafElement
  • I can not use something other than jaxb
+4
source share
1 answer

If you cannot change the element class, then you must create a holder object for the sheet.

 @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public static class LeafElementHolder { @XmlAnyElement private Object leaf; public Object getLeaf() { return leaf; } public void setLeaf(Object leaf) { this.leaf = leaf; } } 

Add this class to context

 public Test() throws JAXBException { context = JAXBContext.newInstance(RootElement.class, LeafElement.class, LeafElementHolder.class); } 

and use this in test () method

  LeafElement leaf = new LeafElement(); LeafElementHolder holder = new LeafElementHolder(); holder.setLeaf(leaf); content.setDummy(marshal(holder)); 

You have 4 elements in XML, so Java should have 4 classes.

 ns2:Root ns2:Content ns2:Header ns3:Leaf 
+1
source

Source: https://habr.com/ru/post/1434264/


All Articles