Avançar para a área de conteúdo

ir para o conteúdo principal

developerWorks Brasil  >  Software livre  >

JiBX 1.2, Parte 2: Esquema XML para Código Java

Gerar código Java customizado mais limpo a partir do esquema XML

developerWorks
Ir para a página anteriorPágina 6 de 14 Ir para a próxima página

Opções de documento

Código de amostra


Classificar este tutorial

Ajude-nos a melhorar este conteúdo


Testando um Esquema do Mundo Real

Trabalhar com uma definição de esquema independente é excelente para uma demonstração simples, mas não fornece muito da sensação de como uma ferramenta funciona quando aplicada às definições de esquema complexas amplamente usadas em aplicativos corporativos. Agora, chegou a hora de seguir para um exemplo mais realista, na forma de uma das definições de esquema HR-XML padrão de mercado.

Esquema HR-XML TimeCard

O HR-XML Consortium é uma organização estabelecida para desenvolver padrões abertos para representações XML para recursos humanos. Representa mais de 110 membros corporativos e quase 50 empresas de tecnologia estão certificadas para atender seus padrões.

Os esquemas HR-XML usados para este tutorial consistem em 157 esquemas, incluindo uma combinação de definições de documentos de nível superior e componentes comuns. CodeGen pode tratar facilmente desse número de esquemas, mas o número de classes geradas e a complexidade dos interrelacionamentos deixariam obscuros os aspectos mais interessantes da manipulação de esquema. Para focar esses detalhes, o subconjunto de HR-XML usado aqui consiste em uma única definição de documento de nível superior, para o elemento TimeCard , juntamente com os componentes referidos como parte da definição TimeCard — um total de sete definições de esquema.

Você pode localizar o subconjunto de definições de esquema HR-XML usadas neste tutorial sob o diretório hrxml/schemas. A Listagem 7 mostra uma versão editada do esquema principal para a definição do elemento TimeCard . Isso fornece uma amostra do estilo de esquema HR-XML, que usa uma combinação de definições aninhadas e de tipo global e contém um intervalo de estruturas de esquema mais amplo do que o primeiro exemplo, incluindo:

  • <xs:choice> que são compositores (conforme mostrado em alguns dos complexTypes integrados à definição TimeCardType )
  • <xs:any> que são partículas (consulte a definição AdditionalDataType perto do início da listagem)
  • <xs:simpleType> <union> (consulte a definição TimeCardDuration no final da listagem)
  • Restrições <xs:simpleType> sem enumeração

