2011-08-25 13 views
8

que tengo un archivo XML:JAXB: ¿Cómo se puede deserializar XML sin espacios de nombres

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

Y quiero deserializar a un objeto de la clase a continuación

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

} 

¿Cómo puedo hacer esto ? A menos que especifique espacios de nombres (ver a continuación), no funciona.

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

Respuesta

5

ACTUALIZACIÓN

Puede conseguir que esto funcione mediante la introducción de una capa intermedia para traducir entre type y xsi:type. A continuación se muestra el ejemplo del uso de StAX StreamReaderDelegate hacer esto para la operación 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 es un mecanismo de esquema para especificar el tipo real de un elemento (similar a un reparto en Java). Si elimina el espacio de nombres, está cambiando la semántica del documento.

En EclipseLink JAXB (MOXy) que le permiten especificar su propio indicador de la herencia de objetos de dominio utilizando @XmlDescriminatorNode y @XmlDescrimatorValue. En este momento no ofrecemos este tipo de personalización para las propiedades de tipo de datos:

+0

Gracias por su respuesta, Blaise. Estoy bien con tener los espacios de nombres. Pero, ¿es posible no tener espacios de nombres en el XML (porque se enfrentará a usuarios dev) pero mantener esa información con las anotaciones de la clase Java, para que cuando jaxb analice el XML, vea "tipo" y sepa "aha, es del espacio de nombres 'xsi' según la declaración de clase Java ". Lo mismo con "xs: boolean". ¿Ves lo que estoy diciendo? O todavía no es posible? Necesito esto porque nuestros usuarios dev van a crear tales declaraciones XML y tenemos que desmarcarlas en Java. – Andrey

+0

@Andrey - Puede hacer que esto funcione, a continuación se muestra un enfoque que podría utilizar para el unmarshal. –

+0

Hola Blaise. La eliminación de un espacio de nombres para el atributo "tipo" funcionó muy bien, GRACIAS MUCHO !!!, pero igual cómo puedo eliminar la declaración de espacio de nombres "xs" para el valor del atributo "tipo" - "xs: booleano". Jugué con la anulación de algunos de los métodos de StreamReaderDelegate, pero no tuve éxito. – Andrey

2

Basándose en los comentarios de Blaise Blaise (gracias!) Y mi investigación. Aquí está la solución a mi problema. ¿Estás de acuerdo con eso Blaise, o tienes una mejor manera?

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

Una forma más fácil sería utilizar unmarshalByDeclaredType, puesto que ya sabe el tipo que desea deserializar.

Mediante el uso de

Unmarshaller.unmarshal(rootNode, MyType.class); 

usted no necesita tener una declaración de espacio en el XML, ya que se pasa en el JAXBElement que tiene el espacio de nombres ya establecidos.

Esto también es perfectamente legal, ya que no es necesario que haga referencia a un espacio de nombres en una instancia XML, consulte http://www.w3.org/TR/xmlschema-0/#PO, y muchos clientes producen XML de esa manera.

Finalmente lo puso a trabajar.Tenga en cuenta que debe eliminar cualquier espacio de nombre personalizado en el esquema; aquí está funcionando código de ejemplo:

El esquema:

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

código 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 
Cuestiones relacionadas