Conteúdo


XML: The bridge between GWT and PHP

GWT pode usar serviços PHP e XML fornece uma fácil ponte de troca de dados

Comments

GWT permite fácil acesso aos servlets do lado do servidor programados na linguagem Java e os dados são passados de forma transparente, nos bastidores, entre cliente e servidor. No entanto, à medida que trabalha com GWT, não está limitado a se comunicar com esses servlets e você pode trocar dados livremente com todos os tipos de serviços da Web. Em muitos casos (para serviços simples), essas transferências podem ser feitas com texto simples, mas sempre que os dados forem estruturados ou simplesmente mais complicados (pense em RSS, por exemplo), as chances são que XML os representará.

Este artigo examina um aplicativo GWT simples e dois serviços da Web PHP, mostrando diversas maneiras diferentes de produzir e consumir documentos XML. Este artigo não tem a intenção de ser um tutorial ou um manual completo, mas ser, na verdade, um conjunto de ponteiros para que você possa começar mais rapidamente a trabalhar com XML como uma ponte entre GWT e PHP por conta própria.

Um Aplicativo de Teste

Para mostrar como é possível usar XML como uma ponte entre PHP e GWT, forneço um aplicativo simples baseado em dados de países/regiões/cidades. Verificando o código e criação do banco de dados na Lista 1, é possível ver que:

  • Países têm um código exclusivo (por exemplo, UY para Uruguai) e um nome.
  • Países são divididos em regiões, identificadas com um código (exclusivo dentro do país) e têm um nome.
  • Regiões têm cidades, que têm um nome (ASCII puro), um nome acentuado (que pode conter caracteres estrangeiros), uma população (ou 0 se desconhecida), uma latitude e uma longitude. O nome da cidade pode aparecer em diferentes regiões do mesmo país.
Listing 1. Database creation code
CREATE DATABASE world
    DEFAULT CHARACTER SET latin1
    COLLATE latin1_general_ci;

USE world;

CREATE TABLE countries (
    countryCode char(2) NOT NULL,
    countryName varchar(50) NOT NULL,
    PRIMARY KEY (countryCode)
    KEY countryName (countryName)
    );

CREATE TABLE regions (
    countryCode char(2) NOT NULL,
    regionCode char(2) NOT NULL,
    regionName varchar(50) NOT NULL,
    PRIMARY KEY (countryCode,regionCode),
    KEY regionName (regionName)
    );

CREATE TABLE cities (
    countryCode char(2) NOT NULL,
    cityName varchar(50) NOT NULL,
    cityAccentedName varchar(50) NOT NULL,
    regionCode char(2) NOT NULL,
    population bigint(20) NOT NULL,
    latitude float(10,7) NOT NULL,
    longitude float(10,7) NOT NULL,
    KEY `INDEX` (countryCode,regionCode,cityName),
    KEY cityName (cityName),
    KEY cityAccentedName (cityAccentedName)
    );

Criei um projeto GWT simples com apenas um formulário e dois serviços da Web PHP. (Consulte Recursos para download para obter o código de origem integral.) Ao iniciar o aplicativo, verá a janela simples da Figura 1.

Figura 1. O Formulário Vazio
Uma Captura de Tela de um Formulário Vazio para os Aplicativos de Amostra
Uma Captura de Tela de um Formulário Vazio para os Aplicativos de Amostra

O formulário GWT permite inserir uma parte de um nome de cidade e chama um serviço PHP para obter todas as cidades que correspondem a o que foi digitado. As cidades são exibidas em uma grade e é possível editar os campos de população, latitude e longitude. Os dados editados podem ser enviados de volta a um segundo serviço da Web PHP, que atualiza o banco de dados. Todas as transferências de dados são feitas em XML. Como 2009 marca 200 anos do nascimento de Charles Darwin e 150 anos de seu livro A Origem das Espécies , poderia procurar cidades com DARWIN em seus nomes; consulte a Figura 2 para obter os resultados.