Listagem 7. Esquemas HR-XML TimeCard
<xs:schema targetNamespace="http://ns.hr-xml.org/2007-04-15" ...
  elementFormDefault="qualified" version="2007-04-15">
  <xs:import namespace="http://www.w3.org/XML/1998/namespace" ...>
  <xs:include schemaLocation="../CPO/EntityIdType.xsd"/>
  ...
  <xs:complexType name="AdditionalDataType" mixed="true">
    ...
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
      <xs:any namespace="##any" processContents="strict" minOccurs="0"
          maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="type" type="xs:string"/>
  </xs:complexType>
 ...
  <xs:element name="TimeCard" type="TimeCardType"/>
  <xs:complexType name="TimeCardType">
    <xs:sequence>
      <xs:element name="Id" type="EntityIdType" minOccurs="0"/>
      <xs:element name="ReportedResource">
        <xs:complexType>
          <xs:choice>
            <xs:element name="Person" type="TimeCardPersonType"/>
            <xs:element name="Resource">
          <xs:complexType>
        <xs:sequence>
         <xs:element name="Id" type="EntityIdType"
           minOccurs="0" maxOccurs="unbounded"/>
         <xs:element name="ResourceName" type="xs:string" minOccurs="0"/>
         <xs:element name="AdditionalData" type="AdditionalDataType" minOccurs="0"
             maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute name="type" type="xs:string"/>
       </xs:complexType>
      </xs:element>
     </xs:choice>
    </xs:complexType>
   </xs:element>
   <xs:element name="ReportedTime" maxOccurs="unbounded">
    <xs:complexType>
     <xs:sequence>
      <xs:element name="PeriodStartDate" type="AnyDateTimeType"/>
      <xs:element name="PeriodEndDate" type="AnyDateTimeType"/>
      <xs:element name="ReportedPersonAssignment" minOccurs="0">
       <xs:complexType>
        <xs:sequence>
         <xs:element name="Id" type="EntityIdType" minOccurs="0"/>
        </xs:sequence>
       </xs:complexType>
      </xs:element>
      <xs:choice maxOccurs="unbounded">
       <xs:element name="TimeInterval">
        <xs:complexType>
         <xs:sequence>
          <xs:element name="Id" type="EntityIdType" minOccurs="0"/>
          <xs:element name="StartDateTime" type="AnyDateTimeType"/>
          <xs:choice>
           <xs:sequence>
            <xs:element name="EndDateTime" type="AnyDateTimeType"/>
            <xs:element name="Duration" type="TimeCardDuration" minOccurs="0"/>
           </xs:sequence>
           <xs:element name="Duration" type="TimeCardDuration"/>
          </xs:choice>
          <xs:element name="PieceWork" minOccurs="0" maxOccurs="unbounded">
           ...
          </xs:element>
          <xs:element name="RateOrAmount" minOccurs="0" maxOccurs="unbounded">
           ...
          </xs:element>
          <xs:element name="Allowance" minOccurs="0" maxOccurs="unbounded">
           ...
          </xs:element>
          ...
         </xs:sequence>
         <xs:attribute name="type" type="xs:string" use="required"/>
         ...
        </xs:complexType>
       </xs:element>
       <xs:element name="TimeEvent">
        ...
       </xs:element>
       <xs:element name="Expense">
        ...
       </xs:element>
       <xs:element name="Allowance">
        ...
       </xs:element>
      </xs:choice>
      ...
     </xs:sequence>
     ...
    </xs:complexType>
   </xs:element>
   ...
  </xs:sequence>
  <xs:attribute ref="xml:lang"/>
 </xs:complexType>
 ...
 <xs:simpleType name="TimeCardDuration">
  <xs:union memberTypes="xs:duration xs:decimal"/>
 </xs:simpleType>
</xs:schema>



Voltar para parte superior


Código Gerado para TimeCard

O arquivo build.xml Ant no diretório hrxml define os destinos Ant para testar a geração de código básico para o esquema TimeCard , incluindo a geração padrão e uns dois exemplos de customização (discutidos posteriormente). O diretório de amostra também contém um programa de teste, org.jibx.hrxml.Test. Desserializa doscumentos de amostra usando as classes do modelo de dados gerado e, em seguida, serializa os documentos de volta e compara o resultado ao documento original. E há um conjunto de documentos de teste da distribuição HR-XML no diretório samples. O destino codegen executa CodeGen usando padrões, compile compila o código gerado e o código de teste, bind compila a ligação JiBX e roundtrip executa o programa de teste nos documentos de amostra. Você também pode usar a tarefa full para executar todas essas etapas em sequência.

A maioria das formas de geração de códigos a partir do esquema gera uma classe separada para a definição complexType e para simpleTypes de enumeeração. CodeGen frequentemente é capaz de reduzir o número de classes geradas examinando referências e colocando as definições em sequência sempre que possível e igorando as definições não usadas em definições de esquemas incluídas e importadas. No caso do esquema TimeCard , há um total de 10 complexTypes globais (denominados) e mais 23 complexTypes locais (anônimos), juntamente com 8 simpleTypes de enumeração. O modelo de dados padrão gerado consiste em 15 classes de nível superior e 23 classes internas, apenas poucas menos do que o número que seria esperado de ver com base nas contagens de componentes de esquema. Você verá, posteriormente, algumas maneiras de usar customizações para simplificar ainda mais o modelo de dados em casos em que nem todos os componentes do esquema são necessários.



Voltar para parte superior


<xs:choice> - Manipulação

A Listagem 8 mostra como CodeGen manipula uma opção entre dois elementos na definição TimeCardType complexType . CodeGen usa, por padrão, uma variável de seleção para controlar qual opção está atualmente ativa. Os métodos configurados para valores incluídos na opção permitem escrever um novo valor para a seleção atual, mas impedem a alteração direta da seleção (emitindo uma IllegalStateException se você tentar). Para alterar a seleção atual quando tiver sido configurada, primeiro, você precisa chamar um método clear (aqui clearReportedResourceSelect()) que reconfigura o estado da seleção.


