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

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

A geração de código a partir de definições de esquema XML é amplamente usada para todos os tipos de troca de dados XML, incluindo serviços da Web. A maioria das ferramentas de ligação de dados estruturam de forma rígida código gerado baseado no esquema — até mesmo aspectos do esquema que podem ser irrelevantes para seu aplicativo. Neste tutorial, o seguindo de uma série em duas partes, aprenda como JiBX 1.2 gera código mais limpo realizando uma tarefa melhor de interpretação do esquema e eliminação do grupo de classes desnecessárias. Você também verá como é possível customizar o código gerado para se adequar melhor às suas necessidades, incluindo customizações que eliminam facilmente os componentes desnecessários do esquema.

Dennis Sosnoski, President, Sosnoski Software Solutions, Inc.

Photo of Dennis SosnoskiDennis Sosnoski é um consultor e facilitador de treinamento especializado em serviços SOA e da Web baseados em Java. Sua experiência em desenvolvimento de software profissional se estende por mais de 30 anos, sendo que a última década focou tecnologias XML e Java do lado do servidor. Dennis é o desenvolvedor líder da ferramenta de ligação de dados XML JiBX de software livre, assim como um dos compromissados com a estrutura de serviços da Web Apache Axis2. Ele também foi um dos membros do grupos de especialistas para as especificações JAX-WS 2.0 e JAXB 2.0. Consulte seu Web site para obter informações sobre seus serviços de treinamento e consultoria.



03/Mar/2009

Antes de Iniciar

Sobre este Tutorial

JiBX é uma ferramenta para fazer a ligação de dados XML a objetos Java™ . A ligação de dados de JiBX é conhecida há muito tempo como a abordagem mais rápida e mais flexível para fazer a ligação de código Java com XML. Mas a complexidade de suas definições de ligação e seu suporte limitado para definições de esquema XML cada vez mais usadas frustraram usuários em alguns momentos. Felizmente, a versão 1.2 de JiBX percorreu um longo caminho para eliminar esses problemas. Neste tutorial, você aprenderá sobre como usar os novos recursos de JiBX 1.2 para gerar código Java a partir de definições de esquema XML e ler e gravar documentos XML que correspondem às definições de esquema geradas — tudo sem precisar entrar nos detalhes de definições de ligação de JiBX. A Parte 1 cobre o lado inverso de iniciar com código Java e gerar definições de esquema XML.

Objetivos

Este tutorial o guia através do processo de usar JiBX para gerar código Java a partir de definições de esquema XML. Primeiro, você irá aprender como trabalhar com um esquema simples e gerar um modelo de dados Java padrão que corresponde a esse esquema, em seguida, usar esse modelo de dados para ler e escrever os documentos XML. Em seguida, você verá como as customizações podem ser usadas para modificar a geração de códigos para que melhor se ajuste às suas necessidades. Por fim, você irá para um exemplo de esquema mais complexo padrão de mercado e explorará o poder de customizações para simplificar o modelo de dados gerado para esse esquema e melhorar a usabilidade. Após ler este tutorial e trabalhar com os exemplos fornecidos, você poderá usar JiBX para gerar modelos de dados Java customizados para seus próprios esquemas.

Pré-requisitos

Para entender este tutorial, você deve ter pelo menos um conhecimento básico de código Java e XML. Você não precisa de um entendimento detalhado de definições de esquema XML, mas alguma familiaridade com o esquema ajudará a entender os exemplos de uma melhor forma.

Requisitos do Sistema

Para executar os exemplos, é necessário instalar:

As instruções de download e instalação de JiBX estão incluídas no tutorial.


Introduzindo JiBX

JiBX é uma das muitas ferramentas usadas para converter entre estruturas de dados Java e documentos XML (consulte Recursos). O que diferencia JiBX dos outros são os recursos de desempenho e flexibilidade. O desempenho de JiBX tem uma avaliação alta de forma consistente, superando a de outras ferramentas comuns (como JAXB 2.0) por um ou dois fatores ou mais. JiBX também é mais flexível do que a maioria das outras ferramentas Java-XML, usando definições de ligação para desacoplar a estrutura Java da representação de XML, de forma que cada uma possa ser alterada independentemente da outra.

Com o release 1.2, JiBX incluir recursos importantes que suportam as definições de esquema XML. Você pode usar ferramentas incluídas no release de JiBX para gerar uma definição de esquema que corresponda a seu código Java ou para gerar código Java que corresponda a sua definição de esquema. De qualquer forma, você também obtém uma definição de ligação que permite que você use JiBX para converter entre o código Java e os documentos XML que correspondem à definição de esquema. Neste tutorial, você verá como aplicar o segundo tipo de geração: indo da definição de esquema para o código Java.

Instalando JiBX

Será necessário instalar JiBX antes de prosseguir com este tutorial. Download do ZIP de distribuição 1.2.x mais recente expanda-o em um local conveniente em seu sistema. Você acabará com um diretório denominado jibx, que contém todos os JARs, documentação, exemplos e até mesmo código de origem de JiBX.

Instalando o Código do Tutorial

Agora, faça download do código de amostrado tutorial, também fornecido como um arquivo ZIP. A maneira mais fácil de instalá-lo em seu sistema é expandindo o ZIP para o diretório-raiz de sua distribuição JiBX (ou, no Windows®, copie o diretório dwcode2 de dentro do arquivo ZIP para o diretório-raiz de sua distribuição JiBX). Isso deve criar um subdiretório dwcode2 no diretório jibx, com os arquivos de exemplo (incluindo build.xml, custom.xml e outros) dentro desse diretório dwcode2.

O código de amostra inclui um arquivo de construção Ant para automatizar a execução das ferramentas JiBX e tratar das outras etapas envolvidas nos exemplos. Se você instalar o código de amostra diretamente no diretório de instalação JiBX, a construção pode acessar os JARs de JiBX sem qualquer configuração adicional. Se você instalar o código de amostra em outro local, você ainda pode usar a construção Ant. Nesse caso, você precisa editar somente o arquivo build.properties no diretório de código de amostra e alterar o valor da propriedade jibx-home para o caminho de sua instalação de JiBX.