Figura 2. Uma Procura por Cidades que Incluem "Darwin" em seus Nomes
Um Formulário de Amostra, Após Obter as Informações das Cidades
Um Formulário de Amostra, Após Obter as Informações das Cidades

Alguma Configuração Extra

Apenas para referência, trabalhei com:

  • GWT versão 1.5.3
  • PHP versão 5.2.8
  • Servidor de banco de dados MySQL® versão 5.0.67
  • Apache versão 2.2.10 sob OpenSUSE® versão 11.1

Instalei todo o software com instalação padrão, mas GWT precisava de uma etapa extra de configuração para que eu pudesse testar a conexão GWT-PHP; verifique a barra lateral O Problema SOP para ver a razão. Para desativar verificações same-origin policy (SOP) no navegador interno do GWT, edite o arquivo ./mozilla-1.7.12/greprefs/all.js em seu diretório do GWT e inclua as linhas da Lista 2 no final desse arquivo:

Listing 2. Configuration changes for the internal GWT browser
pref("capability.policy.default.XMLHttpRequest.abort", "allAccess");
pref("capability.policy.default.XMLHttpRequest.getAllResponseHeaders","allAccess");
pref("capability.policy.default.XMLHttpRequest.getResponseHeader","allAccess");
pref("capability.policy.default.XMLHttpRequest.open", "allAccess");
pref("capability.policy.default.XMLHttpRequest.send", "allAccess");
pref("capability.policy.default.XMLHttpRequest.setRequestHeader","allAccess");
pref("capability.policy.default.XMLHttpRequest.onreadystatechange","allAccess");
pref("capability.policy.default.XMLHttpRequest.readyState", "allAccess");
pref("capability.policy.default.XMLHttpRequest.responseText","allAccess");
pref("capability.policy.default.XMLHttpRequest.responseXML","allAccess");
pref("capability.policy.default.XMLHttpRequest.status", "allAccess");
pref("capability.policy.default.XMLHttpRequest.statusText", "allAccess");

Sempre que atualizar GWT, será necessário fazer essa mudança novamente. Além disso, observe que, sem fazer isso, pode ser que seja gravado código que falha no modo Hospedado, mas é executado corretamente no modo Compilado; após a mudança, pode haver código executado no modo Hospedado, mas que falha no modo Compilado, fique ligado!

Enviando XML com PHP

Esse aplicativo simplesmente define um formulário simples, com alguns rótulos, uma caixa de texto, dois botões de comando e uma grade para os resultados. Sempre que clicar em Obter Cidades, o aplicativo chama um serviço PHP para obter um documento XML com todas as cidades que correspondem ao que foi digitado na caixa de texto. A Lista 3 mostra um XML de amostra (um tanto quanto abreviado) que é enviado p serviço PHP para o aplicativo GWT. O código XML produzido deve ilustrar diversos recursos XML e é, portanto, muito mais longo do que seria para um aplicativo real. Geralmente, um serviço XML bem projetado usa menos tags e mais atributos, evita deslocamento e é um documento mais curto.

Listing 3. The XML document produced when searching for "tokyo"
<?xml version="1.0" encoding="UTF-8"?>
 <cities>
  <city name="tokyo">
   <country code="JP" name="Japan"/>
   <region code="40" name="Tokyo"/>
   <coords>
    <lat>35.6850014</lat>
    <lon>139.7513885</lon>
   </coords>
   <pop>31480498</pop>
  </city>
  <city name="tokyo">
   <country code="PG" name="Papua New Guinea"/>
    <region code="01" name="Central"/>
   <coords>
    <lat>-8.3999996</lat>
    <lon>147.1499939</lon>
   </coords>
  </city>
  <city name="tokyojitori">
   <country code="KR" name="Korea, Republic of"/>
   <region code="16" name="Cholla-namdo"/>
   <coords>
    <lat>34.2380562</lat>
    <lon>125.9394455</lon>
   </coords>
  </city>
