Google Web Toolkit(GWT)应用程序除了以传统的 Java™ 
                          方式连接到 servlet 外,还可以使用 PHP Web 服务发送和接收 XML 格式的数据。您将探索以 
                          Java 和 PHP 语言生成和处理 XML 文档的方法。 
                        通过 GWT 可以方便地访问用 Java 语言编写的服务器端 servlet,并且客户机和服务器之间的数据传递是透明的。但是,当使用 
                          GWT 时,不仅可以与那些 servlet 通信,还可以随意地与所有类型的 Web 服务交换数据。在很多情况下(对于简单的服务),可以用纯文本格式传输数据,但是每当遇到结构化的数据或较复杂的数据(例如 
                          RSS)时,数据很可能是用 XML 表示的。 
                        本文研究一个简单的 GWT 应用程序和两个 PHP Web 服务,展示生成和使用 XML 文档的几种不同的方法。本文无意成为详尽的教程或手册,而是提供一些忠告,使您能更快地开始使用 
                          XML 作为连接 GWT 与 PHP 的桥梁。 
                        
                        为了展示如何使用 XML 作为 PHP 与 GWT 之间的桥梁,我提供一个简单的应用程序,这个应用程序基于国家/地区/城市数据。查看 
                          清单 1 中的数据库创建代码,可以看到: 
                        
                          - 国家(country)有惟一的代码(例如 UY 表示乌拉圭)和名称。 
                          
 
                          - 国家 被划分为多个地区(region),地区有(国家内惟一的)代码和名称。 
                          
 
                          - 地区 内有多个城市(city),城市有(纯 ASCII)名称、别名(可能包括外来字符)、人口(如果未知则为 
                            0)、纬度和经度。城市名可出现在同一个国家的不同地区。 
 
                         
                        清单 1. 数据库创建代码 
                           
                           
                             				
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)
    );
 | 
                           
                         
                        我创建了一个简单的 GWT 项目,它只有一个表单和两个 PHP Web 服务。(下载 
                          小节提供了完整的源代码)。启动该应用程序时,可以看到  
                          图 1 中的窗口。 
                        图 1. 空的表单    
                        GWT 表单允许输入一个城市名的一部分,然后调用一个 PHP 服务获得与输入的内容匹配的所有城市。这些城市显示在一个网格中,可以编辑人口(population)、纬度(latitude)和经度(longitude)字段。然后,可以将编辑的数据发回到另一个 
                          PHP Web 服务,后者将更新数据库。所有数据传输都是通过 XML 进行的。2009 年是查尔斯达尔文 
                          200 诞辰和他的著作物种起源 诞生 150 周年,您可以查看城市名中包含 DARWIN 
                          的城市;图 2 显示了结果。 
                        图 2. 搜索城市名中包含 “Darwin” 的城市  
                          
                        一些额外的配置 
                        下面是我使用的软件,仅供参考: 
                        
                          - GWT version 1.5.3 
 
                          - PHP version 5.2.8 
 
                          - MySQL® database server version 5.0.67 
 
                          - Apache version 2.2.10 under OpenSUSE® version 
                            11.1 
 
                         
                        我安装的所有软件都是开箱即用的,但是 GWT 要求一个额外的配置步骤,这样才能测试 GWT-PHP 连接;请参阅侧边栏 
                           
                          SOP 问题,看看是什么原因。为了禁用内部 GWT 浏览器的同源策略(same-origin policy,SOP)检查,可以编辑 
                          GWT 目录中的 ./mozilla-1.7.12/greprefs/all.js 文件,在文件的最后添加 
                          清单 2 中的代码行: 
                        清单 2. 修改内部 GWT 浏览器的配置 
                           
                           
                             				
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");
 | 
                           
                         
                        每当更新 GWT 时,需要再次作出这样的更改。而且,请注意,如果不这样做,编写的代码在 Hosted 
                          模式下会失败,但是在 compiled 模式下却可以正确运行。作出以上更改后,您的代码可能在 Hosted 
                          模式下可以运行,但是在 Compiled 模式下却会失败,所以要小心! 
                        
                        这个应用程序只定义一个简单的表单,其中有一些标签、一个文本框、两个命令按钮和一个用于显示结果的网格。每当单击 
                          Get cities 时,该应用程序调用一个 PHP 服务,以便获得一个 XML 文档,其中包含与文本框中输入的内容匹配的所有城市。清单 
                          3 显示从 PHP 服务发送到 GWT 应用程序的一个示例 XML(有所简化)。生成的 XML 
                          代码要用于演示一些 XML 功能,所以比用于真正的应用程序的 XML 要长得多。 通常,设计良好的 XML 
                          服务使用更少的标记和更多的属性,避免缩进,并且更短一些。 
                        清单 3. 搜索 “tokyo” 时生成的 XML 文档 
                           
                           
                             				
<?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> | 
                           
                         
                        这个 PHP 服务本身有两个版本 — getcities1.php 和 getcities2.php 
                          —,每个版本展示生成 XML 的不同方法。 
                        到目前为止,生成 XML 的最简单的方法是连续输出适当的文本,或者构建一个字符串,然后使用 echo 
                          发出它。这里应该将 content type 设为 text/xml,使之能够被正确地识别,并且还要记得包括一个适当的 
                          description 行,以指定 XML 版本和数据编码方式。另外还必须转义字符串,使字符串中不包括小于(<)、大于(>)或和(&)字符。最简单的方法是使用 
                          htmlspecialchars() PHP 函数。代码很容易编写,如 清单 
                          4 所示。注意,缩进和换行实际上是不需要的,不过这样做可以让代码更易于阅读。 
                        清单 4. 从 PHP 服务生成 XML 的最简单的方法 
                           
                           
                             				
