2011-03-24 15 views
5

Tengo un esquema que define valores predeterminados para elementos y atributos. Estoy tratando de analizar un documento utilizando JAXB basado en ese esquema, pero JAXB no está estableciendo los valores predeterminados. ¿Alguna idea sobre cómo hacer que JAXB respete los valores predeterminados del esquema?¿JAXB admite valores de esquema predeterminados?

example.xsd:

<?xml version="1.0" encoding="UTF-8"?><xs:schemaxmlns:xs="http://www.w3.org/2001/XMLSchema" 
targetNamespace="http://www.example.org/example" 
xmlns:tns="http://www.example.org/example"> 

<xs:element name="root" type="tns:rootType"/> 

<xs:complexType name="rootType"> 
    <xs:sequence> 
     <xs:element name="child" type="tns:childType"/> 
    </xs:sequence> 
</xs:complexType> 

<xs:complexType name="childType"> 
    <xs:sequence> 
     <xs:element name="childVal" type="xs:string" default="defaultElVal"/> 
    </xs:sequence> 
    <xs:attribute name="attr" type="xs:string" default="defaultAttrVal"/> 
</xs:complexType> 

example1.xml

<?xml version="1.0" encoding="UTF-8"?> 
<tns:root xmlns:tns="http://www.example.org/example" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/example example.xsd "> 
    <child> 
    <childVal/> 
    </child> 
</tns:root> 

TestParser.java

package test; 
import java.io.File; 
import javax.xml.XMLConstants; 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.validation.Schema; 
import javax.xml.validation.SchemaFactory; 
public class TestParser {  
    public static void main(String[] pArgs) { 
     try { 
      JAXBContext context = JAXBContext.newInstance(RootElement.class); 
      Unmarshaller unmarshaller = context.createUnmarshaller(); 

      SchemaFactory schemaFac = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 
      Schema sysConfigSchema = schemaFac.newSchema(
        new File("example.xsd")); 
      unmarshaller.setSchema(sysConfigSchema); 
      RootElement root = (RootElement)unmarshaller.unmarshal(
        new File("example1.xml")); 
      System.out.println("Child Val: " + root.getChild().getChildVal()); 
      System.out.println("Child Attr: " + root.getChild().getAttr()); 
     } catch (Exception e) { 
      System.out.println(e.getMessage()); 
      e.printStackTrace(); 
     } 
    } 
} 

RootElement.java

package test; 
import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement(name="root", namespace="http://www.example.org/example") 
public class RootElement { 

    private ChildEl child; 

    public RootElement() {} 

    public ChildEl getChild() { 
     return child; 
    } 

    public void setChild(ChildEl pChild) { 
     this.child = pChild; 
    } 
} 

ChildEl.java

package test; 

import javax.xml.bind.annotation.XmlAttribute; 
import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement(name="child") 
public class ChildEl { 

    private String attr; 
    private String childVal; 

    public ChildEl() {}; 

    @XmlAttribute 
    public String getAttr() { 
     return attr; 
    } 
    public void setAttr(String pAttr) { 
     this.attr = pAttr; 
    } 

    public String getChildVal() { 
     return childVal; 
    } 
    public void setChildVal(String pVal) { 
     this.childVal = pVal; 
    } 

} 

Respuesta

9

Elemento Valor predeterminado

Para obtener el valor por defecto en la propiedad elemento necesita anotar la siguiente manera:

@XmlElement(defaultValue="defaultElVal") 
public String getChildVal() { 
    return childVal; 
} 

Atributo Valor predeterminado

Si usa EclipseLink JAXB (MOXy), obtendrá el valor de atributo predeterminado con el código proporcionado. Puede haber un error en la implementación de Metro de JAXB que impide que esto funcione. Tenga en cuenta que lidero la implementación de MOXy.


enfoque alternativo

El siguiente código debería funcionar con cualquier aplicación JAXB sin requerir ningún cambio de código a su modelo. Se podría hacer lo siguiente y el apalancamiento SAXSource:

import java.io.File; 
import java.io.FileInputStream; 

import javax.xml.XMLConstants; 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.parsers.SAXParserFactory; 
import javax.xml.transform.sax.SAXSource; 
import javax.xml.validation.Schema; 
import javax.xml.validation.SchemaFactory; 