Gerando Ligação Padrão e Código a partir do Esquema

É fácil gerar uma definição de ligação JiBX e o código Java correspondente, a partir de uma definição de esquema XML. Você aprenderá como nesta seção.

Introduzindo o Esquema de Exemplo Simples

Como um exemplo simples, iniciarei com um dos esquemas gerados na Parte 1. A Listagem 1 mostra uma versão abreviada desse esquema, que pretende representar um pedido de uma loja on-line. O esquema integral é fornecido como starter.xsd no diretório dwcode2 do código de amostra.

Listagem 1. Primeiro Esquema de Exemplo
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://jibx.org/starter" elementFormDefault="qualified"
    targetNamespace="http://jibx.org/starter">
  <xs:simpleType name="shipping">
    <xs:annotation>
      <xs:documentation>Supported shipment methods. The "INTERNATIONAL" shipment
      methods can only be used for orders with shipping addresses outside the U.S., and
      one of these methods is required in this case.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:string">
      <xs:enumeration value="STANDARD_MAIL"/>
      <xs:enumeration value="PRIORITY_MAIL"/>
      <xs:enumeration value="INTERNATIONAL_MAIL"/>
      ...
    </xs:restriction>
  </xs:simpleType>
  <xs:complexType name="item">
    <xs:annotation>
      <xs:documentation>Order line item information.</xs:documentation>
    </xs:annotation>
    <xs:sequence/>
    <xs:attribute type="xs:string" use="required" name="id">
      <xs:annotation>
        <xs:documentation>Stock identifier. This is expected to be 12 characters in
        length, with two leading alpha characters followed by ten decimal digits.
        </xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute type="xs:int" use="required" name="quantity">
      <xs:annotation>
        <xs:documentation>Number of units ordered.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute type="xs:float" use="required" name="price">
      <xs:annotation>
        <xs:documentation>Price per unit.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
  </xs:complexType>
  <xs:complexType name="address">
    <xs:annotation>
      <xs:documentation>Address information.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element type="xs:string" name="street1">
        <xs:annotation>
          <xs:documentation>First line of street information (required).
          </xs:documentation>
        </xs:annotation>
      </xs:element>
      ...
    </xs:sequence>
    <xs:attribute type="xs:string" name="state">
      <xs:annotation>
        <xs:documentation>State abbreviation (required for the U.S. and Canada,
        optional otherwise).</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute type="xs:string" name="postCode">
      <xs:annotation>
        <xs:documentation>Postal code (required for the U.S. and Canada, optional
        otherwise).</xs:documentation>
      </xs:annotation>
    </xs:attribute>
  </xs:complexType>
  <xs:complexType name="customer">
    <xs:annotation>
      <xs:documentation>Customer information.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element type="xs:long" name="customerNumber"/>
      ...
    </xs:sequence>
  </xs:complexType>
  <xs:element type="tns:order" name="order"/>
  <xs:complexType name="order">
    <xs:annotation>
      <xs:documentation>Order information.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element type="xs:long" name="orderNumber"/>
      <xs:element type="tns:customer" name="customer"/>
      <xs:element type="tns:address" name="billTo">
        <xs:annotation>
          <xs:documentation>Billing address information.</xs:documentation>
        </xs:annotation>
      </xs:element>
      ...
      <xs:element type="tns:item" name="item" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute type="xs:date" use="required" name="orderDate">
      <xs:annotation>
        <xs:documentation>Date order was placed with server.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    ...
  </xs:complexType>
</xs:schema>

Gerando a Ligação e o Código Padrão

Para gerar uma ligação de JiBX e classes Java a partir de um esquema XML, você simplesmente precisa executar a ferramenta org.jibx.schema.codegen.CodeGen incluída no jibx-tools.jar a partir da distribuição JiBX. Você pode executar a ferramenta diretamente a partir da linha de comandos ou indiretamente através de uma ferramenta de construção, como Apache Ant.

O download do tutorial inclui um script Ant build.xml com o destino codegen para executar a geração.

Para testar isso, abra um console no diretório dwcode2 do download instalado e digite ant codegen. Se você tiver Ant instalado em seu sistema e tiver instalado o código de download de acordo com as instruções, você deve ver saída semelhante à mostrada na Figura 1:

Figura 1. Usando a Construção Ant
Esquema para geração da construção de saída Ant

Você também pode executar CodeGen diretamente a partir do console. Para fazer isso, você precisa:

  1. Incluir o jibx-tools.jar em seu caminho de classe Java.
  2. Especificar org.jibx.schema.codegen.CodeGen como a classe a ser executada.
  3. Listar as definições de esquemas a serem geradas.

O destino codegen Ant fornecido usa dois parâmetros adicionais para indicar a CodeGen para usar o diretório gen/src como a raiz da estrutura do pacote do modelo de dados gerado e para limpar todos os arquivos existentes desse diretório antes de executar a geração. Segue a linha de comando Java para duplicar o destino codegen Ante a partir de um console no diretório dwcode2 (supondo que você tenha seguido as instruções de instalação recomendadas):

java -cp ../lib/jibx-tools.jar org.jibx.schema.codegen.CodeGen -t gen/src -w starter.xsd

No Windows, o comando é:

java -cp ..\lib\jibx-tools.jar org.jibx.schema.codegen.CodeGen -t gen\src -w starter.xsd

Você pode passar muitas outras opções para CodeGen a partir da linha de comando. Você dará uma olhada neles posteriormente no tutorial. Agora, vamos dar uma olhada no código Java gerado.

Artefatos Gerados

O código gerado consiste em cinco classes, correspondentes às definições tipo globais no esquema da Listagem 1 . A Listagem 2 mostra algumas amostras do código gerado, com trechos da classe org.jibx.starter.Order e toda a classe org.jibx.starter.Shipping :

