Pregunta

Estoy intentando generar clases Java desde la versión 4.5 de FpML (Finanial Products Markup Language). Se genera una tonelada de código, pero no puedo usarlo. Intentando serializar un documento simple obtengo esto:

javax.xml.bind.MarshalException
  - with linked exception: [com.sun.istack.SAXException2: unable
  to marshal type
  "org.fpml._2008.fpml_4_5.PositionReport"
  as an element because it is missing an
  @XmlRootElement annotation]

De hecho, no las clases tienen la anotación @XmlRootElement, ¿qué puedo hacer mal? Señalo xjc (JAXB 2.1) a fpml-main-4-5.xsd, que luego incluye todos los tipos.

¿Fue útil?

Solución

Para unir lo que otros ya han dicho o sugerido, las reglas mediante las cuales JAXB XJC decide si colocar o no la anotación @XmlRootElement en una clase generada no son triviales ( vea este artículo ).

@XmlRootElement existe porque el tiempo de ejecución de JAXB requiere cierta información para ordenar / desmarcar un objeto dado, específicamente el nombre del elemento XML y el espacio de nombres. No puedes simplemente pasar cualquier objeto viejo al Marshaller. @XmlRootElement proporciona esta información.

La anotación es solo una conveniencia, sin embargo, JAXB no la requiere. La alternativa es utilizar los objetos de envoltorio JAXBElement , que proporcionan la misma información que @XmlRootElement , pero en la forma de un objeto, en lugar de una anotación.

Sin embargo, los objetos JAXBElement son difíciles de construir, ya que necesita conocer el nombre del elemento XML y el espacio de nombres, que la lógica de negocios generalmente no conoce.

Afortunadamente, cuando XJC genera un modelo de clase, también genera una clase llamada ObjectFactory . Esto se debe en parte a la compatibilidad hacia atrás con JAXB v1, pero también es un lugar donde XJC pone métodos de fábrica generados que crean envoltorios JAXBElement alrededor de sus propios objetos. Maneja el nombre XML y el espacio de nombres por usted, por lo que no necesita preocuparse por eso. Solo tiene que mirar a través de los métodos ObjectFactory (y para un gran esquema, puede haber cientos de ellos) para encontrar el que necesita.

Otros consejos

Esto se menciona en la parte inferior de la publicación del blog ya vinculada anteriormente, pero funciona como un regalo para mí:

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(new JAXBElement<MyClass>(new QName("uri","local"), MyClass.class, myClassInstance), System.out);

Como se indicó en una de las respuestas anteriores, no obtendrá un XMLRootElement en su elemento raíz si en el XSD su tipo se define como un tipo con nombre, ya que ese tipo con nombre podría usarse en otro lugar en su XSD. Intente marcarlo de forma anónima, es decir, en lugar de:

<xsd:element name="myRootElement" type="MyRootElementType" />

<xsd:complexType name="MyRootElementType">
...
</xsd:complexType>

tendrías:

<xsd:element name="myRootElement">
    <xsd:complexType>
    ...
    <xsd:complexType>
</xsd:element>

@XmlRootElement no es necesario para desmarcar, si uno usa la forma de 2 parámetros de Unmarshaller # unmarshall.

Entonces, si en lugar de hacer:

UserType user = (UserType) unmarshaller.unmarshal(new StringReader(responseString));

uno debe hacer:

JAXBElement<UserType> userElement = unmarshaller.unmarshal(someSource, UserType.class);
UserType user = userElement.getValue();

El último código no requerirá la anotación @XmlRootElement en el nivel de clase UserType.

La respuesta de Joe (Joe, 26 de junio de 2009 a las 17:26) lo hace por mí. La respuesta simple es que la ausencia de una anotación de @XmlRootElement no es un problema si calificas un JAXBElement. Lo que me confundió es que el ObjectFactory generado tiene 2 métodos createMyRootElement: el primero no toma parámetros y da el objeto sin envolver, el segundo toma el objeto sin envolver y lo devuelve envuelto en un JAXBElement, y me parece que JAXBElement funciona bien. Aquí está el código básico que utilicé (soy nuevo en esto, así que pido disculpas si el código no tiene el formato correcto en esta respuesta), en gran parte de texto del enlace :

ObjectFactory objFactory = new ObjectFactory();
MyRootElement root = objFactory.createMyRootElement();
...
// Set root properties
...
if (!writeDocument(objFactory.createMyRootElement(root), output)) {
    System.err.println("Failed to marshal XML document");
}
...

private boolean writeDocument(JAXBElement document, OutputStream output) {

  Class<?> clazz = document.getValue().getClass();
  try {
    JAXBContext context =
        JAXBContext.newInstance(clazz.getPackage().getName());
    Marshaller m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    m.marshal(document, output);
    return true;

  } catch (JAXBException e) {
    e.printStackTrace(System.err);
    return false;
  }
}

Puede solucionar este problema utilizando el enlace de ¿Cómo generar clases de @XmlRootElement para tipos base en XSD? .

Aquí hay un ejemplo con Maven

        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jaxb2-maven-plugin</artifactId>
            <version>1.3.1</version>
            <executions>
                <execution>
                    <id>xjc</id>
                    <goals>
                        <goal>xjc</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <schemaDirectory>src/main/resources/xsd</schemaDirectory>
                <packageName>com.mycompany.schemas</packageName>
                <bindingFiles>bindings.xjb</bindingFiles>
                <extension>true</extension>
            </configuration>
        </plugin>

Aquí está el contenido del archivo binding.xjb