import org.xml.sax.InputSource; 
import org.xml.sax.XMLReader; 
public class TestParser {  
    public static void main(String[] pArgs) { 
     try { 
      JAXBContext context = JAXBContext.newInstance(RootElement.class); 
      Unmarshaller unmarshaller = context.createUnmarshaller(); 

      SchemaFactory schemaFac = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 
      Schema sysConfigSchema = schemaFac.newSchema(
        new File("example.xsd")); 

      SAXParserFactory spf = SAXParserFactory.newInstance(); 
      spf.setNamespaceAware(true); 
      spf.setSchema(sysConfigSchema); 
      XMLReader xmlReader = spf.newSAXParser().getXMLReader(); 
      SAXSource source = new SAXSource(xmlReader, new InputSource(new FileInputStream("example1.xml"))); 
      RootElement root = (RootElement)unmarshaller.unmarshal(
        source); 
      System.out.println("Child Val: " + root.getChild().getChildVal()); 
      System.out.println("Child Attr: " + root.getChild().getAttr()); 
     } catch (Exception e) { 
      System.out.println(e.getMessage()); 
      e.printStackTrace(); 
     } 
    } 
} 
4

encuentro lo que estamos tratando de hacer tan sensible, sobre todo para tener armonía con Atributo vs. elementos simples. Me parece extraño, pero parece que los implementadores de jaxb2 eligen agregar un conjunto extra de extensiones que debe agregar para obtener el comportamiento deseado. Ver:.

(Me hubiera gustado ver más comportamientos predeterminados naturales y la coherencia entre los atributos y elementos de tipos al menos simples - sin tener que registrarse un plugin a continuación, proporcionar un tapón -en casos especiales. Mis únicos chicos sobre por qué no se hizo esto o compatibilidad retroactiva: una suposición.)

El complemento de valor predeterminado de jaxb2-commons se refiere a los comandos adicionales (y jarras) que se agregan a xjc que a su vez agrega comportamientos predeterminados Al campo.En mi caso: (. Cuando, por supuesto, el cheque nulo condicional presenta el valor predeterminado o no)

public String getScalarOptionalMaxAndDefaultString() { 
    if (scalarOptionalMaxAndDefaultString == null) { 
     return "def val 1"; 
    } else { 
     return scalarOptionalMaxAndDefaultString; 
    } 
} 

Usando Blaise Doughan parece un trabajo práctico alrededor. Dependiendo de la naturaleza de su documento XML, esto puede ser perfecto.

Sin embargo, parece que este complemento de Valor predeterminado podría mover la solución al proceso de compilación y no ver un cambio en el código (suponiendo que está utilizando un Dom en comparación con el analizador de Sax propuesto por Blaise).

Parece que el complemento de valor predeterminado resuelve el problema y posiblemente proporciona una extensibilidad adicional (no ha necesitado tal personalización avanzada) en el caso improbable de que necesite aún más control de valor por defecto de programación ejecutando xjc.

Aquí hay un fragmento experto config en caso de que ayuda:

<plugin> 
    <groupId>org.jvnet.jaxb2.maven2</groupId> 
    <artifactId>maven-jaxb2-plugin</artifactId> 
    <version>0.8.0</version> 
    <executions> 
     <execution> 
     <phase>generate-sources</phase> 
     <goals> 
      <goal>generate</goal> 
     </goals> 
     <configuration> 
      <args> 
       <arg>-Xdefault-value</arg> 
      </args> 
      <plugins> 
       <plugin> 
        <groupId>org.jvnet.jaxb2_commons</groupId> 
        <artifactId>jaxb2-default-value</artifactId> 
        <version>1.1</version> 
       </plugin> 
      </plugins> 
     </configuration> 
     </execution> 
    </executions> 
    <configuration><schemaDirectory>src/test/resources</schemaDirectory></configuration> 
    </plugin> 
1

Otra forma de establecer el valor por defecto puede ser una función beforeMarshal (Marshaller marshaller):

private void beforeMarshal(Marshaller marshaller) { 
    childVal = (null == getChildVal) ? CHILD_VAL_DEFAULT_VALUE : childVal; } 
Cuestiones relacionadas