Qual é o objetivo das classes ObjectFactory do JAXB 2?

98

Eu sou novo no uso do JAXB e usei o xjc do JAXB 2.1.3 para gerar um conjunto de classes do meu esquema XML. Além de gerar uma classe para cada elemento em meu esquema, ele criou uma classe ObjectFactory.

Parece que nada me impede de instanciar os elementos diretamente, por exemplo

MyElement element = new MyElement();

enquanto os tutoriais parecem preferir

MyElement element = new ObjectFactory().createMyElement();

Se eu olhar para ObjectFactory.java, vejo:

public MyElement createMyElement() {
    return new MyElement();
}

então qual é o problema? Por que eu deveria me preocupar em manter a classe ObjectFactory por perto? Presumo que também será sobrescrito se eu for recompilar a partir de um esquema alterado.

Andrew Coleson
fonte
Não tenho certeza se é o design pretendido, mas descobri que ObjectFactory é uma classe ideal para usar na criação de JAXBContext. Você precisa enumerar algumas classes lá e JAXB seguirá seus métodos, etc, então eles são algo como raízes. E ObjectFactory tem referências a todos os elementos, então é suficiente apenas usar ObjectFactory.class para criar JAXBContext com todas as classes relevantes.
vbezhenar

Respostas:

68

A compatibilidade com versões anteriores não é a única razão. :-P

Com esquemas mais complicados, como aqueles que têm restrições complicadas nos valores que o conteúdo de um elemento pode assumir, às vezes você precisa criar JAXBElementobjetos reais . Normalmente, eles não são triviais de criar manualmente, portanto, os create*métodos fazem o trabalho difícil para você. Exemplo (do esquema XHTML 1.1):

@XmlElementDecl(namespace = "http://www.w3.org/1999/xhtml", name = "style", scope = XhtmlHeadType.class)
public JAXBElement<XhtmlStyleType> createXhtmlHeadTypeStyle(XhtmlStyleType value) {
    return new JAXBElement<XhtmlStyleType>(_XhtmlHeadTypeStyle_QNAME, XhtmlStyleType.class, XhtmlHeadType.class, value);
}

É assim que você <style>transforma uma <head>tag em uma tag:

ObjectFactory factory = new ObjectFactory();
XhtmlHtmlType html = factory.createXhtmlHtmlType();
XhtmlHeadType head = factory.createXhtmlHeadType();
html.setHead(head);
XhtmlStyleType style = factory.createXhtmlStyleType();
head.getContent().add(factory.createXhtmlHeadTypeStyle(style));

Os três primeiros usos do ObjectFactorypodem ser considerados supérfluos (embora úteis para consistência), mas o quarto torna o JAXB muito, muito mais fácil de usar. Imaging tendo que escrever um new JAXBElementà mão todas as vezes!

Chris Jester-Young
fonte
Você pode dar um exemplo / referência do que (ou quão complicado) um elemento Schema precisa ser para que create * () faça algo útil? Estou tendo problemas para encontrar a parte do Schema que você está referenciando com seu exemplo JAXB. Se meu esquema ficar mais complicado depois, certamente seria bom criar * para lidar com parte dele para mim, mas como ele é criado * nem mesmo se preocupa em criar subelementos por conta própria ..
Andrew Coleson
Se você baixar os tarballs XHTML 1.1 e XHTML Modularization 1.1, você encontrará diretórios dentro de chamados "SCHEMA". Coloque todos os arquivos .xsd nos mesmos diretórios. Alguns dos arquivos .xsd também importarão w3.org/2001/xml.xsd ; você vai querer ajustar os locais apropriadamente se não quiser que o arquivo seja baixado toda vez que executar o xjc. [cont]
Chris Jester-Young
[cont] A parte específica do .xsd que especifica o conteúdo de um <head> está, neste caso, em xhtml11-model-1.xsd, no grupo xhtml.head.content.
Chris Jester-Young
2
Em qualquer caso, ninguém está apontando uma arma para sua cabeça dizendo que você deve usar ObjectFactory (embora eu ache útil de usar), mas quando você se deparar com um caso em que seja genuinamente útil, você saberá. :-)
Chris Jester-Young
Obrigado! Acho que meu esquema não é complicado o suficiente, mas vou mantê-lo em mente no futuro. :) Eu sabia que devia estar faltando alguma coisa.
Andrew Coleson
39

Como @Chris apontou, às vezes JAXB não pode funcionar com POJOs, porque o esquema não pode ser mapeado exatamente para Java. Nesses casos, os JAXBElementobjetos de invólucro são necessários para fornecer as informações de tipo adicionais.

Existem dois exemplos concretos que encontrei em que isso é comum.

  • Se você deseja empacotar um objeto de uma classe que não possui a @XmlRootElementanotação. Por padrão, o XJC gera apenas @XmlRootElementpara alguns elementos e não para outros. A lógica exata para isso é um pouco complicada, mas você pode forçar o XJC a gerar mais @XmlRootElementclasses usando o "modo de ligação simples"

  • Quando seu esquema usa grupos de substituição. Isso é um uso de esquema bastante avançado, mas o XJC traduz grupos de substituição em Java fazendo uso intenso de JAXBElementwrappers.

Portanto, em um modelo de objeto gerado por XJC que faz uso intenso JAXBElement(por qualquer motivo), você precisa de uma maneira de construir essas JAXBElementinstâncias. O gerado ObjectFactoryé de longe a maneira mais fácil de fazer isso. Você mesmo pode construí-los, mas é desajeitado e sujeito a erros de fazer.

skaffman
fonte
Obrigado pelos exemplos adicionais!
Andrew Coleson,
2
Uau, essa é uma resposta vencedora. +1
Chris Jester-Young,
Eu gosto de usar o annox para gerar o XmlRootElement em 95% do tempo se eu tiver um elemento que se refere a um complexType, eu quero o XmlRootElement (bem, mais como 100%, pois não achei o caso de uso onde não quero isso ainda)
Dean Hiller
9

Compatibilidade com versões anteriores, eu acho ...

http://weblogs.java.net/blog/kohsuke/archive/2005/08/a_story_of_migr.html :

... Não há mais ObjectFactory.createXYZ. O problema com esses métodos de fábrica é que eles lançam um JAXBException verificado. Agora você pode simplesmente fazer um novo XYZ (), sem mais blocos try / catch. (Eu sei, eu sei, ... esta é uma daquelas coisas do tipo "o que estávamos pensando !?") ...

Bert F
fonte