...
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"; | 
                           
                         
                        生成 XML 代码的另一种方法(也不是很长)是使用 XMLWriter。(与之成对的另一个类 
                          XMLReader 可用于 XML 处理)。现在可以不用关心字符的转义,因为这是自动完成的。虽然这种方法看起来比前面的 
                          echo() 方法冗长一点,但是这种方法编写的代码更易于理解。特别注意,这里使用了 
                          php://output 协议,以便用 echo 命令发出文本。(如 
                           
                          清单 5 所示)。 
                        清单 5. XMLWriter 提供逐个元素地构建 XML 文档的简单方法 
                           
                           
                             				
...
$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
... | 
                           
                         
                        如果想体验更多生成 XML 的方法,PHP 当然提供了很多的选择。例如,可以使用 SimpleXML;以后您将使用它读取 
                          XML,但是它还提供 XML 文档创建功能。另外,还可以看一下 PEAR 框架,它包括一些可用于轻松生成 
                          XML 的类。(请参阅 参考资料,获得更多信息的链接)。 
                        
                        GWT 只提供 XMLParser(在 com.google.gwt.xml.client 
                          包中)用于读、写 XML。可以使用 parse() 方法创建一个 Document,然后使用 
                          getDocumentElement() 获得它的根元素;然后,便可以开始处理 
                          XML。 
                        注意,应该使用 removeWhitespace() 方法去掉文档中的空白。浏览器解析器有时候会创建与制表符或换行符对应的空文本节点,如果不去掉它们,处理过程中会遇到不相关的、意外的元素,这样会破坏逻辑(见 
                          清单 6)。另外还有一点要注意:如果预期有 
                          CDATA 区段,那么需要检查浏览器是否接受 supportsCDATASection() 
                          方法;如果不接受,那些区段将产生文本节点。请参阅 GWT 文档(见 参考资料),获得更多这方面的信息。 
                         
                        清单 6. GWT 中提供了 XMLParser 用于读取和创建 XML 
                           
                           
                             				
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()
    ...
  }
... | 
                           
                         
                        要获得重复的元素(例如这个例子中的 city),可以使用  getElementsByTagName() 
                          方法,并迭代生成的数组。另一种方法是使用  getFirstChild() 方法,然后使用 
                          getNextSibling() 遍历同一级别上的其他元素。要获得属性,首先需要使用 
                          getAttributeNode() 方法,然后使用 getValue() 
                          方法。还有一些用于处理 CDATA 区段、注释和所有可能的 XML 组件的方法。 
                        
                        GWT 应用程序让用户编辑人口、纬度和经度字段,然后将城市数据发回到服务器,以更新数据库。有两种算法:一种是简单的算法,这种算法逐块构建 
                          XML 字符串,还有一种基于 XMLParser 的算法,该算法使用特定的方法创建所需的结构。 
                        getCities1() 方法中显示了较简单的算法。可以使用 String 
                          或 StringBuffer 对象构建 XML。我使用了前者,因为它使代码更加清晰;但是如果考虑性能,后者很可能更好一些。这里也存在和 
                          PHP 版本中一样的字符串转义问题,所以使用 Html.htmlspecialchars() 
                          方法修改该问题。(见 清单 
                          7,为了看起来更清晰,稍微作了修改)。 
                        清单 7. 使用字符串逐块构建 XML 很简单 
                           
                           
                             				
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;
} | 
                           
                         
                        另一种创建 XML 的简单算法是 XMLParser 类的  createDocument() 
                          方法。这种算法的风格很容易让人想起 PHP 中的 SimpleXML 函数,首先创建一个空文档,然后向其中添加元素。可以创建所有类型的节点,并设置属性值。最后,标准的 
                          Java  toString() 方法生成对象的一个表示。您只需添加初始版本和编码行,就可以得到需要的字符串。(见 
                          清单 8,为了看起来更清晰,代码有所修改和删减)。 
                        清单 8. 创建 XML 的 XMLParser 方法类似于 PHP 
                        中的 SimpleXML 方法 
                           
                           
                             				
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();
}
 | 
                           
                         
                        
                        在 PHP 中处理 XML 是一个老问题,有很多的解决方案。但是,我认为,从清晰和简洁的角度看,没有哪种方案能比得上 
                          SimpleXML 。基本上,首先通过将 XML 文档提供给 simplexml_load_string() 
                          方法创建一个 PHP 对象,然后使用标准的 PHP 操作符遍历结果。属性变成数组中的元素(例如,清单 
                          9 展示了如何读取国家代码),而元素则可以作为一个数组(通过使用 children() 
                          方法)或作为对象的属性来访问。  
                        清单 9. SimpleXML 是目前在 PHP 中进行 XML 处理的最容易的方法 
                           
                           
                             
                              				
$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}')");
}
 | 
                           
                         
                        在 PHP 中处理 XML 还有许多种方法,可以从 参考资料 
                          小节找到相关的链接。  
                        
                        有很多方法可以使用 XML 作为 GWT 与 PHP 之间的桥梁,我只是略作探讨,不过,本文给出的方法应该足以让您迈出第一步。需要记住的要点是,GWT 
                          不止局限于它自己的 RPC 方法,还可以与 XML 友好共存,轻松生成和使用 XML 文档。  
                        
                        
                           
                            | 描述 | 
                            名字 | 
                            大小 | 
                            下载方法 | 
                           
                           
                            | 本文的 Java 源代码 | 
                            java_source_code.zip | 
                            4KB  | 
                              | 
                           
                           
                            | 本文的 PHP 源代码 | 
                            php_source_code.zip | 
                            4KB  | 
                              | 
                           
                         
                        
                        学习 
                        获得产品和技术 
                        讨论  |