Listagem 2. Código Gerado
/**
 * Informações de pedido.
 *
 * Fragmentos do esquema para essa classe:
 * <pre>
 * <xs:complexType xmlns:ns="http://jibx.org/starter"
   xmlns:xs="http://www.w3.org/2001/XMLSchema" name="order">
 *   <xs:sequence>
 *     <xs:element type="xs:long" name="orderNumber"/>
 *     <xs:element type="ns:customer" name="customer"/>
 *     <xs:element type="ns:address" name="billTo"/>
 *     <xs:element type="ns:shipping" name="shipping"/>
 *     <xs:element type="ns:address" name="shipTo" minOccurs="0"/>
 *     <xs:element type="ns:item" name="item" minOccurs="0" maxOccurs="unbounded"/>
 *   </xs:sequence>
 *   <xs:attribute type="xs:date" use="required" name="orderDate"/>
 *   <xs:attribute type="xs:date" name="shipDate"/>
 *   <xs:attribute type="xs:float" name="total"/>
 * </xs:complexType>
 * </pre>
 */
public class Order
{
    private long orderNumber;
    private Customer customer;
    private Address billTo;
    private Shipping shipping;
    private Address shipTo;
    private List<Item> itemList = new ArrayList<Item>();
    private Date orderDate;
    private Date shipDate;
    private Float total;
    ...
    /**
     * Obtenha o valor do elemento 'shipTo'. Informações do endereço de remessa. Se estiver faltando, o
     * endereço para cobrança também é usado como o endereço de remessa.
     */
    public Address getShipTo() {
        return shipTo;
    }
    /**
     * Configure o valor do elemento 'shipTo'. Informações do endereço de remessa. Se estiver faltando, o
     * endereço para cobrança também é usado como o endereço de remessa.
     */
    public void setShipTo(Address shipTo) {
        this.shipTo = shipTo;
    }
    /**
     * Obtenha a lista dos itens do elemento 'item'.
     */
    public List<Item> getItems() {
        return itemList;
    }
    /**
     * Configure a lista de itens do elemento 'item'.
     */
    public void setItems(List<Item> list) {
        itemList = list;
    }
    ...
}
/**
 * Métodos de envio suportados. Os métodos de envio "INTERNATIONAL" podem ser usados somente para
 pedidos com endereços de remessa fora dos EUA e um desses métodos é necessário
 nesse caso.
 *
 * Schema fragment(s) for this class:
 * <pre>
 * <xs:simpleType xmlns:xs="http://www.w3.org/2001/XMLSchema" name="shipping">
 *   <xs:restriction base="xs:string">
 *     <xs:enumeration value="STANDARD_MAIL"/>
 *     <xs:enumeration value="PRIORITY_MAIL"/>
 *     <xs:enumeration value="INTERNATIONAL_MAIL"/>
 *     <xs:enumeration value="DOMESTIC_EXPRESS"/>
 *     <xs:enumeration value="INTERNATIONAL_EXPRESS"/>
 *   </xs:restriction>
 * </xs:simpleType>
 * </pre>
 */
public enum Shipping {
    STANDARD_MAIL, PRIORITY_MAIL, INTERNATIONAL_MAIL, DOMESTIC_EXPRESS,
    INTERNATIONAL_EXPRESS
}

Como pode ver na Listagem 2, CodeGen converte a documentação do esquema para Javadocs no código gerado (mostrado aqui como os comentários de orientação em cada Javadoc de classe e como parte dos comentários para os métodos getShipTo() e setShipTo() ). CodeGen, por padrão, também incorpora as definições reais de esquema nos Javadocs de classe e, para os métodos de acesso de propriedade get/set, descreve o componente do esquema que corresponde à propriedade.

Para valores repetidos, como o elemento do item de repetição na definição complexType do pedido da Listagem 1, CodeGen gera uma lista do tipo Java 5 por padrão. Para enumerações de restrições simpleType , como o tipo de remessa na Listagem 1, CodeGen gera um tipo de enumeração Java 5 por padrão. O código gerado para ambas as instâncias é mostrado na Listagem 2.

Ligação JiBX Gerada

Além do código gerado, CodeGen também produz uma definição de ligação JiBX (como o arquivo binding.xml, neste caso), que indica ao compilador de ligação JiBX como fazer a conversão entre as classes Java e XML. As definições de ligação contêm detalhes integrais das conversões a serem feitas por JiBX, portanto, são necessariamente complexas. Felizmente, não é necessário entender a definição de ligação para trabalhar com JiBX usando ligação e geração de código CodeGen, de forma que este tutorial não cobre os detalhes.


Trabalhando com Documentos XML

Nesta seção, você aprenderá sobre como executar o compilador de ligação JiBX e como usar JiBX no tempo de execução para trabalhar com documentos XML.

Executando o Compilador de Ligação JiBX

Para usar a definição de ligação gerada no trabalho com documentos XML, primeiro, você precisa executar o compilador de ligação JiBX. O compilador de ligação inclui bytecode em seus arquivos de classe compilados que realmente implementa as conversões para e a partir de XML, conforme especificado pela definição de ligação. Você deve executar o compilador de ligação toda vez que recompilar suas classes Java ou modificar a definição de ligação, portanto, geralmente é melhor incluir a etapa do compilador de ligação como parte do processo de construção padrão de seu projeto.

O compilador de ligação está incluído na distribuição JiBX como parte do jibx-bind.jar. A documentação de JiBX fornece detalhes integrais sobre as diferentes maneiras de executar o compilador de ligação, inclusive como você pode chamá-lo ao executar ser aplicativo em vez de como parte da construção. JiBX também fornece plug-ins para o Eclipse e o IntelliJ IDEA que executam o compilador de ligação automaticamente quando você está trabalhando nesses IDEs.

Para os propósitos deste tutorial, vamos manter as coisas simples e simplesmente executar o compilador de ligação através de Ant. O destino compile debuild.xml prepara para a ligação compilando o código gerado e um programa de teste fornecido, enquanto o destino bind realmente executa o compilador de ligação. A Figura 2 mostra a saída que você deve ver ao executar esses destinos, supondo que você já tenha executado os destinos codegen . (Você também pode executar todos os três destinos em seqüência, listando-os em ordem na linha de comando: ant codegen compile bind.)

Figura 2. Tarefas de Construção Ant compile e bind
Ant build output for compile and binding compiler

Usando JiBX no Tempo de Execução

