web-dev-qa-db-fra.com

JAXB: Comment éviter la définition répétée d'espace de noms pour xmlns: xsi

J'ai une configuration JAXB où j'utilise un @XmlJavaTypeAdapter pour remplacer les objets de type Person par des objets de type PersonRef qui ne contiennent que l'UUID de la personne. Cela fonctionne parfaitement bien. Cependant, le XML généré redéclare le même espace de nom (xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance") chaque fois qu'il est utilisé. Bien que ce soit généralement acceptable, cela ne me semble pas juste.

Comment configurer JAXB pour déclarer xmlns: xsi au tout début du document? Puis-je ajouter manuellement des déclarations d'espace de nom à l'élément racine?

Voici un exemple de ce que je veux réaliser:

Actuel:

<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a">
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
    </relation> 
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
    </relation>
    <!-- SNIP: some more relations -->
</person>

Voulait:

<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0"/> 
    </relation> 
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a"/> 
    </relation>
    <!-- SNIP: some more relations -->
</person>
20
sfussenegger

Vous pouvez le faire avec le code:

marshaller.setProperty("com.Sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapper() {
                @Override
                public String[] getPreDeclaredNamespaceUris() {
                    return new String[] { 
                        XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI
                    };
                }

                @Override
                public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
                    if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI))
                        return "xsi";
                    if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI))
                        return "xs";
                    if (namespaceUri.equals(WellKnownNamespace.XML_MIME_URI))
                        return "xmime";
                    return suggestion;

                }
            });
5
Dany

Pas ça joli, mais vous pouvez ajouter un schemaLocation vide à l’élément racine:

marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "");
17
msparer

Il ressemble à un Problème de mappeur d’espace de noms de personnalisation JAXB

Lorsque vous marshallez un document XML à l'aide de JAXB 1.0, un objet Marshaller, un objet JAXB contrôlant le processus de marshalling, fournit des déclarations d'espace de nom dans le document XML obtenu. Parfois, le Marshaller génère de nombreuses déclarations d’espace de noms qui semblent redondantes, par exemple:

   <?xml version="1.0"?>
   <root>
      <ns1:element xmlns:ns1="urn:foo"> ... </ns1:element>
      <ns2:element xmlns:ns2="urn:foo"> ... </ns2:element>
      <ns3:element xmlns:ns3="urn:foo"> ... </ns3:element>
   </root>

JAXB 2.0 modifie ce comportement. Si vous utilisez JAXB 2.0 (ou une version ultérieure) pour marshaler un document XML, le Marshaller déclare tous les URI (Uniform Resource Identifiers) statiquement connus dans l'espace de noms, c'est-à-dire les URI utilisés comme noms d'éléments ou d'attributs dans les annotations JAXB. 

JAXB peut également déclarer des espaces de nom supplémentaires au milieu d'un document XML, par exemple lorsqu'un nom qualifié (QName) utilisé en tant qu'attribut ou valeur d'élément nécessite un nouvel URI d'espace de nom, ou lorsqu'un nœud DOM (Document Object Model) L'arbre de contenu nécessite un nouvel URI d'espace de nom. Ce comportement peut générer un document XML contenant de nombreuses déclarations d'espace de nom avec des préfixes d'espace de nom générés automatiquement. 

Le problème est que les préfixes d’espace de noms générés automatiquement, tels que ns1, ns2 et ns3, ne sont pas conviviaux - ils n’aident généralement pas les gens à comprendre le code XML organisé. 

Heureusement, JAXB version 2.0 (ou ultérieure) fournit une interface fournisseur (SPI) appelée com.Sun.xml.bind.marshaller.NamespacePrefixMapper que vous pouvez utiliser pour spécifier d'autres préfixes d'espace de nom utiles pour le marshalling.

Lorsque le programme JAXBSample organise le document XML pour la première fois, il le fait sans utiliser de classe NamespacePrefixMapper. En conséquence, le Marshaller génère automatiquement un préfixe d'espace de nom, dans ce cas, ns2.

   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   <ns2:JustAnElement xmlns:ns2="a">
       <foo>true</foo>
   </ns2:JustAnElement>

Exemple de configuration évitant la répétition de l'espace de noms:

