How to add XML processing instructions during JAXB Marshal

I would like to add a processing instruction whenever the collection / array property is serialized to get something like

<alice> <? array bob ?> <bob>edgar</bob> <bob>david</bob> </alice> 

Is this possible with JAXB? Or at least with some specific JAXB implementation?

+6
source share
1 answer

You can use XMLStreamWriter and XmlAdapter for this:

Bobadapter

Notes on the XmlAdapter :

  • It has state and refers to XMLStreamWriter . Later we will use the JAXB feature to set the state of the XmlAdapter to Marshaller .
  • It converts from List<String> to List<String> , we just use the XmlAdapter here to get the event.
  • The marshal method is that we call writeProcessingInstruction on the XMLStreamWriter :

 package forum6931520; import java.util.List; import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.stream.XMLStreamWriter; public class BobAdapter extends XmlAdapter<List<String>, List<String>> { private boolean first = true; private XMLStreamWriter xmlStreamWriter; public BobAdapter() { } public BobAdapter(XMLStreamWriter xmlStreamWriter) { this(); this.xmlStreamWriter = xmlStreamWriter; } @Override public List<String> marshal(List<String> stringList) throws Exception { if(first) { xmlStreamWriter.writeProcessingInstruction("array", "bob"); first = false; } return stringList; } @Override public List<String> unmarshal(List<String> stringList) throws Exception { return stringList; } } 

Alice

The @XmlJavaTypeAdapter annotation @XmlJavaTypeAdapter used to associate an XmlAdapter with a field / property:

 package forum6931520; import java.util.List; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @XmlRootElement public class Alice { private List<String> bob; @XmlJavaTypeAdapter(BobAdapter.class) public List<String> getBob() { return bob; } public void setBob(List<String> bob) { this.bob = bob; } } 

Demo

 package forum6931520; import java.io.File; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamWriter; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(Alice.class); File xml = new File("src/forum6931520/input.xml"); Unmarshaller unmarshaller = jc.createUnmarshaller(); Alice alice = (Alice) unmarshaller.unmarshal(xml); Marshaller marshaller = jc.createMarshaller(); XMLOutputFactory xof = XMLOutputFactory.newFactory(); XMLStreamWriter xmlStreamWriter = xof.createXMLStreamWriter(System.out); marshaller.setAdapter(new BobAdapter(xmlStreamWriter)); marshaller.marshal(alice, xmlStreamWriter); } } 

Input.xml

 <?xml version="1.0" encoding="UTF-8"?> <alice> <?array bob?> <bob>edgar</bob> <bob>david</bob> </alice> 

Exit

 <?xml version='1.0' encoding='UTF-8'?><alice><?array bob?><bob>edgar</bob><bob>david</bob></alice> 

Note

This example should be run with EclipseLink JAXB (MOXy) , because JAXB RI will throw the following exception (I am the leader of MOXy):

 Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions java.util.List is an interface, and JAXB can't handle interfaces. this problem is related to the following location: at java.util.List at public java.util.List forum6931520.Alice.getBob() at forum6931520.Alice java.util.List does not have a no-arg default constructor. this problem is related to the following location: at java.util.List at public java.util.List forum6931520.Alice.getBob() at forum6931520.Alice 

Additional Information


UPDATE

I added an extension request ( https://bugs.eclipse.org/354286 ) to add my own support for processing instructions. This ultimately allows you to specify the following in your property:

 @XmlProcessingInstruction(target="array", value="bob") public List<String> getBob() { return bob; } 
+6
source

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


All Articles