A Listagem 3 mostra um documento de teste simples que corresponde ao esquema, incluído no download do código do tutorial como starter.xml:

Listagem 3. Documento de Teste para Esquema de Pedido
<order orderDate="2008-10-18" shipDate="2008-10-22" xmlns="http://jibx.org/starter">
  <orderNumber>12345678</orderNumber>
  <customer>
    <customerNumber>5678</customerNumber>
    <firstName>John</firstName>
    <lastName>Smith</lastName>
  </customer>
  <billTo state="WA" postCode="98059">
    <street1>12345 Happy Lane</street1>
    <city>Plunk</city>
    <country>USA</country>
  </billTo>
  <shipping>PRIORITY_MAIL</shipping>
  <shipTo state="WA" postCode="98034">
    <street1>333 River Avenue</street1>
    <city>Kirkland</city>
  </shipTo>
  <item quantity="1" price="5.99" id="FA9498349851"/>
  <item quantity="2" price="9.50" id="GC1234905049"/>
  <item quantity="1" price="8.95" id="AX9300048820"/>
</order>

O pacote de download também inclui um programa de teste simples, mostrado aqui como a Listage, 4, que demonstra o uso de JiBX para desserializar e serializar documentos. Serializar é o processo de gerar uma representação XML para um objeto em memória, potencialmente incluindo objetos vinculados a partir do objeto original. Desserializar é o processo inverso de serializar, construir um objeto (e potencialmente um gráfico de objetos vinculados) em memória de uma representação XML. O destino Ant run executa esse programa de teste, usando o documento Listagem 3 como entrada e gravando a cópia serializada do documento em um arquivo denominado out.xml.

Listagem 4. Programa de Teste

Clique aqui para ver lista de códigos

Listagem 4. Programa de Teste