Le deuxième tri effectué par le programme JAXBSample utilise une classe NamespacePrefixMapper comme suit:

   NamespacePrefixMapper m = new PreferredMapper();
               marshal(jc, e, m);

   public static class PreferredMapper extends NamespacePrefixMapper {
           @Override
           public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
               return "mappedNamespace" + namespaceUri;
           }
       }

La méthode getPreferredPrefix() de la classe PreferredMapper renvoie le préfixe préféré, dans ce cas, mappedNamespacea à déclarer à l'élément racine du code XML organisé.

   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   <mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a">
       <foo>true</foo>
   </mappedNamespacea:JustAnElement>
9
VonC

si vous utilisez Maven, ajoutez simplement ceci à votre pom:

<dependency>
            <groupId>com.Sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.2</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>

pas besoin de PreferredMapper si vous configurez vos annotations comme défini dans l'exemple ci-dessus. Bien que je possède un fichier package-info.jave se configure comme suit:

@javax.xml.bind.annotation.XmlSchema(
        namespace = "mylovelynamespace1", 
        xmlns = {
                    @javax.xml.bind.annotation.XmlNs(prefix = "myns1", namespaceURI = "mylovelynamespace1"),
                    @javax.xml.bind.annotation.XmlNs(prefix = "myns2", namespaceURI = "mylovelynamespace2")
                }, 
                elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.mylovelycompanyname.package;
3
Miguel de Melo

C’est la meilleure réponse que je trouve sur le Web.

Les déclarations xsi:type sont probablement créées car le type déclaré de JAXBElement ne correspond pas au type de la valeur.

Si le ObjectFactory a une méthode de création pour le JAXBElement correct, vous devriez l'utiliser car il devrait renseigner correctement le QName et le type info; sinon, j'essaierais de définir le type déclaré (second constructeur arg) de JAXBElement sur String.class (en supposant qu'il s'agisse du type de commentTest) au lieu de CommentType.Comment.

Source: http://www.Java.net/forum/topic/glassfish/metro-and-jaxb/how-do-i-remove-namespace-declarations-child-elements

Propriétaire: Cbrettin

2
Aguid

Vous pouvez laisser les espaces de noms être écrits une seule fois. Vous aurez besoin d'une classe proxy de XMLStreamWriter et d'un package-info.Java. Ensuite, vous ferez dans votre code:

StringWriter stringWriter = new StringWriter();
XMLStreamWriter writer = new Wrapper((XMLStreamWriter) XMLOutputFactory
                                                               .newInstance().createXMLStreamWriter(stringWriter));
JAXBContext jaxbContext = JAXBContext.newInstance(Collection.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
jaxbMarshaller.marshal(books, writer);
System.out.println(stringWriter.toString());

Classe de proxy (la méthode importante est "writeNamespace"):

            class WrapperXMLStreamWriter implements XMLStreamWriter {

                   private final XMLStreamWriter writer;

                   public WrapperXMLStreamWriter(XMLStreamWriter writer) {
                       this.writer = writer;
                   }

                     //keeps track of what namespaces were used so that not to 
                     //write them more than once
                   private List<String> namespaces = new ArrayList<String>();

                   public void init(){
                       namespaces.clear();
                   }

                   public void writeStartElement(String localName) throws XMLStreamException {
                       init();
                       writer.writeStartElement(localName);

                   }

                   public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
                       init();
                       writer.writeStartElement(namespaceURI, localName);
                   }

                   public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
                       init();
                       writer.writeStartElement(prefix, localName, namespaceURI);
                   }

                   public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
                       if(namespaces.contains(namespaceURI)){ 
                           return;
                       }
                       namespaces.add(namespaceURI);
                       writer.writeNamespace(prefix, namespaceURI);
                   }

    // .. other delegation method, always the same pattern: writer.method() ...

}

package-info.Java:

@XmlSchema(elementFormDefault=XmlNsForm.QUALIFIED, attributeFormDefault=XmlNsForm.UNQUALIFIED ,
        xmlns = { 
        @XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi")})
package your.package;

import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
1
Amio.io

Ajoutez votre correspondance nsPrefix en procédant comme suit:

marshaller.setNamespaceMapping("myns","urn:foo");

0
Eason Xiao

C'est du XML, vous pouvez donc traiter la sortie en utilisant DOM ou XSLT pour vous débarrasser de plusieurs références d'espace de nom.

0
java.is.for.desktop