</cities>

Há duas versões do próprio serviço PHP —getcities1.php e getcities2.php—cada um dos quais mostra diferentes maneiras de produzir XML.

De longe, a maneira mais simples de produzir XML é simplesmente imprimir o texto apropriado de forma sequencial ou construir uma cadeia de caracteres, em seguida, emiti-lo usando echo. O tipo de conteúdo deve ser configurado para text/xml de forma que seja reconhecido corretamente. Lembre-se também de incluir uma linha de descrição apropriada que especifica a versão XML e a codificação de dados. Será necessário escapar cadeias de caracteres de forma que não incluam caracteres menor que (<), maior que (>) ou e comercial (&); a maneira mais fácil é usar a função PHP htmlspecialchars() . A codificação é fácil, como a parte da Lista 4 mostra. Observe que deslocamento e quebras de linhas não são realmente necessários, apesar de facilitarem a leitura do código.

Listing 4. The simplest method to produce XML from a PHP service
...
header("Content-type: text/xml");
...
echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";
echo '<cities>'."\n";
...
while ($row= mysql_fetch_assoc($result)) {
  echo ' <city name="'.htmlspecialchars($row['cityName']).'">'."\n";
  echo '  <country code="'.$row['countryCode'].'" ';
  echo 'name="'.htmlentities($row['countryName']).'"/>'."\n";
  echo '  <region code="'.$row['regionCode'].'" ';
  echo 'name="'.htmlentities($row['regionName']).'"/>'."\n";

  echo '  <coords>'."\n";
  echo '    <lat>'.$row['latitude'].'</lat>'."\n";
  echo '    <lon>'.$row['longitude'].'</lon>'."\n";
  echo '  </coords>'."\n";

  if ($row['population']>0) {
    echo '  <pop>'.$row['population'].'</pop>'."\n";
  }

  echo ' </city>'."\n";
}
echo '</cities>'."\n";

Um segundo método (e não muito mais longo) para produzir código XML é usando XMLWriter. (Uma classe companheira, XMLReader, permite processamento de XML.) Pode esquecer sobre escapar caracteres, pois isso é feito automaticamente. Apesar de parecer conter mais palavras do que o método echo() acima, pode ser argumentado que essa abordagem cria um código mais compreensível. Observe especialmente o uso do protocolo php://output para que o texto seja emitido como com o comando echo . (Consulte a Lista 5.)

Listing 5. XMLWriter provides simple methods to build up an XML document one element at a time
...
$writer= new XMLWriter();
$writer->openURI('php://output')
$writer->startDocument('1.0', 'UTF-8');
$writer->startElement("cities");

while ($row= mysql_fetch_assoc($result)) {
  $writer->startElement("city");
  $writer->writeAttribute("name", $row['cityName']);

  $writer->startElement("country");
  $writer->writeAttribute("code", $row['countryCode']);
  $writer->writeAttribute("name", $row['countryName']);
  $writer->endElement();

  $writer->startElement("region");
  $writer->writeAttribute("code", $row['regionCode']);
  $writer->writeAttribute("name", $row['regionName']);
  $writer->endElement();

  $writer->startElement("coords");
  $writer->writeElement("lat", $row['latitude']);
  $writer->writeElement("lon", $row['longitude']);
  $writer->endElement();

  if ($row['population']>0) {
    $writer->writeElement("pop", $row['population']);
  }

  $writer->endElement();	// city
}
$writer->endElement(); // cities
...

Se quiser fazer experiências com mais métodos de produção de XML, PHP certamente tem muito a oferecer. Por exemplo, é possível usar SimpleXML; ser;a usado posteriormente para ler XML, mas também pode ser usado para criação de documento XML. Além disso, você também pode dar uma olhada na estrutura PEAR, que inclui diversas classes para fácil geração de XML. (Consulte Recursos para obter links para informações adicionais.)