public class Test
{
    /**
     * Desserializar o documento de amostra de um arquivo, computar e configurar total do pedido, em seguida,
     * serializá-lo novamente em outro arquivo.
     *
     * @param args
     */
    public static void main(String[] args) {
        if (args.length < 2) {
            System.out.println("Usage: java -cp ... " +
                "org.jibx.starter.Test in-file out-file");
            System.exit(0);
        }
        try {

            // desserializar as informações do cliente do arquivo
            IBindingFactory bfact = BindingDirectory.getFactory(Order.class);
            IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
            FileInputStream in = new FileInputStream(args[0]);
            Order order = (Order)uctx.unmarshalDocument(in, null);

            // compute o valor total do pedido
            float total = 0.0f;
            for (Iterator<Item> iter = order.getItems().iterator(); iter.hasNext();) {
                Item item = iter.next();
                total += item.getPrice() * item.getQuantity();
            }
            order.setTotal(new Float(total));

            // desserializar objeto de volta para o arquivo (com bom deslocamento, como UTF-8)
            IMarshallingContext mctx = bfact.createMarshallingContext();
            mctx.setIndent(2);
            FileOutputStream out = new FileOutputStream(args[1]);
            mctx.setOutput(out, null);
            mctx.marshalDocument(order);
            System.out.println("Processed order with " +  order.getItems().size() +
                " items and total value " + total);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.exit(1);
        } catch (JiBXException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
}

A Figura 3 mostra a saída que você deve ver ao executar o destino run :

Figura 3. Tarefa de Construção Ant run
Ant build output from running test program

Esse é o mesmo programa de teste que o usado na Parte 1 e está sujeito às mesmas limitações discutidas nesse tutorial. Exatamente como na Parte 1, o arquivo out.xml possui a saída gerada serializando de volta os dados do pedido obtidos desserializando o documento original.


Introduzindo Cistomizações de CodeGen

Nesta seção, você aprenderá o básico da customização de CodeGen para controlar a estrutura do código gerado a partir de um esquema simples.

Um Exemplo de Customização Simples

CodeGen suporta customizações extensivas para muitos aspectos de geração de código e de ligação. O conjunto de customizações a serem aplicadas é passado para CodeGen como um documento XML, com elementos aninhados relacionados a esquemas ou componentes de esquemas. A Listagem 5 fornece um exemplo simples:

Listagem 5. Exemplo de Customizações Simples
<schema prefer-inline="true" show-schema="false" enumeration-type="simple"
  generate-all="false" includes="order item"/>

A customização da Listagem 5 consiste em um único elemento de esquema sem espaço de nome com diversos atributos diferentes que fornecem as customizações específicas a serem aplicadas. (Até o momento, você está trabalhando somente com uma única definição de esquema, portanto, essa forma de customização muito simples pode ser usada. Posteriormente neste tutorial, você verá exemplos de customizações para trabalhar com diversos esquemas.) O primeiro atributo de customização — prefer-inline="true" — indica CodeGen para definições de esquema sequencial que são referidas somente uma vez, em vez de usar o comportamento padrão de mantê-las como classes separadas. O segundo atributo — show-schema="false" — bloqueia a integração das definições de esquema nos Javadocs de classe. enumeration-type="simple" gera enumerações typesafe simples,em vez de enumerações Java 5.

O par final de atributos funciona junto. CodeGen, por padrão, gera uma classe de nível superior separada para cada definição de tipo global nos esquemas especificados como entrada, juntamente com um mapeamento abstrato correspondente na ligação JiBX gerada para cada complexType. O atributo generate-all="false" altera esse comportamento padrão, gerando, explicitamente, somente os complexTypes incluídos especificamente ou referidos a partir de um tipo incluído. O atributo includes="order item" fornece, então, os nomes a serem gerados, que foram escolhidos para manter a compatibilidade com o programa de teste — o elemento <order> é necessário, pois esse é o elemento-raiz dos documentos da instância e o item complexType é necessário, pois o programa de teste espera localizar uma classe separada de nível superior para esse tipo quando soma o valor total do pedido.

Você pode testar essas customizações usando a tarefa Ant custgen em vez de a tarefa codegen (ou simplesmente usar a tarefa full , que executa a sequência completa de destinos clean custgen compile bind run). A Listagem 6 mostra trechos do código gerado, que você pode comparar ao código gerado padrão mostrado na Listagem 2. As grandes diferenças (além dos Javadocs de classe simplificados) são que a classe Customer agora é sequencial e a classe Shipping agora é uma classe interna que usa uma classe de enumeração typesafe customizada.

Listagem 6. Código Gerado com Customização
/**
 * Informações do pedido.
 */
public class Order
{
    private long orderNumber;
    private long customerCustomerNumber;
    private String customerFirstName;
    private String customerLastName;
    private Address billTo;
    private Shipping shipping;
    private Address shipTo;
    private List<Item> itemList = new ArrayList<Item>();
    private Date orderDate;
    private Date shipDate;
    private Float total;
    ...
    /**
     * Métodos de remessa suportados. Os métodos de envio "INTERNATIONAL" podem ser usados somente para
 pedidos com endereços de remessa fora dos EUA e um desses métodos é necessário
 nesse caso.
     */
    public static class Shipping
    {
        private final String value;
        public static final Shipping STANDARD_MAIL = new Shipping(
                "STANDARD_MAIL");
        public static final Shipping PRIORITY_MAIL = new Shipping(
                "PRIORITY_MAIL");
        public static final Shipping INTERNATIONAL_MAIL = new Shipping(
                "INTERNATIONAL_MAIL");
        public static final Shipping DOMESTIC_EXPRESS = new Shipping(
                "DOMESTIC_EXPRESS");
        public static final Shipping INTERNATIONAL_EXPRESS = new Shipping(
                "INTERNATIONAL_EXPRESS");
        private static final String[] values = new String[]{"DOMESTIC_EXPRESS",
                "INTERNATIONAL_EXPRESS", "INTERNATIONAL_MAIL", "PRIORITY_MAIL",
                "STANDARD_MAIL"};
        private static final Shipping[] instances = new Shipping[]{
                DOMESTIC_EXPRESS, INTERNATIONAL_EXPRESS, INTERNATIONAL_MAIL,
                PRIORITY_MAIL, STANDARD_MAIL};

        private Shipping(String value) {
            this.value = value;
        }

        public String toString() {
            return value;
        }

        public static Shipping convert(String value) {
            int index = java.util.Arrays.binarySearch(values, value);
            if (index >= 0) {
                return instances[index];
            } else {
                return null;
            }
        }

        public static Shipping fromValue(String text) {
            Shipping value = convert(text);
            if (value == null) {
                throw new IllegalArgumentException("Value \'" + text
                        + "\' is not allowed");
            } else {
                return value;
            }
        }
    }
}

Muitas customizações adicionais estão disponíveis para uso com CodeGen. Você verá alguns exemplos disso posteriormente neste tutorial, mas para ter uma melhor ideia do poder dessas customizações, é necessário seguir para um esquema mais complexo.


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>

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.

<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.

<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.

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

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.


Customizando o Modelo de Dados TimeCard

Um exemplo anterior neste tutorial mostrou algumas customizações simples de CodeGen. Agora que você viu como CodeGen manipula o esquema HR-XML TimeCard com configurações padrão, chegou a hora de dar uma olhada em alguma customizações mais poderosas.

Customizando o Modelo de Dados

O código do modelo de dados gerado por CodeGen usando configurações padrão possui alguns pontos fracos. Uma coisa é que os nomes de tipo de esquema terminam com Type e isso é transportado para os nomes de classe gerados correspondentes, tornando os nomes mais longos do que necessário. O nome do pacote gerado a partir do espaço de nome do esquema, org.hrxml.ns, é razoável, mas seria melhor ter um nome de pacote que indicasse que o modelo de dados é especificamente para documentos TimeCard .

A Listagem 11 mostra outro aspecto estranho das classes de modelo de dados gerado, m que java.math.BigInteger é usado para representar um tipo xs:integer . Essa é a representação mais precisa para xs:integer usando classes Java padrão, mas BigInteger é estanho de usar em comparação oa primitivo simples int ou aos tipos de objetos java.lang.Integer . Infelizmente, os esquemas são frequentemente escritos usando o tipo xs:integer mesmo quando xs:int seria mais apropriado, portanto, desenvolvedores podem ficar emperrados com valores BigInteger no código gerado. Esse é definitivamente o caso aqui, em que os valores reais permitidos para GenderCode são todos de um único dígito (conforme mostrado pelo fragmento de esquema original na parte inferior da listagem).

Listagem 11. Exemplo de Geração de xs:integer
/**
 * Fragmentos do esquema para essa classe:
 * <pre>
 * <xs:element xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:integer"
 *    name="GenderCode"/>
 * </pre>
 */
public class GenderCode
{
    private BigInteger genderCode;

    /**
     * Obtenha o valor do elemento 'GenderCode'.
     *
     * @return value
     */
    public BigInteger getGenderCode() {
        return genderCode;
    }

    /**
     * Configure o valor do elemento 'GenderCode'.
     *
     * @param genderCode
     */
    public void setGenderCode(BigInteger genderCode) {
        this.genderCode = genderCode;
    }
}

  <xsd:simpleType name="GenderCodeType">
    <xsd:annotation>
      <xsd:documentation>Must conform to ISO 5218 - Representation of Human Sexes
         (0 - Not Known; 1 - Male; 2 - Female; 9 - Not specified)</xsd:documentation>
    </xsd:annotation>
    <xsd:restriction base="xsd:integer">
      <xsd:pattern value="[0129]"/>
    </xsd:restriction>
  </xsd:simpleType>

A Listagem 12 mostra uma customização para melhorar esses aspectos do modelo de dados gerado. O atributo package="org.hrxml.timecard" fornece o pacote Java a ser usado para as classes geradas. O atributo type-substitutions="xs:integer xs:int" define as substituições de tipo de esquema a serem aplicadas por CodeGen, neste caso, usando o tipo xs:int sempre que xs:integer for referido pelo esquema. É possível definir diversos pares de substituições apenas incluindo mais nomes de tipos na lista, usando espaços como separadores entre os pares, assim como entre os nomes de tipos de cada par.

O elemento name-converter aninhado configura a manipulação de nomes XML que estão sendo convertidos para nomes Java. Neste caso, o atributo strip-suffixes="Type" indica a CodeGen para remover Type sempre que ocorrer no final de um nome. É possível fornecer diversas alternativas para serem removidas com esse atributo, como uma lista separada por espaços. Você também pode usar um atributo strip-prefixes para remover texto de orientação desnecessário de nomes, juntamente com diversas outras formas de customização. É até mesmo possível substituir a classe de conversão de nome padrão com sua própria implementação, se você quiser fazer algo realmente especial nas conversões de nomes. Para obter os detalhes integrais sobre essas opções name-converter , consulte a documentação de JiBX CodeGen.

Por fim, o elemento class-decorator aninhado inclui um decorador na sequência de geração de códigos. Nesse caso, o decorador é um predefinido fornecido como parte da distribuição CodeGen, que inclui métodos de suporte úteis para valores de coleta. Qualquer decorador de geração de ocódigo configurado é chamado na sequência pelo CodeGen à medida que constrói o código de origem para as classes de modelo de dados e tem a oportunidade de modificar ou incluir no campo, método e construções de classe geradas por CodeGen. Cada uma dessas construções é passada para o decorador como um componente Árvore de Sintaxe Abstrata (AST), usando a implementação do Eclipse AST. Os decoradores fornecidos (incluindo o decorador org.jibx.schema.codegen.extend.CollectionMethodsDecorator usado aqui para incluir métodos e um org.jibx.schema.codegen.extend.SerializableDecorator usado para incluir a interface java.io.Serializable e, como opção, um ID de versão para classes do modelo de dados) fornecem exemplos de trabalhar com o Eclipse AST para estender CodeGen, de forma que o código de origem dessas classes seja um excelente ponto de partida para escrever seus próprios decoradores.

Listagem 12. Exemplo de Customizações de TimeCard
<schema-set xmlns:xs="http://www.w3.org/2001/XMLSchema" package="org.hrxml.timecard"
    type-substitutions="xs:integer xs:int">
  <name-converter strip-suffixes="Type"/>
  <class-decorator class="org.jibx.schema.codegen.extend.CollectionMethodsDecorator"/>
</schema-set>

Você pode testar a customização da Listagem 12 usando o destino Ant custgen1 ou o destino custom1 para executar a sequência completa de geração, compilação, ligação e teste. A Listagem 13 mostra o resultado da aplicação das customizações. O nome da classe TimeCardType foi alterado para apenas TimeCard e, além dos métodos get e set de List , agora há os métodos added size, add, indexed get e clear. Na classe GenderCode , a referência BigInteger foi substituída por um tipo primitivo int simples.

Listagem 13. Modelo de Dados Customizado
/**
 * Fragmentos do esquema para essa classe:
 * <pre>
 * ...
 * </pre>
 */
public class TimeCard
{
    ...
    private List<ReportedTime> reportedTimeList = new ArrayList<ReportedTime>();
    ...
    /**
     * Obtenha a lista de itens do elemento 'ReportedTime'.
     *
     * @return list
     */
    public List<ReportedTime> getReportedTimes() {
        return reportedTimeList;
    }

    /**
     * Configure a lista de itens do elemento 'ReportedTime'.
     *
     * @param list
     */
    public void setReportedTimes(List<ReportedTime> list) {
        reportedTimeList = list;
    }

    /**
     * Obtenha o número de itens do elemento 'ReportedTime'.
     * @return count
     */
    public int sizeReportedTime() {
        return reportedTimeList.size();
    }

    /**
     * Inclua um item do elemento 'ReportedTime'.
     * @param item
     */
    public void addReportedTime(ReportedTime item) {
        reportedTimeList.add(item);
    }

    /**
     * Obtenha o item do elemento 'ReportedTime' por posição.
     * @return item
     * @param index
     */
    public ReportedTime getReportedTime(int index) {
        return reportedTimeList.get(index);
    }

    /**
     * Remova todos os itens do elemento 'ReportedTime'.
     */
    public void clearReportedTime() {
        reportedTimeList.clear();
    }
    ...
}
/**
 * Fragmentos do esquema para essa classe:
 * <pre>
 * &lt;xs:element xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:int"
 *   name="GenderCode"/>
 * </pre>
 */
public class GenderCode
{
    private int genderCode;

    /**
     * Obtenha o valor do elemento 'GenderCode'.
     *
     * @return value
     */
    public int getGenderCode() {
        return genderCode;
    }

    /**
     * Configure o valor do elemento 'GenderCode'.
     *
     * @param genderCode
     */
    public void setGenderCode(int genderCode) {
        this.genderCode = genderCode;
    }
}

Eliminando Definições Não Usadas

No primeiro exemplo de customização, usando o esquema original simples, você viu como controlar as definições de tipo incluídas no modelo de dados gerado usando generate-all="false" para desativar a geração de cada definição global e uma lista includes para forçar a geração de definições específicas. A Listagem 14 mostra uma customização modificada para o esquema TimeCard que inclui esses atributos, com apenas o elemento TimeCard a ser incluído no modelo de dados gerado (juntamente com tudo usado pela representação TimeCard , é claro).

Listagem 14. Customização omente com Componentes TimeCard
<schema-set xmlns:xs="http://www.w3.org/2001/XMLSchema" package="org.hrxml.timecard"
    type-substitutions="xs:integer xs:int" generate-all="false">
  <name-converter strip-suffixes="Type"/>
  <class-decorator class="org.jibx.schema.codegen.extend.CollectionMethodsDecorator"/>
  <schema name="TimeCard.xsd" includes="TimeCard"/>
</schema-set>

É possível usar o destino Ant custgen2 para tentar essa customização com CodeGen ou usar o destino custom2 para executar a sequência completa de geração, compilação, ligação e teste. Essa mudança reduz o número de classes de nível superior no modelo de dados de 15 para 10 — nada mal para iniciar a simplificação do modelo de dados.


Customizando Componentes Individuais

Até o momento, você viu somente exemplos de customizações que se aplicam a todo o conjunto de esquemas ou a esquemas individuais. Você também pode customizar a manipulação de componentes específicos de CodeGen em uma definição de esquema, incluindo definições globais e itens integrados às definições globais. As customizações disponíveis incluem eliminar o componente do modelo de dados, alterando a classe ou o nome do valor usado para o componente e alterando o tipo de esquema do componente.

As customizações para eliminar componentes do modelo de dados não são muito úteis se você controlar o esquema — nesse caso, sempre será mais simples apenas alterar o esquema diretamente. Mas esquemas de troca de de dados corporativos frequentemente incluem componentes especializados que podem não ser apropriados para aplicativos específicos usando esses esquemas e esses esquemas geralmente não estão sob seu controle. Usar customizações nesse caso permite simplificar seu modelo de dados sem tocar os esquemas fornecidos.

Customizações de Componentes

As customizações para componentes de esquema funcionam associando um elemento de customização ao elementos de definição de esquema que representa o componente. Você pode usar diversas abordagens diferentes para estabelecer a associação entre a customização e o elemento do esquema, pois uma abordagem pode ser mais conveniente do que outra em uma situação específica. Uma parte da associação é fixa, no entanto: o nome do elemento de customização deve sempre corresponder ao nome do elemento do componente do esquema. Portanto, para customizar uma definição <xs:element> em um esquema, por exemplo, você precisa usar um elemento de customização <element (sem nenhum namespace).

A Listagem 15 mostra uma definição de um dos outros esquemas referidos por TimeCard, que é uma boa amostra para demonstrar customização de componentes individuais. O PersonNameType consiste em diversos elementos xs:string simples, juntamente com alguns outros elementos com estrutura complexa. Como ocorre, os documentos de teste usados no código do tutorial não incluem nenhuma instância dos elementos Affix ou AlternateScript desse tipo, portanto, são bons candidatos para eliminar para simplificar o modelo de dados gerado.

Listagem 15. Esquema PersonName
<xsd:complexType name="PersonNameType">
  <xsd:sequence>
    <xsd:element name="FormattedName" type="xsd:string" minOccurs="0"/>
    <xsd:element name="LegalName" type="xsd:string" minOccurs="0"/>
    <xsd:element name="GivenName" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
    <xsd:element name="PreferredGivenName" type="xsd:string" minOccurs="0"/>
    <xsd:element name="MiddleName" type="xsd:string" minOccurs="0"/>
    <xsd:element name="FamilyName" minOccurs="0" maxOccurs="unbounded">
      <xsd:complexType>
        ...
      </xsd:complexType>
    </xsd:element>
    <xsd:element name="Affix" minOccurs="0" maxOccurs="unbounded">
      <xsd:complexType>
        ...
      </xsd:complexType>
    </xsd:element>
    <xsd:element name="AlternateScript" minOccurs="0" maxOccurs="unbounded">
      <xsd:complexType>
        ...
      </xsd:complexType>
    </xsd:element>
  </xsd:sequence>
  <xsd:attribute name="script" type="xsd:string"/>
</xsd:complexType>

A Listagem 16 mostra uma maneira de definir customizações para eliminar os elementos Affix eAlternateScript do modelo de dados. Essa abordagem usa uma especificação de caminho, que é um conjunto de instruções como XPath para navegar pela estrutura de definição de esquema. As etapas do caminho são separadas por caracteres barra (/) e as etapas que correspondem a componentes denominados da definição de esquema (tipo global, grupo ou definições attributeGroup , os definições de elementos ou atributos, sendo globais ou não) podem usar um predicado element or attribute definitions whether global or not) can use an [@name=...] para destacar uma instância específica do tipo de componente.

Listagem 16. Customização Direta para Componentes Desnecessários
<schema-set ...>
  <schema name="PersonName.xsd">
    <element path="complexType[@name=PersonNameType]/sequence/element[@name=Affix]"
        ignore="true"/>
    <element path=
        "complexType[@name=PersonNameType]/sequence/element[@name=AlternateScript]"
        ignore="true"/>
  </schema>
</schema-set>

Na Listagem 16, cada caminho está escrito por extenso, a partir do nível do esquema. Você também pode usar curingas nos caminhos. O curinga * como etapa do caminho corresponde a qualquer elemento único na definição de esquema, enquanto que o curinga ** corresponde a um número qualquer de elementos aninhados na definição do esquema. Portanto, em vez de o caminho complexType[@name=PersonNameType]/sequence/element[@name=Affix], você poderia usar complexType[@name=PersonNameType]/*/element[@name=Affix]ou complexType[@name=PersonNameType]/**/element[@name=Affix]. Não poderia usar **/element[@name=Affix], no entanto — CodeGen requer que você identifique explicitamente o componente da definição global envolvido em qualquer customização, como segurança para evitar a aplicação da customização incorretamente.

As customizações do componentes podem ser aninhadas, desde que o aninhamento corresponda à estrutura de definição de esquema. Nesse caso, cada customização precisa apenas de especificar seu destino em relação à customização contida. Você também pode usar um atributo name="..." na customização como uma alternativa para um predicado [@name=...] na etapa final de um caminho e pode ignorar o nome do elemento na etapa final (pois deve sempre ser o mesmo que o nome do elemento de customização). Você pode até mesmo evitar o uso de um caminho completamente, usando, em vez disso aninhamento combinado a um atributo de nome. A Listagem 17 mostra as mesmas customizações que a Listagem 16, reestruturada para usar essa abordagem alternativa:

Listagem 17. Customização Aninhada para Componentes Desnecessários
<schema-set ...>
  <schema name="PersonName.xsd">
    <complexType name="PersonNameType">
      <sequence>
        <element name="Affix" ignore="true"/>
        <element name="AlternateScript" ignore="true"/>
      </sequence>
    </complexType>
  </schema>
</schema-set>

Simplufucando o Modelo de Dados

Além dos componentes PersonName usados como exemplos na subseção anterior, os esquemas TimeCard têm outros diversos componentes complexos que não são usados nos documentos de amostra incluídos neste tutorial. Usando customizações para eliminar esses componentes não usados, você pode simplificar de forma considerável o modelo de dados gerado. Há também alguns casos em que nomes de valor Java usados por CodeGen não funcionarão bem. Especificamente, casos em que o mesmo nome de elemento é usado repetidamente podem resultar em nomes de valores distintos somente por sufixos de dígitos, dificultando o entendimento do uso apropriado dos valores. Consulte a Listagem 10 para obter um exemplo, onde um par de campos denominados duration e duration1 estão incluídos no código gerado. Você pode usar uma customização para alterar um desses nomes para algo mais significativo.

A Listagem 18 mostra o arquivo custom3.xml do diretório hrxml do código, que inclui todas essas customizações. Isso usa intencionalmente diversas abordagens de identificação de componente discutidas na subseção anterior, com aninhamento, caminho e caminhos combinados com nomes. A customização do nome do valor está na parte inferior, usando um atributo value-name="simpleDuration" para alterar o nome usado para a segunda duração para uma forma mais descritiva.

Listagem 18. Simplificando e Esclarecendo o Modelo de Dados TimeCard
<schema-set xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://ns.hr-xml.org/2007-04-15" package="org.hrxml.timecard"
    type-substitutions="xs:integer xs:int" generate-all="false">
  <name-converter strip-suffixes="Type"/>
  <class-decorator class="org.jibx.schema.codegen.extend.CollectionMethodsDecorator"/>
  <schema name="UserArea.xsd" excludes="UserArea UserAreaType"/>
  <schema name="PersonName.xsd">
    <complexType name="PersonNameType">
      <sequence>
        <element name="Affix" ignore="true"/>
        <element name="AlternateScript" ignore="true"/>
      </sequence>
    </complexType>
  </schema>
  <schema name="TimeCard.xsd" includes="TimeCard">
    <complexType name="TimeCardType">
      <element path="**/element[@name=Allowance]" ignore="true"/>
      <element path="**/element[@name=PieceWork]" ignore="true"/>
      <element path="**/element[@name=TimeEvent]/**/" name="RateOrAmount" ignore="true"/>
      <element path="**/choice/[@name=Duration]" value-name="simpleDuration"/>
    </complexType>
  </schema>
</schema-set>

É possível usar o destino Ant custgen3 para tentar essa customização com CodeGen ou usar o destino custom3 para executar a sequência completa de geração, compilação, ligação e teste. As customizações reduzem a contagem de classes geradas para 9 classes de nível superior e 10 classes internas, um total de 19. Essa contagem é exatamente metade do número de classe do modelo de dados gerado original sem usar customizações.


Parâmetros da Linha de Comando de CodeGen

CodeGen suporta diversos parâmetros de linha de comando adicionais além dos usados no código do tutorial. A Tabela 1 lista as opções mais importantes:

Tabela 1. Opções da Linha de Comando de CodeGen
ComandoPropósito
-c pathCorreção para inserir arquivo de customizações
-n packagePacote padrão para definições de esquema sem namespace (o padrão é o pacote padrão)
-p packagePacote padrão para todas as definições de esquema (o padrão é usar o pacote gerado de cada namespace de esquema)
-s pathCaminho do diretório-raiz do esquema (o padrão é o diretório atual)
-t pathCaminho do diretório de destino para saída gerada (o padrão é o diretório atual)
-vSinalizador de saída detalhada
-wLimpe todos os arquivos do diretório de destino antes de gerar a saída (ignorado se o diretório de destino for o mesmo que o diretório atual)

Você também pode passar customizações globais para CodeGen como parâmetros da linha de comando, sem a necessidade de criar um arquivo de customizações, usando o prefixo especial -- antes do valor do atributo de customização. Portanto, para configurar as mesmas opções globais que as usadas nas customizações da Listagem 5 , você inclui --prefer-inline=true --show-schema=false --enumeration-type=simple --generate-all=false na linha de comando de CodeGen. (Você não pode especificar a lista de componentes do esquema a serem incluídos ba geração desta forma, pois são específicos de um esquema específico.) Aspas não são necessárias para o valor do atributo quando você usa essa técnica. Se quiser configurar uma customização que aceita uma lista de diversos valores, use vírgulas em vez de espaços como separadores entre os valores individuais. (Portanto, para ignorar os sufixos de nomes de esquemas Type e Group , por exemplo, você usaria o parâmetro da linha de comando --strip-suffixes=Type,Group .)


Indo Além

Neste tutorial, você aprendeu o básico de como usar JiBX para gerar um modelo de dados Java a partir de uma definição de esquema XML primeiro e, em seguida, converter documentos que correspondem ao esquema para e a partir do modelo de dados. Você também viu exemplos de como usar customizações para controlar como o modelo de dados é gerado. Há muitas outras customizações que você pode usar para controlar diferentes aspectos do modelo de dados, além das que cobri neste tutorial. A documentação de JiBX fornece detalhes integrais sobre todas essas opções de customização, juntamente com mais exemplos de geração de código a partir de esquema.

Definições de serviços da Web são um dos principais usos de esquemas XML. JiBX pode ser atualmente usado em pilhas de serviços da Web Apache Axis2, Apache CXF, XFire e Spring-WS e também suporta seu próprio mecanismo leve de serviços da Web no formato de JiBX/WS. Você pode usar os recursos de geração de código a partir de esquema discutidos neste tutorial em qualquer uma dessas pilhas de serviços da Web, apesar de atualmente ser necessário extrair a definição do esquema da definição de serviço Web Services Description Language (WSDL) antes de poder ser gerado. Você também precisa passar por etapas adicionais para cada pilha para chegar a um serviço da Web funcional. Releases futuros de JiBX simplificarão o processo de criação de implementações de serviços da Web, portanto, verifique a documentação em sua distribuição JiBX para descobrir sobre quaisquer novos recursos nessa área.


Download

DescriçãoNomeTamanho
Sample code for this tutorialj-jibx2.zip27KB

Recursos

Aprender

Obter produtos e tecnologias

  • JiBX: Faça download da estrutura JiBX.
  • Ant: Faça download de Apache Ant.
  • Sun JDK 1.5 ou posterior: Você precisará de pelo menos a versão 1.5.0_09 para seguir os exemplos deste tutorial.
  • IBM® developer kits: IBM Java developer kits estão disponíveis para AIX e Linux.

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=387446
ArticleTitle=JiBX 1.2, Parte 2: Esquema XML para Código Java
publish-date=03032009