在Ajax 应用程序中实现数据之间交换
出于安全和可靠性的考虑,应当在服务器端对数据进行重新验证,而不应想当然地认为 XML 请求格式设置正确。XML 模式是在服务器端验证复杂请求的有用工具。示例代码下载中包含了一个名为 XMLUtil 的类,它提供用于加载和使用模式文档的方法。以下代码段显示了如何初始化 SchemaFactory:
import javax.xml.*;
import javax.xml.validation.*;
...
protected static SchemaFactory schemaFactory;
static {
schemaFactory = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setErrorHandler(newErrorHandler());
}
The newErrorHandler() method returns a SAX error handler:
import org.xml.sax.*;
...
public static ErrorHandler newErrorHandler() {
return new ErrorHandler() {
public void warning(SAXParseException e)
throws SAXException {
Logger.global.warning(e.getMessage());
}
public void error(SAXParseException e)
throws SAXException {
throw e;
}
public void fatalError(SAXParseException e)
throws SAXException {
throw e;
}
};
}
可以使用 getResourceAsStream() 查找并加载某个目录中的 XSD 文件或 CLASSPATH 中指定的 JAR:
public static InputStream getResourceAsStream(String name)
throws IOException {
InputStream in = XMLUtil.class.getResourceAsStream(name);
if (in == null)
throw new FileNotFoundException(name);
return in;
}
然后,使用 SchemaFactory 实例通过 newSchema() 方法获取 Schema 对象:
import javax.xml.validation.*;
...
public static Schema newSchema(String name)
throws IOException, SAXException {
Schema schema;
InputStream in = getResourceAsStream(name);
try{
schema = schemaFactory.newSchema(new StreamSource(in));
}finally{
in.close();
}
return schema;
}
您还可以使用以下方法创建 Oracle XMLSchema 对象:
import oracle.xml.parser.schema.XMLSchema;
import oracle.xml.parser.schema.XSDBuilder;
...
public static XMLSchema newOracleSchema(String name)
throws IOException, SAXException {
XMLSchema schema;
InputStream in = getResourceAsStream(name);
try{
XSDBuilder builder = new XSDBuilder();
schema = builder.build(new InputSource(in));
} catch (Exception e){
throw new SAXException(e);
}finally{
in.close();
}
return schema;
}
接下来,您需要创建一个 DocumentBuilderFactory。如果在 CLASSPATH 中找到的是 JAXP 1.1 实现,则由 JAXP 1.2 定义的 setSchema() 方法可能会抛出 UnsupportedOperationException,此时需要将 JAXP 1.1 实现替换为 Java SE 5.0 的 JAXP 1.2 实现。在这种情况下,您仍可使用 newOracleSchema() 创建模式对象,并通过 setAttribute()方法对其进行设置:
import javax.xml.parsers.*;
import oracle.xml.jaxp.JXDocumentBuilderFactory;
...
public static DocumentBuilderFactory newParserFactory(
String schemaName) throws IOException, SAXException {
DocumentBuilderFactory parserFactory
= DocumentBuilderFactory.newInstance();
try{
parserFactory.setSchema(newSchema(schemaName));
} catch (UnsupportedOperationException e) {
if (parserFactory instanceof JXDocumentBuilderFactory) {
parserFactory.setAttribute(
JXDocumentBuilderFactory.SCHEMA_OBJECT,
newOracleSchema(schemaName));
}
}
return parserFactory;
}
然后,创建一个 DocumentBuilder 对象,并使用该对象验证和分析 XML 文档:
import javax.xml.parsers.*;
...
public static DocumentBuilder newParser(
DocumentBuilderFactory parserFactory)
throws ParserConfigurationException {
DocumentBuilder parser = parserFactory.newDocumentBuilder();
parser.setErrorHandler(newErrorHandler());
return parser;
};
假设您要根据 portfolio.xsd 模式示例验证 XML 文档:
< xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
< xsd:element name="portfolio" type="portfolioType"
< xsd:complexType name="portfolioType">
< xsd:sequence>
< xsd:element name="stock"
minOccurs="0" maxOccurs="unbounded">
< xsd:complexType>
< xsd:attribute name="symbol"
type="xsd:string" use="required"/>
< xsd:attribute name="shares"
type="xsd:positiveInteger" use="required"/>
< xsd:attribute name="paidPrice"
type="xsd:decimal" use="required"/>
< /xsd:complexType>
< /xsd:element>
< /xsd:sequence>
< /xsd:complexType>
< /xsd:schema>
DataModel 类的 parsePortfolioDoc() 方法使用 XMLUtil 验证和分析 xml 参数,并返回一个 DOM 文档:
private static final String SCHEMA_NAME
= "/ajaxapp/model/portfolio.xsd";
private static DocumentBuilderFactory parserFactory;
private static Document parsePortfolioDoc(String xml)
throws IOException, SAXException,
ParserConfigurationException {
synchronized (DataModel.class) {
if (parserFactory == null)
parserFactory = XMLUtil.newParserFactory(SCHEMA_NAME);
}
DocumentBuilder parser = XMLUtil.newParser(parserFactory);
InputSource in = new InputSource(new StringReader(xml));
return parser.parse(in);
}
现在,您拥有了一个 DOM 树,接下来要获取形成 DOM 节点所需的数据。
提取所需信息。您可以使用 DOM API 或查询语言(如 XQuery 或 XPath)来浏览 DOM 树。Java 为 XPath 提供了标准的 API,后面会用到。XMLUtil 类创建一个具有 newXPath() 方法的 XPathFactory:
import javax.xml.xpath.*;
...
protected static XPathFactory xpathFactory;
static {
xpathFactory = XPathFactory.newInstance();
}
public static XPath newXPath() {
return xpathFactory.newXPath();
}
以下方法在给定的上下文中求解 XPath 表达式,返回结果值:
import javax.xml.xpath.*;
import org.w3c.dom.*;
...
public static String evalToString(String expression,
Object context) throws XPathExpressionException {
return (String) newXPath().evaluate(expression, context,
XPathConstants.STRING);
}
public static boolean evalToBoolean(String expression,
Object context) throws XPathExpressionException {
return ((Boolean) newXPath().evaluate(expression, context,
XPathConstants.BOOLEAN)).booleanValue();
}
public static double evalToNumber(String expression,
Object context) throws XPathExpressionException {
return ((Double) newXPath().evaluate(expression, context,
XPathConstants.NUMBER)).doubleValue();
}
public static Node evalToNode(String expression,
Object context) throws XPathExpressionException {
return (Node) newXPath().evaluate(expression, context,
XPathConstants.NODE);
}
public static NodeList evalToNodeList(String expression,
Object context) throws XPathExpressionException {
return (NodeList) newXPath().evaluate(expression, context,
XPathConstants.NODESET);
}
DataModel 的 setData() 方法使用 XPath 求解方法从组合 XML 文档提取信息:
public synchronized void setData(String xml)
throws IOException, SAXException,
ParserConfigurationException,
XPathExpressionException {
try{
ArrayList stockList
= new ArrayList();
Document doc = parsePortfolioDoc(xml);
NodeList nodeList = XMLUtil.evalToNodeList(
"/portfolio/stock", doc);
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
StockBean stock = new StockBean();
stock.setSymbol(
XMLUtil.evalToString("@symbol", node));
stock.setShares(
(int) XMLUtil.evalToNumber("@shares", node));
stock.setPaidPrice(
XMLUtil.evalToNumber("@paidPrice", node));
stockList.add(stock);
}
this.stockList = stockList;
} catch (Exception e){
Logger.global.logp(Level.SEVERE, "DataModel", "setData",
e.getMessage(), e);
}
}
一旦服务器端的数据模型中具备了数据,就可根据应用程序的要求对其进行处理了。然后,您必须响应 Ajax 请求。
在服务器端生成响应
将 HTML 作为 Ajax 请求的响应而返回是一种最简单的解决方案,这是因为您可以使用 JSP 语法构建标记,而 Ajax 客户端只需使用 < div> 或 < span> 元素的 innerHTML 属性在页面某处插入 HTML。但是,向 Ajax 客户端返回不带任何表示标记的数据则更为有效。您可以使用 XML 格式或 JSON。




