2011-08-25 1 views
8

Je dispose d'un fichier XML:JAXB: Comment puis-je désorganiser XML sans namespaces

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<object> 
    <str>the type</str> 
    <bool type="boolean">true</bool>   
</object> 

Et je veux désorganiser à un objet de la classe ci-dessous

@XmlRootElement(name="object") 
public class Spec { 
    public String str; 
    public Object bool; 

} 

Comment puis-je faire ? Sauf si je spécifie des espaces de noms (voir ci-dessous), cela ne fonctionne pas.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<object> 
    <str>the type</str> 
    <bool xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     xsi:type="xs:boolean">true</bool>   
</object> 

Répondre

5

MISE À JOUR

Vous pouvez obtenir ce travail en introduisant une couche intermédiaire pour traduire entre type et xsi:type. Ci-dessous exemple d'utilisation du StAX StreamReaderDelegate à faire pour l'opération unmarshal JAXB:

package forum7184526; 

import java.io.FileInputStream; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.stream.XMLInputFactory; 
import javax.xml.stream.XMLStreamReader; 
import javax.xml.stream.util.StreamReaderDelegate; 

import org.eclipse.persistence.oxm.XMLConstants; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     XMLInputFactory xif = XMLInputFactory.newFactory(); 
     XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("input.xml")); 
     xsr = new XsiTypeReader(xsr); 

     JAXBContext jc = JAXBContext.newInstance(Spec.class); 
     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     Spec spec = (Spec) unmarshaller.unmarshal(xsr); 
    } 

    private static class XsiTypeReader extends StreamReaderDelegate { 

     public XsiTypeReader(XMLStreamReader reader) { 
      super(reader); 
     } 

     @Override 
     public String getAttributeNamespace(int arg0) { 
      if("type".equals(getAttributeLocalName(arg0))) { 
       return XMLConstants.SCHEMA_INSTANCE_URL; 
      } 
      return super.getAttributeNamespace(arg0); 
     } 

    } 
} 

xsi:type est un mécanisme de schéma pour spécifier le type réel d'un élément (similaire à un casting en Java). Si vous supprimez l'espace de noms, vous modifiez la sémantique du document.

En EclipseLink JAXB (MOXy) nous vous permet de définir votre propre indicateur d'héritage pour les objets de domaine en utilisant @XmlDescriminatorNode et @XmlDescrimatorValue. Nous ne proposons pas ce type de personnalisation pour les propriétés de type de données:

+0

Merci pour votre réponse, Blaise. Je vais bien avoir les espaces de noms. Mais est-il possible de ne pas avoir d'espace de noms dans le XML (car il va faire face aux utilisateurs de dev) mais de garder cette information avec les annotations de classe Java, de sorte que jaxb analyse le XML, voit "type" et sait "aha, c'est de l'espace de noms 'xsi' selon la déclaration de classe Java ". Idem avec "xs: boolean". Voyez-vous ce que je dis? Ou ce n'est toujours pas possible? J'en ai besoin parce que nos utilisateurs de développement vont créer de telles déclarations XML et nous devons les déconstruire à Java. – Andrey

+0

@Andrey - Vous pouvez faire ce travail, ci-dessous est une approche que vous pourriez faire pour le marxiste. –

+0

Salut Blaise. La suppression d'un espace de noms pour l'attribut "type" a bien fonctionné, MERCI BEAUCOUP !!!, mais comment puis-je supprimer la déclaration d'espace de noms "xs" pour la valeur de l'attribut "type" - "xs: boolean". J'ai joué en remplaçant certaines des méthodes StreamReaderDelegate, mais sans succès. – Andrey

2

Basé sur les commentaires de Blaise (merci Blaise!) Et mes recherches. Voici la solution à mon problème. Êtes-vous d'accord avec ce Blaise, ou vous avez un meilleur moyen?

package forum7184526; 

import java.io.FileInputStream; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.stream.XMLInputFactory; 
import javax.xml.stream.XMLStreamReader; 
import javax.xml.stream.util.StreamReaderDelegate; 

