Pergunta

Eu estou tentando gerar classes Java a partir do FpML (Finanial produtos Markup Language) versão 4.5. Uma tonelada de código é gerado, mas não posso usá-lo. Tentando serializar um documento simples fico com esta:

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]

Na verdade não classes possuem ter a anotação @XmlRootElement, então o que posso estar fazendo de errado ?. Estou apontando xjc (JAXB 2.1) para FPML-main-4-5.xsd, que, em seguida, inclui todos os tipos.

Foi útil?

Solução

Para unir o que os outros já disse ou insinuou, as regras pelas quais JAXB XJC decide se deve ou não colocar a anotação @XmlRootElement em uma classe gerada não são trivial ( ver este artigo ).

@XmlRootElement existe porque o tempo de execução JAXB requer certas informações, a fim de marechal / unmarshal um determinado objeto, especificamente o nome do elemento XML e namespace. Você não pode apenas passar qualquer objeto antigo para o Marshaller. @XmlRootElement fornece essas informações.

A anotação é apenas uma conveniência, no entanto - JAXB não requer isso. A alternativa para é a utilização de JAXBElement invólucro objectos, os quais fornecem a mesma informação que @XmlRootElement, mas sob a forma de um objecto, em vez de uma anotação.

No entanto, objetos JAXBElement são difíceis de construir, desde que você precisa saber o nome do elemento XML e namespace, que a lógica de negócios geralmente não faz.

Felizmente, quando XJC gera um modelo de classe, ele também gera uma classe chamada ObjectFactory. Isto é em parte lá para compatibilidade com JAXB v1, mas é também lá como um lugar para XJC para colocar métodos de fábrica gerados que criam wrappers JAXBElement em torno de seus próprios objetos. Ele lida com o nome XML e namespace para você, então você não precisa se preocupar com isso. Você só precisa olhar através dos métodos ObjectFactory (e para grande esquema, pode haver centenas deles) para encontrar o que você precisa.

Outras dicas

Esta é mencionado na parte inferior do post já ligada acima mas isso funciona como um deleite para mim:

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 sugerido em uma das respostas acima, você não vai obter uma XMLRootElement em seu elemento raiz se no XSD seu tipo é definido como um tipo de chamada, uma vez que esse tipo de chamada pode ser usado em outro lugar em sua XSD. Tente mking que um tipo anônimo, ou seja, em vez de:

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

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

você teria que:

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

@XmlRootElement não é necessário para unmarshalling -. Se alguém usa o formulário 2 parâmetro de unmarshaller # desempacotar

Assim, se em vez de fazer:

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

se deve fazer:

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

O último código não vai exigir anotação @XmlRootElement no nível de classe UserType.

resposta de Joe (Joe 26 jun '09 em 17:26) faz isso por mim. A resposta simples é que a ausência de uma anotação @XmlRootElement não é problema se você empacotar uma JAXBElement. A única coisa que me confundiu é o ObjectFactory gerado tem 2 métodos createMyRootElement - o primeiro não tem parâmetros e dá o objeto desembrulhado, a segunda leva o objeto desembrulhado e devolve-lo envolto em uma JAXBElement e triagem que JAXBElement funciona bem. Aqui está o código básico que eu usei (eu sou novo para isso, então desculpas se o código não está formatado corretamente nesta resposta), em grande parte plagiou link texto :

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

Você pode corrigir esse problema usando a ligação de como gerar Classes @XmlRootElement para Tipos base em XSD? .

Aqui está um exemplo com 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>

Aqui está o conteúdo do arquivo 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 você sabe a resposta é usar o ObjectFactory (). Aqui está um exemplo do código que funcionou para mim:)

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();
    }

Não está funcionando para nós também. Mas nós encontramos um artigo amplamente citado que adiciona algum fundo ... Vou ligar para ele aqui para o bem da pessoa seguinte: http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html

No caso de minha experiência deste problema dá a alguém um Eureka! momento .. vou acrescentar o seguinte:

Eu também estava ficando este problema, ao usar um arquivo XSD que eu tinha gerado usando IntelliJ de "Gerar xsd Instância documento da" opção de menu.

Quando aceitei todos os padrões desta ferramenta, é gerado um arquivo XSD que quando usado com jaxb, arquivos Java gerados sem @XmlRootElement. Em tempo de execução quando tentei organizar eu tenho a mesma exceção como discutido nesta questão.

Voltei para a ferramenta IntellJ, e viu a opção padrão no "Desgin tipo" drop down (que é claro que eu não entendi .. e ainda não sei se eu sou honesto) foi:

Desgin Tipo:

"elementos local / global tipos complexos"

Eu mudei isso para

"Elementos locais / tipos"

, agora, gerada uma (substancialmente) XSD diferente, que produziu o @XmlRootElement quando utilizado com jaxb. Não posso dizer que entender na de e fora do dele, mas ele trabalhou para mim.

Com uma compilação Maven, você pode adicionar o @XmlRootElement anotação

com o "jaxb2-basics-annotate" plug-in.

Veja mais informações: veja

Configurar Maven para gerar classes de XML Schema usando JAXB

JAXB XJC código de geração

Você tentou mudar sua XSD como este?

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

JAXBElement wrappers funciona para casos em que não @XmlRootElement é gerado pelo JAXB. Estes invólucros estão disponíveis na classe ObjectFactory gerado pelo maven-jaxb2-plugin. Por exemplo:

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

Depois de sruggling por dois dias eu encontrei a solução para o problem.You pode usar o ObjectFactory classe para solução alternativa para as classes que não têm o @XmlRootElement . ObjectFactory tem sobrecarregado métodos para envolvê-la em torno da JAXBElement. Método: 1 é que a simples criação do objeto e Método: 2 irá embrulhar o objeto com @JAXBElement . Sempre para uso Método: 2 para evitar javax.xml.bind.MarshalException - com exceção vinculada faltando um @XmlRootElement anotação

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);
    }

exemplo de código de trabalho:

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 soluction-lo você deve configurar uma xml vinculativo antes de compilar com wsimport, estabelecendo generateElementProperty como falsa.

     <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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top