82 votes

JAXB: Comment ignorer l'espace de noms lors de la résolution d'un document XML?

Mon schéma spécifie un espace de noms, contrairement aux documents. Quelle est la manière la plus simple d’ignorer l’espace de noms lors de la dissémination de JAXB (XML -> objet)?

En d'autres termes, j'ai

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

au lieu de,

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

109voto

Kristofer Points 939

Voici une extension/modifier des VonCs solution juste au cas où quelqu'un ne veut pas passer par les tracas de la mise en œuvre de leur propre filtre pour ce faire. Il montre également comment la sortie d'un JAXB élément sans l'espace de noms présents. Ceci est accompli à l'aide d'un SAX Filtre.

Filtre de mise en œuvre:

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

}

Ce filtre est conçu pour être en mesure d'ajouter de l'espace de noms si elle n'est pas présente:

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

et pour supprimer toute espace de noms:

new NamespaceFilter(null, false);

Le filtre peut être utilisé lors de l'analyse comme suit:

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

Pour utiliser ce filtre de sortie XML à partir d'un JAXB objet, regardez le code ci-dessous.

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

Cela devrait aider quelqu'un car j'ai passé un jour à le faire, et a presque donné deux fois ;)

44voto

lunicon Points 141

J'ai des problèmes d'encodage avec la solution XMLFilter, j'ai donc fait en sorte que XMLReaderStream ignore les espaces de noms:

 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 Points 414372

Je crois que vous devez ajouter l'espace de noms à votre document xml, avec, par exemple, l'utilisation d'un SAX filtre.

Cela signifie que:

  • Définir une interface ContentHandler avec une nouvelle classe qui va intercepter les événements SAX avant de JAXB peut les obtenir.
  • Définir un XMLReader qui servira de gestionnaire de contenu

ensuite relier les deux ensemble:

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

5voto

Henrique Points 11

Dans ma situation, j'ai plusieurs espaces de noms et après un certain débogage, je trouve une autre solution en changeant simplement la classe NamespaceFitler. Pour ma situation (juste unmarshall) cela fonctionne 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

mafro Points 587

Un autre moyen d’ajouter un espace de nom par défaut à un document XML avant de le transmettre à JAXB consiste à utiliser JDom :

  1. Analyser XML dans un document
  2. Itérer et définir un espace de noms sur tous les éléments
  3. Unmarshall utilisant un JDOMSource

Comme ça:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X