Listagem 8. Amostra de Código Gerado por HR-XML TimeCard
/**
 * Fragmentos do esquema para essa classe:
 * <pre>
 * <xs:complexType xmlns:ns="http://ns.hr-xml.org/2007-04-15"
 *    xmlns:ns1="http://www.w3.org/XML/1998/namespace"
 *    xmlns:xs="http://www.w3.org/2001/XMLSchema" name="TimeCardType">
 *   <xs:sequence>
 *     <xs:element type="ns:EntityIdType" name="Id" minOccurs="0"/>
 *     <xs:element name="ReportedResource">
 *       <xs:complexType>
 *         <xs:choice>
 *           <xs:element type="ns:TimeCardPersonType" name="Person"/>
 *           <xs:element name="Resource">
 *             <!-- Reference to inner class Resource -->
 *           </xs:element>
 *         </xs:choice>
 *       </xs:complexType>
 *     </xs:element>
 *     ...
 */
public class TimeCardType
{
    private EntityIdType id;
    private int reportedResourceSelect = -1;
    private final int REPORTED_RESOURCE_PERSON_CHOICE = 0;
    private final int RESOURCE_CHOICE = 1;
    private TimeCardPersonType reportedResourcePerson;
    private Resource resource;
    ...
    private void setReportedResourceSelect(int choice) {
      if (reportedResourceSelect == -1) {
          reportedResourceSelect = choice;
      } else if (reportedResourceSelect != choice) {
          throw new IllegalStateException(
            "Need to call clearReportedResourceSelect() before changing existing choice");
        }
    }

    /**
     * Limpar a seleção da opção.
     */
    public void clearReportedResourceSelect() {
        reportedResourceSelect = -1;
    }

    /**
     * Verifique se ReportedResourcePerson é a seleção atual para a opção.
     *
     * @return <code>true</code> if selection, <code>false</code> if not
     */
    public boolean ifReportedResourcePerson() {
        return reportedResourceSelect == REPORTED_RESOURCE_PERSON_CHOICE;
    }

    /**
     * Obtenha o valor do elemento 'Person'.
     *
     * @return value
     */
    public TimeCardPersonType getReportedResourcePerson() {
        return reportedResourcePerson;
    }

    /**
     * Configure o valor do elemento 'Person'.
     *
     * @param reportedResourcePerson
     */
    public void setReportedResourcePerson(
            TimeCardPersonType reportedResourcePerson) {
        setReportedResourceSelect(REPORTED_RESOURCE_PERSON_CHOICE);
        this.reportedResourcePerson = reportedResourcePerson;
    }

    /**
     * Verifique se Resource é a seleção atual para a opção.
     *
     * @return <code>true</code> if selection, <code>false</code> if not
     */
    public boolean ifResource() {
        return reportedResourceSelect == RESOURCE_CHOICE;
    }

    /**
     * Obtenha o valor do elemento 'Resource'.
     *
     * @return value
     */
    public Resource getResource() {
        return resource;
    }

