67 votos

JAXB: Cómo hacer caso omiso de nombres durante unmarshalling documento XML?

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

En otras palabras, tengo

<foo><bar></bar></foo>

En lugar de

<foo xmlns="http://tempuri.org/"><bar></bar></foo>

102voto

Kristofer Puntos 939

Aquí es una extensión/edición de VonCs solución en caso de que alguien no quiere pasar por la molestia de poner en práctica su propio filtro para hacer esto. También se muestra cómo a la salida de un JAXB elemento sin el espacio de nombres actual. Todo esto se consigue utilizando un SAXO de Filtro.

Filtro de aplicación:

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, i.e. 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 tanto para ser capaz de agregar el espacio de nombres si no está presente:

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

y para eliminar cualquier espacio de nombres:

new NamespaceFilter(null, false);

El filtro puede ser utilizado 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 utilizar este filtro para la salida de XML a partir de una JAXB objeto, eche un vistazo a el código de abajo.

//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);

Esperamos que esto ayude a alguien que desde que me pasé un día haciendo esto y casi dio dos veces ;)

24voto

lunicon Puntos 141

Tengo problemas con XMLFilter solución de codificación así hice XMLReaderStream para omitir 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);

19voto

VonC Puntos 414372

Yo creo que usted debe agregar el espacio de nombres para el documento xml, con, por ejemplo, el uso de un SAXO de filtro.

Lo que significa:

  • Definir un ContentHandler interfaz con una nueva clase que va a interceptar los eventos SAX antes de JAXB puede obtener de ellos.
  • Definir un XMLReader que va a fijar el contenido de controlador

a continuación, el enlace de los dos juntos:

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
    }
}

3voto

Henrique Puntos 11

En mi caso, tengo muchos espacios de nombres y después algunos depuración encontrar otra solución, sólo cambiar la clase NamespaceFitler. Para mi situación (sólo unmarshall) esto funciona muy 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<QName> 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;
    }
}

}

1voto

WaldenL Puntos 1001

Otra manera de agregar un espacio de nombres predeterminado a un documento XML antes de darle de comer a JAXB es utilizar JDom:

  1. Parsear XML a un documento
  2. Iteración y conjunto de nombres de todos los elementos
  3. Unmarshall usando un JDOMSource

Así:

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);
    		}
    	}
    }

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X