JAXB: ¿Cómo ignorar el espacio de nombres durante la eliminación del mapeo de documentos XML?

Mi esquema especifica un espacio de nombres, pero los documentos no. ¿Cuál es la forma más sencilla de ignorar el espacio de nombres durante el desasignamiento de JAXB (XML -> objeto)?

En otras palabras, tengo

 

en lugar de,

  

Creo que debe agregar el espacio de nombres a su documento xml, con, por ejemplo, el uso de un filtro SAX .

Eso significa:

  • Defina una interfaz ContentHandler con una nueva clase que interceptará eventos SAX antes de que JAXB pueda obtenerlos.
  • Definir un XMLReader que establecerá el controlador de contenido

luego une los dos:

 public static Object unmarshallWithFilter(Unmarshaller unmarshaller, java.io.File source) throws FileNotFoundException, JAXBException { FileReader fr = null; try { fr = new FileReader(source); XMLReader reader = new NamespaceFilterXMLReader(); InputSource is = new InputSource(fr); SAXSource ss = new SAXSource(reader, is); return unmarshaller.unmarshal(ss); } catch (SAXException e) { //not technically a jaxb exception, but close enough throw new JAXBException(e); } catch (ParserConfigurationException e) { //not technically a jaxb exception, but close enough throw new JAXBException(e); } finally { FileUtil.close(fr); //replace with this some safe close method you have } } 

Aquí hay una extensión / edición de la solución VonCs por si alguien no quiere pasar por la molestia de implementar su propio filtro para hacer esto. También muestra cómo generar un elemento JAXB sin el espacio de nombres presente. Todo esto se logra usando un filtro SAX.

Implementación del filtro:

 import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.XMLFilterImpl; public class NamespaceFilter extends XMLFilterImpl { private String usedNamespaceUri; private boolean addNamespace; //State variable private boolean addedNamespace = false; public NamespaceFilter(String namespaceUri, boolean addNamespace) { super(); if (addNamespace) this.usedNamespaceUri = namespaceUri; else this.usedNamespaceUri = ""; this.addNamespace = addNamespace; } @Override public void startDocument() throws SAXException { super.startDocument(); if (addNamespace) { startControlledPrefixMapping(); } } @Override public void startElement(String arg0, String arg1, String arg2, Attributes arg3) throws SAXException { super.startElement(this.usedNamespaceUri, arg1, arg2, arg3); } @Override public void endElement(String arg0, String arg1, String arg2) throws SAXException { super.endElement(this.usedNamespaceUri, arg1, arg2); } @Override public void startPrefixMapping(String prefix, String url) throws SAXException { if (addNamespace) { this.startControlledPrefixMapping(); } else { //Remove the namespace, ie don´t call startPrefixMapping for parent! } } private void startControlledPrefixMapping() throws SAXException { if (this.addNamespace && !this.addedNamespace) { //We should add namespace since it is set and has not yet been done. super.startPrefixMapping("", this.usedNamespaceUri); //Make sure we dont do it twice this.addedNamespace = true; } } } 

Este filtro está diseñado para que ambos puedan agregar el espacio de nombres si no está presente:

 new NamespaceFilter("http://www.example.com/namespaceurl", true); 

y para eliminar cualquier espacio de nombre actual:

 new NamespaceFilter(null, false); 

El filtro se puede usar durante el análisis de la siguiente manera:

 //Prepare JAXB objects JAXBContext jc = JAXBContext.newInstance("jaxb.package"); Unmarshaller u = jc.createUnmarshaller(); //Create an XMLReader to use with our filter XMLReader reader = XMLReaderFactory.createXMLReader(); //Create the filter (to add namespace) and set the xmlReader as its parent. NamespaceFilter inFilter = new NamespaceFilter("http://www.example.com/namespaceurl", true); inFilter.setParent(reader); //Prepare the input, in this case a java.io.File (output) InputSource is = new InputSource(new FileInputStream(output)); //Create a SAXSource specifying the filter SAXSource source = new SAXSource(inFilter, is); //Do unmarshalling Object myJaxbObject = u.unmarshal(source); 

Para usar este filtro para generar XML desde un objeto JAXB, eche un vistazo al código a continuación.

 //Prepare JAXB objects JAXBContext jc = JAXBContext.newInstance("jaxb.package"); Marshaller m = jc.createMarshaller(); //Define an output file File output = new File("test.xml"); //Create a filter that will remove the xmlns attribute NamespaceFilter outFilter = new NamespaceFilter(null, false); //Do some formatting, this is obviously optional and may effect performance OutputFormat format = new OutputFormat(); format.setIndent(true); format.setNewlines(true); //Create a new org.dom4j.io.XMLWriter that will serve as the //ContentHandler for our filter. XMLWriter writer = new XMLWriter(new FileOutputStream(output), format); //Attach the writer to the filter outFilter.setContentHandler(writer); //Tell JAXB to marshall to the filter which in turn will call the writer m.marshal(myJaxbObject, outFilter); 

Espero que esto ayude a alguien, ya que pasé un día haciendo esto y casi me rindo dos veces;)

Tengo problemas de encoding con la solución XMLFilter, así que hice XMLStreamReader para ignorar los espacios de nombres:

 class XMLReaderWithoutNamespace extends StreamReaderDelegate { public XMLReaderWithoutNamespace(XMLStreamReader reader) { super(reader); } @Override public String getAttributeNamespace(int arg0) { return ""; } @Override public String getNamespaceURI() { return ""; } } InputStream is = new FileInputStream(name); XMLStreamReader xsr = XMLInputFactory.newFactory().createXMLStreamReader(is); XMLReaderWithoutNamespace xr = new XMLReaderWithoutNamespace(xsr); Unmarshaller um = jc.createUnmarshaller(); Object res = um.unmarshal(xr); 

En mi situación, tengo muchos espacios de nombres y después de algunas depuraciones encuentro otra solución simplemente cambiando la clase NamespaceFitler. Para mi situación (simplemente unmarshall) esto funciona bien.

  import javax.xml.namespace.QName; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.XMLFilterImpl; import com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector; public class NamespaceFilter extends XMLFilterImpl { private SAXConnector saxConnector; @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { if(saxConnector != null) { Collection expected = saxConnector.getContext().getCurrentExpectedElements(); for(QName expectedQname : expected) { if(localName.equals(expectedQname.getLocalPart())) { super.startElement(expectedQname.getNamespaceURI(), localName, qName, atts); return; } } } super.startElement(uri, localName, qName, atts); } @Override public void setContentHandler(ContentHandler handler) { super.setContentHandler(handler); if(handler instanceof SAXConnector) { saxConnector = (SAXConnector) handler; } } } 

Otra forma de agregar un espacio de nombre predeterminado a un Documento XML antes de enviarlo a JAXB es usar JDom :

  1. Parse XML a un documento
  2. Iteramente y establece el espacio de nombres en todos los elementos
  3. Unmarshall utilizando un JDOMSource

Me gusta esto:

 public class XMLObjectFactory { private static Namespace DEFAULT_NS = Namespace.getNamespace("http://tempuri.org/"); public static Object createObject(InputStream in) { try { SAXBuilder sb = new SAXBuilder(false); Document doc = sb.build(in); setNamespace(doc.getRootElement(), DEFAULT_NS, true); Source src = new JDOMSource(doc); JAXBContext context = JAXBContext.newInstance("org.tempuri"); Unmarshaller unmarshaller = context.createUnmarshaller(); JAXBElement root = unmarshaller.unmarshal(src); return root.getValue(); } catch (Exception e) { throw new RuntimeException("Failed to create Object", e); } } private static void setNamespace(Element elem, Namespace ns, boolean recurse) { elem.setNamespace(ns); if (recurse) { for (Object o : elem.getChildren()) { setNamespace((Element) o, ns, recurse); } } }