    /**
     * Configure o valor do elemento 'Resource'.
     *
     * @param resource
     */
    public void setResource(Resource resource) {
        setReportedResourceSelect(RESOURCE_CHOICE);
        this.resource = resource;
    }

Para a maioria dos aplicativos, esse tipo de manipulação de opção funciona bem, evitando que o usuário tente configurar mais de uma alternativa em uma opção. As customizações podem ser usadas para modificar a manipulação de opção padrão, no entanto, portanto, se você não gostar dessa forma de manipulação de opção. pode alterá-la facilmente. O atributo choice-check controla como o estado de seleção de um <xsd:choice> é verificado no código gerado. O valor choice-check="disable" desativa toda a verificação e não controla um estado de seleção, deixando por conta do usuário configurar um, somente um, valor para cada opção. choice-check="checkset" corresponde à manipulação padrão mostrada na Listagem 8, onde somente os métodos configurados verificam uma configuração atual e emitem uma exceção. choice-check="checkboth" também verifica o estado da seleção quando um método get é chamado, emitindo uma exceção se o método get não corresponder ao estado de seleção atual. Por fim, choice-check="override" altera a manipulação padrão sempre para alterar o estado atual quando qualquer valor da opção é configurado, em vez de emitir uma exceção quando um estado diferente tiver sido configurado anteriormente.

O atributo de customização choice-exposed funciona em combinação com as configurações de choice-check , que controlam um estado de seleção atual. Um valor igual a choice-exposed="false" mantém as constantes do estado de seleção, o valor da variável de estado e o método de alteração de estado todos privados, correspondendo à geração de código padrão mostrada na Listagem 8. choice-exposed="true" tornas todos eles acessíveis publicamente, incluindo um método get para a variável de estado. Isso permite usar uma instrução Java switch facilmente para executar código diferente, dependendo do estado atual, no lugar de diversas instruções if .

Ambos os atributos podem ser usados em qualquer nível de customização, permitindo que você configure o comportamento de todo o código gerado na customização mais externa facilmente enquanto ainda retém a capacidade de fazer algo diferente caso-a-caso.



Voltar para parte superior


<xs:any> e mixed="true" - Manipulação

Como muitos esquemas corporativos, os esquemas HR-XML usam os componentes de esquema <xs:any> para criarem pontos de extensão para dados que podem ser definidos por usuários de forma independente do esquema original. CodeGen, por padrão, trata de componentes de esquema <xs:any> usando um objeto org.w3c.dom.Element (ou lista de Element, se o valor de maxOccurs em <xs:any> for maior que 1). O objeto Element pode ser usado para representar qualquer elementos XML arbitrário (incluindo todos os atributos, declarações de namespace e conteúdo), de forma que fornece toda a flexibilidade necessária para trabalhar com qualquer documento que corresponda à definição do esquema.

A Listagem 9 mostra o código gerado que corresponde a um componente <xs:any> na amostra do esquema da Listagem 7 . Como <xs:any> usa maxOccurs="unbounded", o código gerado usa uma lista de Elements.


Listagem 9. Amostra de Código Gerado por <xs:any>
/**
 * ...
 * Fragmentos do esquema para essa classe:
 * <pre>
 * <xs:complexType xmlns:xs="http://www.w3.org/2001/XMLSchema" mixed="true"
 *    name="AdditionalDataType">
 *   <xs:sequence>
 *     <xs:any minOccurs="0" maxOccurs="unbounded" processContents="strict"
 *        namespace="##any"/>
 *   </xs:sequence>
 *   <xs:attribute type="xs:string" name="type"/>
 * </xs:complexType>
 * </pre>
 */
public class AdditionalDataType
{
    private List<Element> anyList = new ArrayList<Element>();
    private String type;

    /**
     * Obtenha a lista de itens da sequência.
     *
     * @return list
     */
    public List<Element> getAny() {
        return anyList;
    }

