O único propósito da API principal do Ajax (chamada XMLHttpRequest) é enviar solicitações HTTP para troca de dados entre o navegador da Web e o servidor. O código JavaScript executado em uma página da Web pode usar XMLHttpRequest para enviar os parâmetros de solicitação a um script do lado do servidor, como um Servlet ou uma página JSP. O Servlet/JSP chamador enviará de volta uma resposta que contém dados normalmente usados para atualizar a visualização do usuário sem atualizar a página inteira. Essa abordagem oferece vantagens exclusivas em termos de desempenho e usabilidade porque o tráfego de rede é reduzido e a UI da Web é quase tão utilizável quanto a GUI do desktop.
No entanto, desenvolver tal interface de usuário não é simples, pois é necessário implementar troca, validação e processamento de dados usando JavaScript no lado do cliente e Java (ou linguagem equivalente) no lado do servidor. Porém, em muitos casos, o esforço extra para construir uma interface baseada em Ajax vale a pena, considerando os benefícios que serão obtidos.
Neste artigo, apresentarei um dos principais métodos para transferência de dados entre clientes e servidores Ajax e compararei as diferenças entre o modelo de aplicativo Web tradicional e o modelo Ajax. Além disso, o artigo também explorará técnicas de processamento de dados no lado do servidor e no lado do cliente.
Primeiro, você aprenderá como usar JavaScript para codificar os parâmetros de um objeto de solicitação no lado do cliente. Você pode usar a chamada codificação de URL (a codificação padrão usada pelos navegadores da web) ou pode incluir os parâmetros de solicitação no documento XML. O servidor processará a solicitação e retornará uma resposta cujos dados também deverão ser codificados. Este artigo explora JavaScript Object Notation (JSON) e XML, que são as principais opções de formato de dados de resposta.
A maior parte deste artigo se concentrará em APIs relacionadas a XML comumente usadas em aplicativos Ajax. Do lado do cliente, a API XML é muito limitada, mas suficiente. Na maioria dos casos, todas as operações necessárias podem ser realizadas usando XMLHttpRequest. Além disso, o JavaScript pode ser usado para analisar documentos XML e serializar árvores DOM em um navegador da Web. No lado do servidor, existem muitas APIs e estruturas disponíveis para processamento de documentos XML. Este artigo descreve como executar tarefas básicas usando a API Java padrão para XML, que suporta XML Schema, XPath, DOM e muitos outros padrões.
Através deste artigo, você poderá aprender sobre as melhores técnicas e as APIs mais recentes para troca de dados em aplicações Ajax. O código de amostra envolvido está em três pacotes: util, model e feed. As classes no pacote utilitário fornecem métodos para análise XML, validação baseada em esquema, consulta baseada em XPath, serialização DOM e codificação JSON. O pacote de modelo contém modelos de dados de amostra que podem ser inicializados a partir de documentos XML e depois convertidos para o formato JSON. Há também um exemplo de esquema no diretório do modelo que pode ser usado para validação XML. As classes do pacote feed podem ser usadas para simular um feed de dados, que recupera informações via Ajax a cada 5 segundos para atualizar a página da Web. Este artigo explica como evitar vazamentos de memória em seu navegador da Web encerrando solicitações Ajax pendentes e excluindo o objeto XMLHttpRequest quando terminar de usá-lo.
Amostras JSP e JavaScript estão incluídas no diretório da web. ajaxUtil.js contém funções utilitárias para enviar solicitações Ajax, encerrar solicitações e tratar erros HTTP. Este arquivo também fornece utilitários JavaScript para codificação XML e URL, análise XML e serialização DOM. O arquivo ajaxCtrl.jsp atua como um controlador Ajax, recebendo cada solicitação Ajax, encaminhando parâmetros para o modelo de dados ou fornecendo processamento e, em seguida, retornando uma resposta Ajax. O restante dos arquivos da Web são exemplos que demonstram como usar esse método prático.
A maneira mais simplesde construir uma solicitação no lado do cliente
para enviar dados a um servidor Web é codificar a solicitação como uma string de consulta, que pode ser anexada à URL ou incluída no corpo da solicitação, dependendo do método HTTP usado. Se você precisar enviar estruturas de dados complexas, a melhor solução é codificar as informações em um documento XML. Abordarei os dois métodos nesta seção.
Codificar parâmetros de solicitação. Ao desenvolver aplicativos Web tradicionais, você não precisa se preocupar com a codificação dos dados do formulário porque o navegador Web faz isso automaticamente quando o usuário envia os dados. Entretanto, em um aplicativo Ajax, você mesmo deve codificar os parâmetros da solicitação. JavaScript fornece uma função muito útil escape(), que substitui quaisquer caracteres que não possam fazer parte da URL por %HH (onde HH é o código hexadecimal). Por exemplo, quaisquer caracteres de espaço em branco são substituídos por %20.
O download do código de amostra fornece uma função utilitária, buildQueryString(), que concatena parâmetros recuperados de um array, separando o nome e o valor de cada parâmetro por = e colocando o caractere & entre cada par nome-valor:
function buildQueryString(params) {
var consulta = "";
for (var i = 0; i < params.length; i++) {
consulta += (i > 0? "&": "")
+ escape(params[i].nome) + "="
+ escape(params[i].valor);
}
consulta de retorno;
}
Suponha que você queira codificar os seguintes parâmetros:
var someParams = [
{nome: "nome", valor: "John Smith" },
{nome: "e-mail", valor: " [email protected] " },
{nome:"telefone", valor: "(123) 456 7890" }
];
Uma chamada para buildQueryString(someParams) produzirá resultados contendo:
name=John%20Smith&[email protected]&phone=%28123%29%20456%207890
Se desejar usar o método GET, você deve anexar a consulta ao URL após o caractere ? Ao usar POST, o cabeçalho Content-Type deve ser definido como application/x-www-form-urlencoded via setRequestHeader(), e a string de consulta deve ser passada para o método send() de XMLHttpRequest, que enviará a solicitação HTTP para servidor.
Crie um documento XML. Usar strings para construir elementos a partir de seus atributos e dados é a maneira mais simples de criar documentos XML com JavaScript. Se você adotar esta solução, precisará de um método utilitário para escapar dos caracteres &, <, >, "e:
function escapeXML(content) {
if (conteúdo == indefinido)
retornar "";
if (!content.length || !content.charAt)
conteúdo = new String(conteúdo);
var resultado = "";
var comprimento = conteúdo.comprimento;
for (var i = 0; i < comprimento; i++) {
var ch = content.charAt(i);
mudar (ch) {
caso &:
resultado += "&";
quebrar;
caso <:
resultado += "<";
quebrar;
caso >:
resultado += ">";
quebrar;
caso ":
resultado += """;
quebrar;
caso \:
resultado += "'";
quebrar;
padrão:
resultado += ch;
}
}
resultado de retorno;
}
Para tornar a tarefa mais simples, alguns métodos utilitários adicionais são necessários, como:
function attribute(name, value) {
return " " + nome + "="" + escapeXML(valor) + """;
}
O exemplo a seguir cria um documento XML a partir de uma matriz de objetos com três propriedades: símbolo, ações e pagoPrice:
function buildPortfolioDoc(stocks) {
var xml = "<portfólio>";
for (var i = 0; i < stocks.length; i++) {
var estoque = ações[i];
xml += "<estoque";
xml += atributo("símbolo", estoque.symbol);
xml += atributo("ações", stock.shares);
xml += atributo("preçopago", estoque.preçopago);
xml+= "";
}
xml += "< /portfólio>";
retornar xml;
}
Se preferir trabalhar com DOM, você pode usar a API do seu navegador para analisar XML e serializar a árvore DOM. Com o IE, você pode criar um documento vazio com um novo ActiveXObject("Microsoft.XMLDOM"). O XML pode então ser analisado a partir de uma string ou URL usando os métodos loadXML() ou load() respectivamente. No caso do IE, cada nó possui um atributo chamado xml que permite obter uma representação XML do nó e de todos os seus nós filhos. Portanto, você pode analisar uma string XML, modificar a árvore DOM e serializar o DOM de volta para XML.
Os navegadores Firefox e Netscape permitem criar um documento vazio usando document.implementation.createDocument(...). Os nós DOM podem então ser criados usando createElement(), createTextNode(), createCDATASection(), etc. O navegador Mozilla também fornece duas APIs chamadas DOMParser e XMLSerializer. A API DOMParser contém os métodos parseFromStream() e parseFromString(). A classe XMLSerializer possui métodos correspondentes para serializar a árvore DOM: serializeToStream() e serializeToString().
A função a seguir analisa uma string XML e retorna um documento DOM:
function parse(xml) {
var dom;
tentar{
dom = novo ActiveXObject("Microsoft.XMLDOM");
dom.async = falso;
dom.loadXML(xml);
} pegar (erro) {
tentar{
var analisador = new DOMParser();
dom = analisador.parseFromString(xml, "texto/xml");
excluir analisador;
} pegar (erro2) {
se(depurar)
alert("A análise XML não é suportada.");
}
}
retornar dom;
}
A segunda função serializa um nó DOM e todos os seus nós filhos, retornando o XML como uma string:
function serialize(dom) {
varxml=dom.xml;
if (xml == indefinido) {
tentar{
var serializador = novo XMLSerializer();
xml = serializer.serializeToString(dom);
excluir serializador;
} pegar (erro) {
se(depurar)
alert("Serialização DOM não suportada.");
}
}
retornar xml;
}
Você também pode usar XMLHttpRequest como analisador ou serializador. Depois que uma resposta a uma solicitação Ajax é recebida do servidor, a resposta é analisada automaticamente. A versão de texto e a árvore DOM podem ser acessadas por meio das propriedades responseText e responseXML de XMLHttpRequest respectivamente. Além disso, a árvore DOM é serializada automaticamente quando passada para o método send().
Envie uma solicitação. Em um artigo anterior, apresentei a API XMLHttpRequest e uma função utilitária, sendHttpRequest(), que você pode encontrar no arquivo ajaxUtil.js no exemplo fornecido para download. Esta função usa quatro parâmetros (método HTTP, URL, uma matriz de parâmetros e um retorno de chamada), cria um objeto XMLHttpRequest, define suas propriedades e chama o método send(). Se um parâmetro de retorno de chamada for fornecido, a solicitação será enviada de forma assíncrona e a função de retorno de chamada será chamada após o recebimento da resposta. Caso contrário, a solicitação será enviada de forma síncrona e você poderá tratar a resposta assim que sendHttpRequest() retornar.
Como você pode ver, você precisa fazer algumas escolhas importantes ao usar XMLHttpRequest
. Qual método HTTP deve ser usado (GET ou POST)?
O formato usado para codificar parâmetros de solicitação (a codificação XML e URL foi discutida anteriormente neste artigo)
Se deve fazer a chamada de forma síncrona (aguardando uma resposta) ou assíncrona (usando um retorno de chamada). O formato da resposta, como XML, XHTML, HTML ou JavaScript Object Notation (JSON) (discutido posteriormente neste artigo). Digamos que você queira obter algumas informações sobre preços de ações de um feed de dados e atualizar as informações periodicamente sem intervenção do usuário. Neste caso, a solicitação HTTP deve ser enviada de forma assíncrona para que a interface do usuário não seja bloqueada enquanto as informações são recuperadas. O parâmetro de solicitação é uma matriz de símbolos que podem ser codificados na URL. Como o servidor pode estar sobrecarregado, você não deseja enviar documentos XML quando forem feitas solicitações frequentes. Como você está interessado apenas no preço mais recente das ações, quaisquer solicitações anteriores pendentes devem ser encerradas:
var ctrlURL = "ajaxCtrl.jsp";
var feedRequest = null;
function sendInfoRequest(símbolos, retorno de chamada) {
if(feedRequest)
abortarRequest(feedRequest);
var params = new Array();
for (var i = 0; i < símbolos.comprimento; i++)
parâmetros[i] = {
nome:"símbolo",
valor:símbolos[i]
};
feedRequest = sendHttpRequest(
"GET", ctrlURL, parâmetros, retorno de chamada);
}
Antes de chamar o método abort() do objeto de solicitação, a função abortRequest() (encontrada no arquivo ajaxUtil.js) define a propriedade onreadystatechange como um retorno de chamada que não faz nada. Além disso, é fundamental excluir o objeto de solicitação para evitar vazamentos de memória:
function abortRequest(request) {
function doNothing() {
}
request.onreadystatechange=doNothing;
request.abort();
excluir feedRequest;
}
Vamos considerar outro caso: ao transferir todos os dados do usuário para serem salvos no banco de dados, a solicitação deve ser enviada de forma síncrona, pois provavelmente você não deseja que o usuário a modifique enquanto o salvamento desses dados estiver em andamento. Neste caso, o formato XML é preferido porque muitas vezes é mais simples codificar o modelo de objeto no documento do que usar muitos parâmetros de string. Além disso, as solicitações para salvar dados são pouco frequentes e o servidor lida com a carga sem problemas. Um documento XML pode ser codificado como um parâmetro para que você possa acessá-lo em uma página JSP usando a sintaxe EL (${param.xml}). Aqui está a função que envia dados do modelo codificados em um documento XML:
function sendSaveRequest(xml) {
var params = [ {nome:"xml", valor:xml } ];
var saveRequest = sendHttpRequest("POST", ctrlURL, params);
if(salvarPedido)
excluir saveRequest;
}
Se precisar restaurar o modelo de objeto, você também pode enviar uma solicitação de forma síncrona para recuperar dados do servidor. Nesse caso, o servidor deve retornar uma resposta JSON para que você possa convertê-la facilmente em uma árvore de objetos JavaScript usando eval(loadRequest.responseText):
function sendLoadRequest() {
var modelo = nulo;
var loadRequest = sendHttpRequest("GET", ctrlURL);
if (carregarRequest) {
modelo = eval(loadRequest.responseText);
excluir loadRequest;
}
modelo de retorno;
}
As duas seções a seguir descrevem as operações normalmente executadas em documentos XML no servidor e como responder às solicitações Ajax.
Tratamento de solicitações no lado do servidor
O contêiner Servlet/JSP analisa cada solicitação HTTP e cria uma instância ServletRequest, que permite obter os parâmetros da solicitação por meio de getParameter() / getParameterValues() ou o corpo da solicitação por meio de getInputStream(). Em páginas JSP, esses parâmetros também podem ser obtidos usando a sintaxe EL (${param...} e ${paramValues...}). Observe que passar getParameter só é possível se o cliente Ajax usar uma função utilitária como buildQueryString() para codificar os dados no formato application/x-www-form-urlencoded (descrito na seção anterior deste artigo () ou $). {param...} para obter parâmetros de solicitação. Se você passar um documento XML ou árvore DOM para o método send() de XMLHttpRequest no lado do cliente, deverá usar o método getInputStream() de ServletRequest no lado do servidor.
Validação de dados. Um aplicativo web típico executa muitas operações de validação de dados. A maioria dos erros possíveis são bastante simples, como parâmetros de solicitação ausentes, formatos de números incorretos e assim por diante. Esses erros geralmente são causados pelo usuário que se esquece de inserir um valor para um elemento do formulário ou fornece um valor inválido. Frameworks Web como JSF e Oracle ADF Faces são muito bons para lidar com esses erros do usuário. Em aplicativos Ajax, esses erros podem ser detectados e tratados no lado do cliente usando JavaScript. Por exemplo, você pode usar isNaN(new Number(value)) para verificar se um valor numérico não é válido.
Por razões de segurança e confiabilidade, os dados devem ser revalidados no lado do servidor e as solicitações XML não devem ser consideradas bem formatadas. Os esquemas XML são uma ferramenta útil para validar solicitações complexas no lado do servidor. O download do código de amostra inclui uma classe chamada XMLUtil que fornece métodos para carregar e usar documentos de esquema. O trecho de código a seguir mostra como inicializar o SchemaFactory:
import javax.xml.*;
importar javax.xml.validation.*;
...
SchemaFactory estático protegido esquemaFactory;
estático {
esquemaFactory = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI);
esquemaFactory.setErrorHandler(newErrorHandler());
}
O método newErrorHandler() retorna um manipulador de erros SAX:
import org.xml.sax.*;
...
public static ErrorHandler newErrorHandler() {
retornar novo ErrorHandler() {
aviso de vazio público (SAXParseException e)
lança SAXException {
Logger.global.warning(e.getMessage());
}
erro de void público (SAXParseException e)
lança SAXException {
jogue e;
}
public void fatalError(SAXParseException e)
lança SAXException {
jogue e;
}
};
}
Você pode usar getResourceAsStream() para localizar e carregar um arquivo XSD em um diretório ou JAR especificado no CLASSPATH:
public static InputStream getResourceAsStream(String name)
lança IOException {
InputStream in = XMLUtil.class.getResourceAsStream(nome);
se (em == nulo)
lança nova FileNotFoundException(nome);
retornar;
}
Em seguida, use a instância SchemaFactory para obter o objeto Schema por meio do método newSchema():
import javax.xml.validation.*;
...
Esquema estático público newSchema (nome da string)
lança IOException, SAXException {
Esquema de esquema;
InputStream in = getResourceAsStream(nome);
tentar{
esquema = esquemaFactory.newSchema(novo StreamSource(in));
}finalmente{
in.close();
}
esquema de retorno;
}
Você também pode criar um objeto Oracle XMLSchema usando:
import oracle.xml.parser.schema.XMLSchema;
importar oracle.xml.parser.schema.XSDBuilder;
...
XMLSchema estático público newOracleSchema (nome da string)
lança IOException, SAXException {
Esquema XMLSchema;
InputStream in = getResourceAsStream(nome);
tentar{
Construtor XSDBuilder = novo XSDBuilder();
esquema = construtor.build(new InputSource(in));
} catch (Exceção e){
lançar nova SAXException(e);
}finalmente{
in.close();
}
esquema de retorno;
}
Em seguida, você precisa criar um DocumentBuilderFactory. Se uma implementação JAXP 1.1 for encontrada no CLASSPATH, o método setSchema() definido por JAXP 1.2 poderá lançar uma UnsupportedOperationException, caso em que a implementação JAXP 1.1 precisará ser substituída pela implementação JAXP 1.2 do Java SE 5.0. Neste caso, você ainda pode criar um objeto de esquema usando newOracleSchema() e configurá-lo através do método setAttribute():
import javax.xml.parsers.*;
importar oracle.xml.jaxp.JXDocumentBuilderFactory;
...
public static DocumentBuilderFactory newParserFactory(
String esquemaName) lança IOException, SAXException {
Analisador DocumentBuilderFactoryFactory
= DocumentBuilderFactory.newInstance();
tentar{
parserFactory.setSchema(newSchema(schemaName));
} catch (UnsupportedOperationException e) {
if (parserFactory instância de JXDocumentBuilderFactory) {
parserFactory.setAttribute(
JXDocumentBuilderFactory.SCHEMA_OBJECT,
newOracleSchema(nomedoesquema));
}
}
retornar analisadorFactory;
}
Em seguida, crie um objeto DocumentBuilder e utilize-o para validar e analisar o documento XML:
import javax.xml.parsers.*;
...
public static DocumentBuilder newParser(
analisador DocumentBuilderFactoryFactory)
lança ParserConfigurationException {
Analisador DocumentBuilder = parserFactory.newDocumentBuilder();
parser.setErrorHandler(newErrorHandler());
analisador de retorno;
};
Suponha que você queira validar um documento XML em relação ao exemplo de esquema portfólio.xsd:
< xsd:schema xmlns:xsd=" http://www.w3.org/2001/XMLSchema ">
< xsd:element name="portfolio" type ="portfolioType "
< xsd:complexType name="portfolioType">
<xsd:sequência>
<xsd:element name="estoque"
minOccurs="0" maxOccurs="ilimitado">
<xsd:complexType>
< xsd:nome do atributo="símbolo"
type="xsd:string" use="required"/>
< xsd:attribute name="compartilhamentos"
type="xsd:positivoInteger" use="required"/>
< xsd:attribute name="paidPrice"
type="xsd:decimal" use="required"/>
< /xsd:complexType>
< /xsd:elemento>
< /xsd:sequência>
</xsd:complexType>
</xsd:schema>
O método parsePortfolioDoc() da classe DataModel usa XMLUtil para validar e analisar os parâmetros xml e retornar um documento DOM:
private static final String SCHEMA_NAME
= "/ajaxapp/model/portfolio.xsd";
private static DocumentBuilderFactory parserFactory;
Documento estático privado parsePortfolioDoc (String xml)
lança IOException, SAXException,
ParserConfigurationException {
sincronizado (DataModel.class) {
if (parserFactory == nulo)
parserFactory = XMLUtil.newParserFactory(SCHEMA_NAME);
}
Analisador DocumentBuilder = XMLUtil.newParser(parserFactory);
InputSource in = new InputSource(new StringReader(xml));
retornar analisador.parse(in);
}
Agora que você tem uma árvore DOM, precisa obter os dados necessários para formar os nós DOM.
Extraia as informações necessárias. Você pode usar a API DOM ou uma linguagem de consulta (como XQuery ou XPath) para navegar na árvore DOM. Java fornece uma API padrão para XPath, que será usada posteriormente. A classe XMLUtil cria um XPathFactory com um método newXPath():
import javax.xml.xpath.*;
...
XPathFactory estático protegido xpathFactory;
estático {
xpathFactory = XPathFactory.newInstance();
}
public static XPath newXPath() {
retornar xpathFactory.newXPath();
}
Os métodos a seguir avaliam uma expressão XPath em um determinado contexto e retornam o valor resultante:
import javax.xml.xpath.*;
importar org.w3c.dom.*;
...
string estática pública evalToString (expressão de string,
Contexto do objeto) lança XPathExpressionException {
return (String) newXPath().evaluate(expressão, contexto,
XPathConstants.STRING);
}
public static boolean evalToBoolean (expressão de string,
Contexto do objeto) lança XPathExpressionException {
return ((Boolean) newXPath().evaluate(expressão, contexto,
XPathConstants.BOOLEAN)).booleanValue();
}
public static double evalToNumber (expressão de string,
Contexto do objeto) lança XPathExpressionException {
return ((Double) newXPath().evaluate(expressão, contexto,
XPathConstants.NUMBER)).doubleValue();
}
Nó estático público evalToNode (expressão de string,
Contexto do objeto) lança XPathExpressionException {
return (Nó) newXPath().evaluate(expressão, contexto,
XPathConstants.NODE);
}
public static NodeList evalToNodeList (expressão de string,
Contexto do objeto) lança XPathExpressionException {
return (NodeList) newXPath().evaluate(expressão, contexto,
XPathConstants.NODESET);
}
O método setData() do DataModel usa o método solucionador XPath para extrair informações do documento XML combinado:
public sincronizado void setData(String xml)
lança IOException, SAXException,
ParserConfigurationException,
XPathExpressionException{
tentar{
ArrayList stockList
= newArrayList();
Documento doc = parsePortfolioDoc(xml);
NodeList nodeList = XMLUtil.evalToNodeList(
"/portfólio/estoque", doc);
for (int i = 0; i <nodeList.getLength(); i++) {
Nó nó = nodeList.item(i);
Estoque StockBean = new StockBean();
estoque.setSymbol(
XMLUtil.evalToString("@symbol", nó));
estoque.setShares(
(int) XMLUtil.evalToNumber("@shares", node));
estoque.setPaidPrice(
XMLUtil.evalToNumber("@paidPrice", node));
stockList.add(estoque);
}
this.stockList = stockList;
} catch (Exceção e){
Logger.global.logp(Level.SEVERE, "DataModel", "setData",
e.getMessage(), e);
}
}
Assim que os dados estiverem disponíveis no modelo de dados do lado do servidor, eles poderão ser processados de acordo com os requisitos da aplicação. Então, você deve responder à solicitação do Ajax.
Gerando a resposta no lado do servidor
Retornar HTML como resposta a uma solicitação Ajax é a solução mais simples porque você pode construir a marcação usando a sintaxe JSP e o cliente Ajax simplesmente usa o elemento <div> ou <span> A propriedade innerHTML insere HTML. em algum lugar da página. Entretanto, é mais eficiente retornar dados ao cliente Ajax sem qualquer marcação de apresentação. Você pode usar o formato XML ou JSON.
Gere resposta XML. Java EE oferece muitas opções para a criação de documentos XML: gerados via JSP, criados a partir de uma árvore de objetos via JAXB ou gerados usando javax.xml.transform. O transformador no exemplo a seguir serializará uma árvore DOM:
import javax.xml.transform.*;
importar javax.xml.transform.dom.*;
importar javax.xml.transform.stream.*;
...
public static TransformerFactory serializerFctory;
estático {
serializerFctory = TransformerFactory.newInstance();
}
public static void serialize (nó do nó, saída OutputStream)
lança TransformerException {
Serializador do transformador = serializerFctory.newTransformer();
Propriedades serializerProps = new Propriedades();
serializerProps.put(OutputKeys.METHOD, "xml");
serializer.setOutputProperties(serializerProps);
Fonte fonte = novo DOMSource(node);
Resultado resultado = new StreamResult(out);
serializer.transform(fonte, resultado);
}
Existem tantas opções padrão e estruturas de origem de desenvolvimento para gerar XML no lado do servidor que a única coisa que você precisa fazer é escolher aquela que funciona para você. Porém, no cliente, a situação é muito diferente, pois o XML só pode ser analisado usando o DOM. Alguns navegadores também oferecem suporte a XPath e XSLT.
Em artigos anteriores sobre Ajax, você aprendeu como gerar XML via JSP e depois analisá-lo no cliente usando JavaScript e DOM. Outra solução é usar JSON em vez de XML como formato de dados para responder às solicitações Ajax. Conforme mencionado anteriormente, uma string JSON pode ser convertida em uma árvore de objetos JavaScript usando a função eval(). Isso é mais simples do que usar JavaScript para extrair informações da árvore DOM. Tudo que você precisa é de uma boa classe de utilitário que gere JSON no lado do servidor.
Codificação JSON. A classe JSONEncoder fornece métodos para codificação de literais, objetos e matrizes. Os resultados são armazenados em java.lang.StringBuilder:
package ajaxapp.util
public class JSONEncoder {;
buf StringBuilder privado;
public JSONEncoder() {
buf = new StringBuilder();
}
...
}
O método character() codifica um único caractere:
public void character(char ch) {
mudar (ch) {
caso \:
caso \":
caso \ :
buf.append( \ );
buf.append(ch);
quebrar;
caso :
buf.append( \ );
buf.append();
quebrar;
caso
:
buf.append( \ );
buf.append(
);
quebrar;
caso
:
buf.append( \ );
buf.append(
);
quebrar;
padrão:
se (ch >= 32 && ch < 128)
buf.append(ch);
outro{
buf.append( \ );
buf.append(u);
para (int j = 12; j >= 0; j-=4) {
int k = (((int) ch) >> j) & 0x0f;
int c = k < 10 ? + k :a + k - 10;
buf.append((char)c);
}
}
}
}
O método string() codifica a string inteira:
public void string(String str) {
comprimento interno = str.comprimento();
for (int i = 0; i < comprimento; i++)
personagem(str.charAt(i));
}
O método literal() codifica o literal JavaScript:
public void literal (valor do objeto) {
if (valor instância de String) {
buf.append(");
string((String) valor);
buf.append(");
} else if (valor instância do caractere) {
buf.append(\);
caractere(((personagem) valor).charValue());
buf.append(\);
} outro
buf.append(valor.toString());
}
O método comma() acrescenta um caractere de vírgula:
private void comma() {
buf.append(),;
}
O método deleteLastComma() removerá o último caractere de vírgula no final do buffer (se houver):
private void deleteLastComma() {
if (buf. comprimento() > 0)
if (buf.charAt(buf.length()-1) == ,)
buf.deleteCharAt(buf.length()-1);
}
O método startObject() acrescenta um caractere { para indicar o início de um objeto JavaScript:
public void startObject() {
buf.append({);
}
O método property() codifica propriedades JavaScript:
public void property(String name, Object value) {
buf.append(nome);
buf.append(:);
literal(valor);
vírgula();
}
O método endObject() acrescenta um caractere } para indicar o fim de um objeto JavaScript:
public void endObject() {
deleteLastComma();
buf.append());
vírgula();
}
O método startArray() anexa um caractere [ para indicar o início de um array JavaScript:
public void startArray() {
buf.append([);
}
O método element() codifica os elementos de um array JavaScript:
public void element(Object value) {
literal(valor);
vírgula();
}
O método endArray() acrescenta um caractere ] para indicar o fim de um array JavaScript:
public void endArray() {
deleteLastComma();
buf.append());
vírgula();
}
O método toString() retorna string JSON:
public String toString() {
deleteLastComma();
retornar buf.toString();
}
O método clear() limpa o buffer:
public void clear() {
buf.setLength(0);
}
DataModel usa a classe JSONEncoder para codificar os dados que mantém:
public sincronizado String getData() {
JSONEncoder json = new JSONEncoder();
json.startArray();
for (int i = 0; i < stockList.size(); i++) {
Estoque StockBean = stockList.get(i);
json.startObject();
json.property("símbolo", stock.getSymbol());
json.property("ações", stock.getShares());
json.property("pagoPrice", stock.getPaidPrice());
json.endObject();
}
json.endArray();
retornar json.toString();
}
Se os parâmetros de solicitação xml forem fornecidos, a página ajaxCtrl.jsp definirá os dados do modelo. Caso contrário, a página usará a expressão EL ${dataModel.data} para gerar a string JSON retornada por getData():
< %@ taglib prefix="c" uri=" http://java.sun.com/jsp/ jstl /núcleo "%>
...
<jsp:useBean id="dataModel" escopo="sessão"
class="ajaxapp.model.DataModel" />
< c:choose>
...
< c:quando test="${!empty param.xml}">
<c:set target="${dataModel}"
propriedade = "dados"
valor="${param.xml}" />
</c:quando>
<c:caso contrário>
${dataModel.data}
</c:caso contrário>
</c:escolher>
Este trabalho não está concluído porque o cliente Ajax deve processar os dados JSON.
Processando respostas no lado do cliente
Em um aplicativo da Web típico, você gera conteúdo no lado do servidor usando JSPs, estruturas da Web e bibliotecas de tags. Os aplicativos Ajax são ideais para essa situação porque estruturas Web como JavaServer Faces e Oracle ADF Faces são muito úteis na construção de aplicativos Ajax. No entanto, ainda existem diferenças significativas entre aplicativos Ajax e não Ajax. Ao usar Ajax, você deve processar os dados no lado do cliente e usar JavaScript para gerar conteúdo dinamicamente para fornecer os dados ao usuário.
Se você estiver usando o formato JSON para conversão de dados, é muito fácil converter texto em uma árvore de objetos usando a função eval() fornecida pelo JavaScript. Se você preferir usar XML, há muitas outras coisas que você precisa fazer, mas esse formato também tem suas vantagens. Por exemplo, XML pode ser usado por muitos tipos de clientes, enquanto JSON só é fácil de analisar em um ambiente JavaScript. Além disso, ao usar XML, os erros podem ser encontrados e corrigidos mais rapidamente, reduzindo assim o tempo de depuração.
Use JavaScript para acessar a árvore DOM. A API DOM do JavaScript é muito semelhante ao pacote org.w3c.dom do Java. A principal diferença é o acesso às propriedades. Em JavaScript você pode acessar propriedades diretamente, enquanto Java trata as propriedades como privadas e você precisa acessá-las através dos métodos get e set. Por exemplo, você pode obter o elemento raiz de um documento por meio de dom.documentElement.
O DOM é uma API de baixo nível que fornece acesso à estrutura do documento analisado. Por exemplo, você deseja ignorar comentários na maioria dos casos e pode não querer ter nós de texto adjacentes. Considere o seguinte exemplo simples:
var xml = "< element>da< !--comment-->ta&"
+ "< ![CDATA[cdata< /element>";
Você pode analisar a string XML acima usando a função utilitária apresentada anteriormente:
var dom = parse(xml);
Você pode encontrar o código para a função parse() em ajaxUtil.js; neste caso, a função retorna uma árvore DOM cujo elemento raiz contém um nó de texto, seguido por um comentário, outro nó de texto e um nó de dados de caractere. Caso queira incluir texto sem comentários, deve-se iterar sobre os elementos filhos do elemento, concatenando os valores dos nós de dados de texto e caractere (que possuem tipos 3 e 4 respectivamente):
var element = dom.documentElement;
var childNodes = element.childNodes;
var texto = "";
for (var i = 0; i < childNodes.length; i++)
if (childNodes[i].nodeValue) {
var tipo = childNodes[i].nodeType;
se (tipo == 3 || tipo == 4)
texto += childNodes[i].nodeValue;
}
Ao trabalhar com o DOM, você deve construir um pequeno conjunto de funções utilitárias para evitar lidar com esses detalhes de baixo nível.
Use JavaScript para gerar conteúdo dinâmico. Os navegadores da Web permitem acessar a estrutura DOM de uma página da Web por meio de objetos de documento. Por exemplo, você pode usar document.getElementById(...) para encontrar um elemento com muita facilidade. Você também pode criar novos elementos e nós de texto que podem ser inseridos em documentos existentes. No entanto, é mais simples construir HTML concatenando strings conforme mostrado abaixo:
function updateInfo(request) {
var ações = eval(request.responseText);
var tabela = "< borda da tabela = 1 cellpadding = 5>";
tabela += "<tr>";
tabela += "< th>Símbolo< /th>";
tabela += "<th>Tendência< /th>";
tabela += "<th>Último Preço< /th>";
tabela += "< /tr>";
for (var i = 0; i < ações.comprimento; i++) {
var compartilhar = ações[i];
var símbolo = escapeXML(share.symbol)
var tendência = share.trend > 0 ? "+" : "-";
var lastPrice = new Number(share.lastPrice).toFixed(2);
tabela += "<tr>";
tabela += "< td>" + símbolo + "< /td>";
tabela += "< td>" + tendência + "< /td>";
tabela += "<td>" + lastPrice + "< /td>";
tabela += "< /tr>";
}
tabela += "< /tabela>";
document.getElementById("tabela").innerHTML = tabela;
}
O HTML gerado pode ser inserido
em um elemento vazio, definindo a propriedade INNERHTML do objeto retornado por getElementById (), por exemplo:
<div id = "tabela">
</div>
O exemplo deste artigo usa a função updateInfo () como um retorno de chamada para lidar com respostas às solicitações do AJAX enviadas ao servidor através do sendinforeQuest no arquivo AjaxLogic.js. Se você deseja atualizar as informações a cada 5 segundos, poderá usar a função setInterval () do JavaScript:
var símbolos = [...];
setInterval ("sendinforeQuest (símbolos, updateInfo)", 5000);
Uma classe chamada DataFeed simula um feed do lado do servidor. A página Ajaxctrl.jsp chama o método getData () do feed, retornando a resposta como uma string json. No lado do cliente, a função updateInfo () analisa a sequência JSON usando avaliar (request.Responsetext), conforme mostrado no exemplo de código anterior.