<?xml version="1.0"?>
<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
              xmlns:xjc= "http://java.sun.com/xml/ns/jaxb/xjc"
              jxb:extensionBindingPrefixes="xjc" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <jxb:bindings schemaLocation="path/to/myschema.xsd" node="/xs:schema">
        <jxb:globalBindings>
            <xjc:simple/>
        </jxb:globalBindings>
    </jxb:bindings>
</jxb:bindings>

Como sabe, la respuesta es usar ObjectFactory (). Aquí hay una muestra del código que me funcionó :)

ObjectFactory myRootFactory = new ObjectFactory();

MyRootType myRootType = myRootFactory.createMyRootType();

try {

        File file = new File("./file.xml");
        JAXBContext jaxbContext = JAXBContext.newInstance(MyRoot.class);
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

        //output pretty printed
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        JABXElement<MyRootType> myRootElement = myRootFactory.createMyRoot(myRootType);

        jaxbMarshaller.marshal(myRootElement, file);
        jaxbMarshaller.marshal(myRootElement, System.out);

    } catch (JAXBException e) {
        e.printStackTrace();
    }

Tampoco nos está funcionando. Pero sí encontramos un artículo ampliamente citado que agrega ALGUNOS antecedentes ... Lo enlazaré aquí por el bien de la siguiente persona: http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html

¡En caso de que mi experiencia con este problema le dé a alguien un Eureka! momento .. voy a añadir lo siguiente:

También tuve este problema, cuando utilizaba un archivo xsd que había generado utilizando IntelliJ " Generar xsd desde el documento de instancia " opción de menú.

Cuando acepté todos los valores predeterminados de esta herramienta, generó un archivo xsd que cuando se usó con jaxb, generó archivos java sin @XmlRootElement . En el tiempo de ejecución, cuando intenté reunir, obtuve la misma excepción que se explica en esta pregunta.

Regresé a la herramienta IntellJ y vi la opción predeterminada en el " Tipo de Desgin " El menú desplegable (que por supuesto no entendí ... y aún no entiendo si soy honesto) fue:

Tipo de diseño:

  

" elementos locales / tipos complejos globales "

Cambié esto a

  

" elementos / tipos locales "

, ahora generó un xsd (sustancialmente) diferente, que produjo el @XmlRootElement cuando se usó con jaxb. No puedo decir que entiendo lo de dentro y fuera de eso, pero funcionó para mí.

Con una compilación de Maven, puede agregar la anotación @XmlRootElement

con el " jaxb2-basics-annotate " plug-in.

Ver más información: ver

Configure Maven para generar clases a partir del esquema XML usando JAXB

y JAXB XJC code generation

¿Intentaste cambiar tu xsd de esta manera?

<!-- create-logical-system -->
<xs:element name="methodCall">
  <xs:complexType>
    ...
  </xs:complexType>
</xs:element>

Las envolturas JAXBElement funcionan en los casos en que JAXB no genera @XmlRootElement . Estos envoltorios están disponibles en la clase ObjectFactory generada por maven-jaxb2-plugin . Por ejemplo:

     public class HelloWorldEndpoint {
        @PayloadRoot(namespace = NAMESPACE_URI, localPart = "person")
        @ResponsePayload
        public JAXBElement<Greeting> sayHello(@RequestPayload JAXBElement<Person> request) {

        Person person = request.getValue();

        String greeting = "Hello " + person.getFirstName() + " " + person.getLastName() + "!";

        Greeting greet = new Greeting();
        greet.setGreeting(greeting);

        ObjectFactory factory = new ObjectFactory();
        JAXBElement<Greeting> response = factory.createGreeting(greet);
        return response;
      }
 }

Después de sruggling durante dos días, encontré la solución al problema. Puede usar la clase ObjectFactory para solucionar las clases que no tienen el @XmlRootElement . ObjectFactory ha sobrecargado los métodos para envolverlo alrededor de JAXBElement. Método: 1 realiza la creación simple del objeto y Método: 2 envuelve el objeto con @JAXBElement . Siempre use el Método: 2 para evitar javax.xml.bind.MarshalException - a la excepción vinculada le falta una anotación @XmlRootElement

Método: 1

public GetCountry createGetCountry() {
        return new GetCountry();
    }

Método:2

 @XmlElementDecl(namespace = "my/name/space", name = "getCountry")
 public JAXBElement<GetCountry> createGetCountry(GetCountry value) {
        return new JAXBElement<GetCountry>(_GetCountry_QNAME, GetCountry.class, null, value);
    }

Ejemplo de código de trabajo:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
WebServiceTemplate springWSTemplate = context.getBean(WebServiceTemplate.class);

GetCountry request = new GetCountry();
request.setGuid("1f3e1771-3049-49f5-95e6-dc3732c3227b");

JAXBElement<GetCountryResponse> jaxbResponse = (JAXBElement<GetCountryResponse>)springWSTemplate .marshalSendAndReceive(new ObjectFactory().createGetCountry(request));

GetCountryResponse response = jaxbResponse.getValue();

Para realizar la reducción, debe configurar un enlace xml antes de compilar con wsimport, estableciendo generateElementProperty como falso.

     <jaxws:bindings wsdlLocation="LOCATION_OF_WSDL"
      xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
      xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" 
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
      xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
         <jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
    <jaxws:bindings  node="wsdl:definitions/wsdl:types/xs:schema[@targetNamespace='NAMESPACE_OF_WSDL']">
      <jxb:globalBindings xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <xjc:generateElementProperty>false</xjc:generateElementProperty> 
      </jxb:globalBindings>
  </jaxws:bindings>
</jaxws:bindings>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top