    /**
     * Configure a lista de itens da sequência.
     *
     * @param list
     */
    public void setAny(List<Element> list) {
        anyList = list;
    }
   ...
}

Alguns aspectos da definição de esquema na Listagem 9 são ignorados ou manipulados somente parcialmente por CodeGen. Primeiro, a definição englobadora <xs:complexType> especifica mixed="true", o que significa que o dados de caractere podem ser combinados com os elementos representados pela partícula <xs:any> . O modelo de dados gerado por CodeGen não possui local para manter esse conteúdo de dados de caractere, portanto, será simplesmente descartado quando um documento for desserializado. Segundo, <xs:any> usa processContents="strict", o que significa que quaisquer elementos presentes nos documentos da instância precisam ter suas próprias definições de esquema. CodeGen ignora esse atributo, apesar de ser possível obter um efeito semelhante usando uma forma diferente de manipulação de <xs:any> (discutida abaixo). CodeGen também ignora restrições de namespace <xs:any> . Listagem 9 usa namespace="##any", o que significa que os elementos que correspondem a <xs:any> não têm restrição de namespace, mas se o valor em vez disso fosse namespace="##other", por exempli, o resultado seria o mesmo.

É possível usar o atributo de customização any-handling em qualquer nível de customizações para selecionar outras maneiras de manipular <xs:any>. O valor any-handling="discard" simplesmente ignora <xs:any> no modelo de dados gerado e descarta quaisquer elementos correspondentes a <xs:any> quando ocorrer a desserialização. any-handling="dom" corresponde à manipulação padrão, usando org.w3c.dom.Element para representar um elemento que corresponda a <xs:any>. Por fim, any-handling="mapped" gera código que requer ma definição de esquema global para cada elemento que corresponder a <xs:any> (correspondendo em geral à condição de esquema processContents="strict" ). Nesse último caso, o modelo de dados usa java.lang.Object para representar um elemento, com o tipo de tempo de execução real do objeto correspondendo à definição de esquema global.



Voltar para parte superior


<xs:simpleType> - Manipulação

Como a maioria das formas de geração de código a partir de esquema, CodeGen ignora ou trata apenas parcialmente de muitos aspectos de definições <xs:simpleType> . <xs:simpleType> possui restrições, o que é um exemplo desse suporte limitado. Das muitas variedades de restrições de simpleType definidas pelo esquema (que incluem restrições de comprimento, intervalos de valores e até mesmo padrões de expressão regular), somente as restrições <xs:enumeration> são atualmente impingidas no modelo de dados gerado.

<xs:simpleType> <union>s também são atualmente ignorados por CodeGen. A Listagem 10 mostra o código gerado que corresponde a uma referência <xs:union> , juntamente com os fragmentos de esquema originais que correspondem ao código (na parte inferior da listagem). Você pode ver na Listagem 10 que cada uma das referências a um tipo de união (incluindo o tipo TimeCardDuration mostrado na listagem e o AnyDateTimeType) é representada por um valor String simples no código gerado.


Listagem 10. Amostra de Código Gerado por <xs:union> e Esquema Original
/**
     * Fragmentos do esquema para essa classe:
     * <pre>
     * <xs:element xmlns:ns="http://ns.hr-xml.org/2007-04-15"
     *    xmlns:xs="http://www.w3.org/2001/XMLSchema" name="TimeInterval">
     *   <xs:complexType>
     *     <xs:sequence>
     *       <xs:element type="ns:EntityIdType" name="Id" minOccurs="0"/>
     *       <xs:element type="xs:string" name="StartDateTime"/>
     *       <xs:choice>
     *         <xs:sequence>
     *           <xs:element type="xs:string" name="EndDateTime"/>
     *           <xs:element type="xs:string" name="Duration" minOccurs="0"/>
     *         </xs:sequence>
     *         <xs:element type="xs:string" name="Duration"/>
     *       </xs:choice>
     *       ...
     * </pre>
     */
    public static class TimeInterval
    {
        private EntityIdType id;
        private String startDateTime;
        private int choiceSelect = -1;
        private final int END_DATE_TIME_CHOICE = 0;
        private final int DURATION_CHOICE = 1;
        private String endDateTime;
        private String duration;
        private String duration1;
        ...

    ...
    <xsd:element name="TimeInterval">
      <xsd:complexType>
        <xsd:sequence>
          <xsd:element name="Id" type="EntityIdType" minOccurs="0"/>
          <xsd:element name="StartDateTime" type="AnyDateTimeType"/>
          <xsd:choice>
            <xsd:sequence>
              <xsd:element name="EndDateTime" type="AnyDateTimeType"/>
              <xsd:element name="Duration" type="TimeCardDuration" minOccurs="0"/>
            </xsd:sequence>
            <xsd:element name="Duration" type="TimeCardDuration"/>
          </xsd:choice>
          ...
<xsd:simpleType name="TimeCardDuration">
  <xsd:union memberTypes="xsd:duration xsd:decimal"/>
</xsd:simpleType>



Voltar para parte superior


Modificações de Esquema

Se você comparar os fragmentos de esquema integrados no Javadoc na parte superior da Listagem 10 aos fragmentos de esquema reais na parte inferior da listagem, você verá que as referências union simpleType no esquema original foram substituídas pelas referências xs:string na versão Javadoc. Isso é deliberadamente e é representante de diversos tipos de transformações em estruturas de esquema executadas por CodeGen. Algumas transformações, como eliminação de <union> simpleTypes e de facetas de restrição de simpleType diferente de <xs:enumeration>, são codificadas permanentemente na operação de CodeGen. Outras transformações são controladas por customizações. De qualquer forma, os fragmentos de esquema incluídos em Javadocs sempre mostram o esquema transformado, pois isso é o que é realmente usado para gerar o código.

Você verá mais tipos de transformações controladas por customizações nas seções a seguir do tutorial.



Voltar para parte superior



Ir para a página anteriorPágina 6 de 14 Ir para a próxima página