Processando XML com GWT

GWT fornece somente XMLParser (no pacote com.google.gwt.xml.client ) para leitura e gravação e XML. Use o método parse() para criar um Document, em seguida, use getDocumentElement() para obter seu elemento raiz; em seguida, estará pronto para começar a percorrer o XML.

Um ponto importante é que o método removeWhitespace() deve ser usado para remover espaço em branco de seu documento. Analisadores de navegador às vezes criam nós de texto vazios correspondentes a guias ou quebras de linha e, caso não sejam removidos, sei processo encontrará elementos irrelevantes inesperados, que podem afetar sua lógica (consulte Lista 6). Outro ponto: Se esperar seções CDATA, seria necessário verificar se seu navegador aceita o método supportsCDATASection() ; do contrário, essas seções produzirão nós de texto, em vez disso. Verifique a documentação de GWT (consulte Recursos) para saber mais sobre isso.

Listing 6. XMLParser provides for both XML reading and creation in GWT
protected void loadCities(final String xmlCities) {
  ...
  final Document xmlDoc= XMLParser.parse(xmlCities);
  final Element root= xmlDoc.getDocumentElement();
  XMLParser.removeWhitespace(xmlDoc);

  final NodeList cities= root.getElementsByTagName("city");
  for (int i= 0; i < cities.getLength(); i++) {
    final Element city= (Element)cities.item(i);
    // show city.getAttributeNode("name").getValue()

    final Element country= (Element)city.getElementsByTagName("country").item(0);
    // show country.getAttributeNode("code").getValue()
    // show country.getAttributeNode("name").getValue()
    ...
    final Element population= (Element)city.getElementsByTagName("pop").item(0);
    if (population != null) {
      // show population.getFirstChild().getNodeValue()
    }

    final Element coords= (Element)city.getElementsByTagName("coords").item(0);
    final Element lat= (Element)coords.getElementsByTagName("lat").item(0);
    // show lat.getFirstChild().getNodeValue()
    ...
  }
...

É possível obter elementos repetidos (como city neste exemplo) usando o método getElementsByTagName() e percorrendo a array resultante. Uma alternativa é usar o método getFirstChild() , em seguida, percorrer os demais elementos no mesmo nível com getNextSibling(). Obter atributos requer, primeiramente, o método getAttributeNode() , em seguida, o método getValue() . Há métodos que processam seções CDATA, comentários e todos os componentes XML possíveis.

Enviando XML com GWT

O aplicativo GWT permite que os usuários editem os campos de população, latitude e longitude,em seguida, enviem os dados das cidades de volta ao servidor para atualizar o banco de dados. Dois algoritmos são mostrados: um simples, construindo a cadeia de caracteres XML por partes e um algoritmo baseado em XMLParser que usa métodos específicos para criar a estrutura desejada.

O algoritmo mais simples é mostrado no método getCities1() . Um objeto String ou StringBuffer pode ser usado para construir o XML. Usei o primeiro, pois torna o código mais claro; mas, em termos de desempenho, a segunda opção é provavelmente melhor. Os mesmos problemas de escape de caractere visto na versão PHP estão aqui, portanto, use o método Html.htmlspecialchars() para corrigir isso. (Consulte Lista 7, que foi ligeiramente modificada por questão de clareza.)

Listing 7. Building XML piece by piece is simple using strings
protected String getCities1() {
  String result= "";

  result+= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  result+= "<cities>\n";

  for (all rows in the grid) {
    // get cityName, countryCode, regionCode, pop, lat, and lon, from the grid

    result+= " <city name=\"" + Html.htmlspecialchars(cityName) + "\">\n";
    result+= "  <country code=\"" + countryCode + "\"/>\n";
    result+= "  <region code=\"" + regionCode + "\"/>\n";
    if (!pop.equals("0") && !pop.isEmpty()) {
      result+= "  <pop>" + pop + "</pop>\n";
    }

    result+= "  <coords>\n";
    result+= "   <lat>" + lat + "</lat>\n";
    result+= "   <lon>" + lon + "</lon>\n";
    result+= "  </coords>\n";
    result+= "</city>\n";
  }
  result+= "</cities>\n";
  return result;
}

O outro algoritmo simples para criação de XML é o método createDocument() da classe XMLParser . Em um estilo fortemente remanescente das funções SimpleXML em PHP, primeiro, cria-se um documento vazio, em seguida, inclui-se elementos nele. É possível criar todos os tipos de nós, assim como configurar valores de atributos. Por fim, o método toString() Java padrão produz uma representação do objeto; é necessário incluir apenas a linha de versão e codificação inicial e terá sua cadeia de caracteres necessária. (Consulte Lista 8, que foi modificada e resumida por questão de clareza.)

Listing 8. The XMLParser methods for XML creation are similar to SimpleXML's methods in PHP
protected String getCities2() {
  Document xml= XMLParser.createDocument();
  Element cities= xml.createElement("cities");
  xml.appendChild(cities);

  for (all rows in the grid) {
    // get cityName, countryCode, regionCode, pop, lat, and lon, from the grid

    Element city= xml.createElement("city");
    city.setAttribute("name", cityName);

    Element country= xml.createElement("country");
    country.setAttribute("code", countryCode);
    city.appendChild(country);
    ...
    if (!pop.equals("0") && !pop.isEmpty()) {
      Element popEl= xml.createElement("pop");
      Text popText= xml.createTextNode(pop);
      popEl.appendChild(popText);
      city.appendChild(popEl);
    }

    Element coords= xml.createElement("coords");
    Element lat= xml.createElement("lat");
    Text latText= xml.createTextNode(lat);
    lat.appendChild(latText);
    coords.appendChild(lat);
    ...
    city.appendChild(coords);
    cities.appendChild(city);
  }
  return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + xml.toString();
}

Lendo XML em PHP

Processar XML em PHP é um velho problema e há diversas soluções. No entanto, na minha opinião, nenhuma chega perto de SimpleXML em termos de clareza e concisão. Basicamente, cria-se um objeto PHP alimentando o documento XML ao método simplexml_load_string() , em seguida, atravesse o resultado usando operadores PHP padrão. Atributos tornam-se elementos de arrays (consulte como o código do país é lido na Lista 9, por exemplo) e elementos podem ser acessados como uma array (usando o método children() ) ou diretamente, como atributos do objeto.

Listing 9. SimpleXML is by far the easiest method of XML processing in PHP
$xml_str= $_POST["xmldata"];
$xml_obj= simplexml_load_string($xml_str);
...
foreach($xml_obj->children() as $city) {
  $name= addslashes($city['name']);
  $country= $city->country['code'];
  $region= $city->region['code'];
  $pop= $city->pop;
  $lat= $city->coords->lat;
  $lon= $city->coords->lon;

  mysql_query("REPLACE INTO cities ".
    "(cityName, countryCode, regionCode, population, latitude, longitude) VALUES (".
    "'{$name}', '{$country}', '{$region}', '{$pop}', '{$lat}', '{$lon}')");
}

Há muitas outras maneiras de processar XML com PHP. Verifique a seção Recursos para obter links.

Conclusão

Só arranhei a superfície das diversas maneiras que XML pode ser usado como uma ponte entre GWT e PHP, mas os métodos fornecidos devem suficientes para fornecer um ponto de partida. O ponto principal a se lembrar é que GWT não está limitado a usar seu próprio método RPC e também pode existir felizmente com XML, produzindo e consumindo esses documentos com facilidade.


Recursos para download


Temas relacionados


Comentários

Acesse ou registre-se para adicionar e acompanhar os comentários.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=396061
ArticleTitle=XML: The bridge between GWT and PHP
publish-date=04072009