import org.eclipse.persistence.oxm.XMLConstants; 

public class Demo { 

public static void main(String[] args) throws Exception { 
    XMLInputFactory xif = XMLInputFactory.newFactory(); 
    XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("input.xml")); 
    xsr = new XsiTypeReader(xsr); 

    JAXBContext jc = JAXBContext.newInstance(Spec.class); 
    Unmarshaller unmarshaller = jc.createUnmarshaller(); 
    Spec spec = (Spec) unmarshaller.unmarshal(xsr); 
} 

private static class XsiTypeReader extends StreamReaderDelegate { 

    public XsiTypeReader(XMLStreamReader reader) { 
     super(reader); 
    } 

    @Override 
    public String getAttributeNamespace(int arg0) { 
     if("type".equals(getAttributeLocalName(arg0))) { 
      return "http://www.w3.org/2001/XMLSchema-instance"; 
     } 
     return super.getAttributeNamespace(arg0); 
    } 

    @Override 
    public String getAttributeValue(int arg0) { 
     String n = getAttributeLocalName(arg0); 
     if("type".equals(n)) { 
     String v = super.getAttributeValue(arg0); 
      return "xs:"+ v; 
     } 
     return super.getAttributeValue(arg0); 
    } 
    @Override 
    public NamespaceContext getNamespaceContext() { 
     return new MyNamespaceContext(super.getNamespaceContext()); 
    } 
} 

private static class MyNamespaceContext implements NamespaceContext { 
    public NamespaceContext _context; 
    public MyNamespaceContext(NamespaceContext c){ 
    _context = c; 
    } 

    @Override 
    public Iterator<?> getPrefixes(String namespaceURI) { 
    return _context.getPrefixes(namespaceURI); 
    } 

    @Override 
    public String getPrefix(String namespaceURI) { 
    return _context.getPrefix(namespaceURI); 
    } 

    @Override 
    public String getNamespaceURI(String prefix) { 
    if("xs".equals(prefix)) { 
     return "http://www.w3.org/2001/XMLSchema"; 
    } 
    return _context.getNamespaceURI(prefix); 
    } 
} 
} 
7

Un moyen plus facile peut-être utiliser unmarshalByDeclaredType, puisque vous connaissez déjà le type que vous voulez désorganiser.

En utilisant

Unmarshaller.unmarshal(rootNode, MyType.class); 

vous n'avez pas besoin d'avoir une déclaration d'espace de noms dans le fichier XML, puisque vous passez dans le JAXBElement qui a l'espace de noms déjà défini. Ceci est également parfaitement légal, puisque vous n'avez pas besoin de référencer un espace de noms dans une instance XML, voir http://www.w3.org/TR/xmlschema-0/#PO - et de nombreux clients produisent du XML de cette manière.

Finalement, je l'ai mis au travail.Notez que vous devez supprimer n'importe quel espace de noms personnalisé dans le schéma; ici qui fonctionne de code:

Le schéma:

<xsd:schema 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
<xsd:element name="customer"> 
    <xsd:complexType> 
     <xsd:sequence minOccurs="1" maxOccurs="1"> 
     <xsd:element name="name" type="xsd:string" minOccurs="1" maxOccurs="1" /> 
     <xsd:element name="phone" type="xsd:string" minOccurs="1" maxOccurs="1" /> 
     </xsd:sequence> 
    </xsd:complexType> 
</xsd:element> 

XML:

<?xml version="1.0" encoding="UTF-8"?> 
<customer> 
    <name>Jane Doe</name> 
    <phone>08154712</phone> 
</customer> 

Code JAXB:

JAXBContext jc = JAXBContext.newInstance(Customer.class); 
Unmarshaller u = jc.createUnmarshaller(); 
u.setSchema(schemaInputStream); // load your schema from File or any streamsource 
Customer = u.unmarshal(new StreamSource(inputStream), clazz); // pass in your XML as inputStream