Quando você pensa em Node.js, XML provavelmente não é a primeira coisa que vem à mente. Mas há casos em que você pode achar que precisa ler ou gravar XML a partir de um aplicativo Node.js. Não é por acaso que uma pesquisa por “XML” no npm retorna 3.400 + resultados ; há uma coleção considerável de pacotes relacionados a XML que se especializam em diferentes maneiras de trabalhar com XML.

Neste artigo, exploraremos alguns casos de uso de XML do mundo real usando alguns dos pacotes npm mais populares disponíveis , incluindo:

xml2js com express-xml-bodyparser libxmljs2 cheerio svgson

Observe que os exemplos de código neste artigo são para fins de demonstração. Um código de amostra elaborado e funcional está disponível em briandesousa/node-xml-demo .

Se desejar seguir as instruções neste artigo, você pode começar gerando um aplicativo Express com o destino express-generator . Selecione o mecanismo de visualização Pug (os exemplos de código de visualização são escritos como modelos Pug).

Recebendo XML sobre HTTP

XML-RPC e serviços da web SOAP costumavam ser o padrão para a troca de dados entre aplicativos, mas APIs JSON (ou seja, REST, GraphQL) têm todos, exceto serviços XML destronados .

Seja como for, ainda existem casos em que você pode precisar expor uma API baseada em XML para permitir que outros aplicativos alimentem dados XML em seu aplicativo. Felizmente, há muitos pacotes no npm que facilitam o consumo de dados XML.

Um dos pacotes XML mais populares é o xml2js , e uma das estruturas de aplicativos mais populares para Node.js é Express. Naturalmente, existe um pacote de middleware Express, express-xml-bodyparser , que vincula esses dois juntos. Vamos construir uma rota Express que pode receber XML.

Primeiro, precisamos instalar o pacote express-xml-bodyparser:

npm install express-xml-bodyparser

Em seguida, adicione o express-middleware xml-bodyparser para o aplicativo Express. Por padrão, o middleware analisará todas as solicitações recebidas onde se o cabeçalho Content-Type da solicitação for definido como text/xml.

//app.js var express=require (‘express’); var xmlparser=require (‘express-xml-bodyparser’); var app=express (); app.use (xmlparser ());//outro middleware e configurações Express

Adicione uma rota/xml2js/cliente que recebe uma solicitação XML:

//routes/xml2js.js router.post (‘/xml2js/customer’, function (req, res , próximo) {console.log (‘XML bruto:’+ req.rawBody); console.log (‘XML analisado:’+ JSON.stringify (req.body)); if (req.body.retrievecustomer) {var id=req.body.retrievecustomer.id; res.send (` $ {id} Bob Smith `);} else {res.status (400).send (‘XML inesperado recebido, falta ‘);}});

Para testar nossa rota, podemos escrever algum JavaScript do lado do cliente que envia uma solicitação XML:

//views/xml2js.pug fetch (‘/xml2js/customer’, {method:’POST’, headers: {‘Content-Type’:’text/xml’}, body:’ 39399444 ‘}).then (response=> {console.log (‘Response status:’+ response.status); return response.text ();}).then (responseText=> console.log (‘Texto de resposta:’+ responseText).catch (error=> console.log (‘Erro detectado:’+ erro));

A rota retorna uma resposta XML. Se você der uma olhada na saída do console do servidor, poderá ver o corpo da solicitação que o servidor recebeu:

XML bruto: 39399444 XML analisado: {“retrievecustomer”: {“id”: [“39399444”]}}

Espere um minuto-você notou algo diferente entre o XML bruto que foi recebido e o objeto que o middleware express-xml-bodyparser retornou?

O XML bruto tem a tag retrieveCustomer XML em caso de camelo, mas t A chave de recuperação do cliente no objeto JSON está em minúsculas. Isso está acontecendo porque o middleware express-xml-bodyparser está configurando o analisador xml2js, incluindo a definição de uma opção para converter todas as tags XML em minúsculas.

Queremos que as propriedades do objeto JSON correspondam exatamente às tags XML no pedido original. Felizmente, podemos especificar nossas próprias opções xml2js e substituir os padrões fornecidos pelo middleware.

Modifique o middleware xmlparser que foi adicionado ao aplicativo Express anteriormente para incluir um objeto de configuração com a opção normalizeTags definida como falsa:

//app.js app.use (xmlparser ({normalizeTags: false}));

Execute novamente o código do lado do cliente e visualize o log do console do servidor. Os nomes de tag agora devem corresponder entre o XML bruto e o objeto JSON analisado:

XML bruto: 39399444 XML analisado: {“retrieveCustomer”: {“id”: [“39399444”]}}

O pacote xml2js expõe várias outras opções que permitem que você personalize como o XML é analisado. Consulte xml2js em npm para uma lista completa de opções.

Validando XML com esquemas

Outro uso comum de XML é como um formato para troca de dados entre diferentes aplicativos, às vezes entre diferentes organizações. Normalmente, um esquema XML (XSD) é usado para definir a estrutura da mensagem XML que cada aplicativo deve esperar enviar e receber. Cada aplicativo valida os dados XML de entrada em relação ao esquema.

Os dados XML podem ser transmitidos entre aplicativos de várias maneiras. Por exemplo, os aplicativos podem receber XML por meio de uma conexão HTTP ou por meio de um arquivo simples salvo em um sistema de arquivos por meio de uma conexão SFTP.

Embora existam alguns pacotes npm disponíveis para trabalhar com XML, as opções são um pouco mais limitado quando você também requer a validação do esquema XML. Vamos dar uma olhada no pacote libxmljs2 , que oferece suporte à validação de esquema XML. Vamos escrever um código para carregar um esquema XML do sistema de arquivos do servidor e usá-lo para validar alguns dados XML de entrada.

Comece criando o esquema XML em um diretório de esquemas no diretório raiz de seu aplicativo:

Instale o pacote libxmljs2:

npm install lib4xml4js2

Crie uma nova rota/libxmljs2/validateSessionXml:

//routes/libxmljs2.js var express=require (‘express’); var roteador=express.Router (); var libxmljs=require (‘libxmljs2’); var fs=requer (‘fs’); var path=require (‘path’); var roteador=express.Router (); router.post (‘/libxmljs2/validateSessionXml’, (req, res, next)=> {var xmlData=req.body;//analisa dados XML de entrada var xmlDoc=libxmljs.parseXml (xmlData);//carrega o esquema XML de sistema de arquivos var xmlSchemaDoc=loadXmlSchema (‘session-info.xsd’);//validar dados XML em relação ao esquema var validationResult=xmlDoc.validate (xmlSchemaDoc);//retornar sucesso ou falha com erros de validação if (validationResult) {res.status (200).send (‘validação bem-sucedida’);} else {res.status (400).send (`$ {xmlDoc.validationErrors}`);}}); função loadXmlSchema (nome do arquivo) {var schemaPath=path.join (__ dirname,’..’,’schemas’, nome do arquivo); var schemaText=fs.readFileSync (schemaPath,’utf8′); return libxmljs.parseXml (xmlSchema); }

Dica : se você ainda estiver usando o middleware express-xml-bodyparser do exemplo anterior, pode ser necessário alterar a linha 2 para usar req.rawBody em vez de req.body para ignorar os xlm2js e acessar a string de solicitação bruta.

Na linha 14, a função parseXml () de libxmljs2 analisa o XML na solicitação. Ele retorna um objeto libxmljs.Document, que expõe uma função validate () que aceita outro libxmljs.Document contendo um esquema XML. A função de validação retornará verdadeiro ou uma string contendo uma lista de erros de validação. Nas linhas 23-27, retornamos uma resposta apropriada com base no resultado da validação.

A função loadXmlSchema () na linha 30 carrega um esquema XML do sistema de arquivos do servidor usando caminho Node.js padrão e módulos fs. Mais uma vez, estamos usando a função parseXml () para analisar o conteúdo do arquivo de esquema em um objeto libxmljs.Document. Esquemas XML são apenas documentos XML no final do dia.

Agora que implementamos uma rota, podemos escrever algum JavaScript simples do lado do cliente para testar nossa rota com uma solicitação XML válida:

//views/libxmljs2.pug fetch (‘/libxmljs2/validateSessionXml’, {method:’POST’, headers: {‘Content-Type’:’text/xml’}, body: ` 39399444 Bob Smith 343ldf0bk343bz43lddd `}). então (response=> resposta.text ()).then (response=> console.log (response)).catch (error=> console.log (‘Erro detectado:’+ erro));//saída do console: validação bem-sucedida

Também podemos enviar uma solicitação XML inválida e observar os erros de validação que são retornados:

//views/libxmljs2.pug fetch (‘/libxmljs2/validateSessionXml’, {método:’POST’, cabeçalhos: {‘Content-Type’:’text/xml’}, corpo: ` Bob Smith 343ldf0bk343bz43lddd `}).then (response=> response.text ()).then (response=> console.log (response)).catch (error=> console.log (‘Erro detectado:’+ erro));//saída do console: Erro: Elemento’customerName’: este elemento não é esperado. O esperado é (customerId).

As mensagens de erro retornadas da função validate () de libxmljs2 são bastante detalhadas; no entanto, o formato da mensagem de erro torna difícil analisar mensagens de erro individuais e mapeá-las para texto final amigável. Fora isso, há muito pouco código necessário para validar XML em um esquema.

Manipulando conteúdo HTML

E se seu aplicativo precisar manipular HTML? Ao contrário dos exemplos que abordamos até agora, o HTML não é tecnicamente compatível com a especificação XML.

Existem vários pacotes npm que se especializam em lidar com as nuances do HTML em comparação com o XML. Um deles é Cheerio . Vamos dar uma olhada em como podemos usar o Cheerio para ler, manipular e retornar um fragmento HTML.

Comece instalando o pacote Cheerio:

npm install cheerio

Crie um novo/cheerio/highlightTable route:

//routes/cheerio.js var cheerio=require (‘cheerio’); var express=require (‘express’); var roteador=express.Router (); router.post (‘/cheerio/highlightTable’, (req, res, next)=> {//decodificar HTML framgent no corpo da solicitação var decodedHtml=decodeURI (req.body.encodedHtml); tente {//analisar fragmento HTML var $=cheerio.load (decodedHtml);//use o seletor cheerio para localizar todas as células da tabela no HTML $ (‘td’). each (function () {tableCellText=$ (this).text (); tableCellNumber=parseFloat ( tableCellText); if (tableCellNumber) {//realça as células com base em seu valor numérico if (tableCellNumber>=0) {$ (this).prop (‘style’,’background-color: # 90ee90′);} else {$ (this).prop (‘style’,’background-color: # fa8072′);}}} catch (err) {return res.status (500).send ({error: err});}//apenas retornar o fragmento HTML que foi recebido na solicitação updatedHtml=$ (‘body’). html (); res.status (200).send ({encodedHtml: encodeURI (updatedHtml)});});

Na linha 8 , a propriedade da solicitação encodedHtml é decodificada usando a função decodeURI embutida. Precisamos codificar a string HTML ao enviá-la e recebê-la em uma solicitação JSON para evitar que caracteres especiais, como aspas duplas, entrem em conflito com a sintaxe JSON.

Na linha 12, a função load () de Cheerio é usado para analisar o fragmento HTML. Esta função retorna um objeto seletor com uma API que é quase idêntica à API principal do jQuery.

O seletor é então usado nas linhas 15–25 para localizar e extrair o texto dentro de todas as células da tabela no fragmento HTML. A função prop () fornecida pelo seletor é usada nas linhas 21 e 23 para modificar o fragmento HTML adicionando novos atributos de estilo.

Na linha 31, o seletor é usado para extrair o elemento do corpo do HTML fragmento e retorná-lo como uma string HTML. Mesmo que o fragmento de HTML que foi passado na solicitação não contenha tags ou externas, o Cheerio automaticamente envolve o fragmento de HTML em um documento HTML devidamente estruturado. Isso acontece quando a função load () é chamada.

Finalmente, a string HTML é codificada e enviada de volta ao cliente na linha 32.

Vamos escrever algum JavaScript do lado do cliente para teste a rota:

//views/cheerio.pug var sampleHtml=’

\ n’+’

\ n’+’

\ n’+’

\ n’+’

\ n’+’

\ n’+’

\ n’+’

\ n’+’

\ n’+’

\ n’+’

\ n’+’

\ n’+’

\ n’+’

Sammy Steakhouse Inc. -130,33
Depósito em caixa eletrônico 500,00
Depósito em cheque do governo 150,00

‘; fetch (‘/cheerio/highlightTable’, {método:’POST’, cabeçalhos: {‘Content-Type’:’application/json’}, body: JSON.stringify ({encodedHtml: encodeURI (sampleHtml)})}). then (response=> response.json ()).then (response=> {if (response.error) {console.log (‘Erro recebido:’+ response.error);} else {decodedHtml=decodeURI (response.encodedHtml ); console.log (‘HTML recebido:’+ decodedHtml);//resultado do console://

//

//

//

//

//

//

//

//

//

//

//

//

//

Sammy Steakhouse Inc. -130,33
Depósito em caixa eletrônico 500,00
Depósito de cheque do governo 150,00

}}).catch (error=> console.log (‘Erro detectado:’+ $ {erro}));

Na linha 22, o fragmento de HTML é codificado usando a função encodeURI embutida do navegador, semelhante a como o decodificamos no lado do servidor no exemplo anterior.

Você notará algumas diferenças entre o fragmento HTML original que está sendo enviado (linhas 3–16) e o fragmento HTML modificado que é retornado (demonstrado nos comentários nas linhas 32–46):

Algumas tags adicionais, como

foram adicionadas ao HTML que foi retornado. Essas tags foram adicionadas automaticamente pela função Cheerio load (). Cheerio é muito complacente quando se trata de carregar HTML: ele adicionará tags adicionais para garantir que o HTML seja um documento HTML compatível com os padrões. Os atributos de estilo foram adicionados a cada tag

conforme esperado

Gerando imagens SVG

Este exemplo é um pouco mais visual e divertido em comparação com os outros. Vamos manipular algumas imagens SVG modificando seu código-fonte XML.

Primeiro, uma introdução rápida sobre Scalable Vector Graphics (SVG). SVG é um formato de imagem baseado em XML suportado por todos os principais navegadores. O XML SVG é composto por uma série de elementos que definem diferentes tipos de formas. Estilos CSS podem ser incluídos em cada forma para definir sua aparência. Normalmente, você usaria uma ferramenta para gerar XML SVG em vez de codificá-lo manualmente, mas como as imagens SVG são apenas XML, a manipulação de imagens via JavaScript é possível.

Vamos criar uma rota que aceita três cores, carrega uma imagem SVG do sistema de arquivos do servidor, aplica as cores a certas formas na imagem e a retorna ao cliente para ser renderizada. Usaremos o pacote svgson para converter entre SVG XML e JSON para simplificar o código de que precisamos para escrever para manipular a imagem.

Comece instalando o pacote svgson:

npm install svgson

Crie uma nova rota/svgson/updateSVGImageColors:

//routes/svgson. js var express=require (‘express’); var fs=requer (‘fs’); var path=require (‘path’); var {parse: svgsonParse, stringify: svgsonStringify}=require (‘svgson’) var router=express.Router (); router.post (‘/svgson/updateSVGImageColors’, function (req, res, next) {//3 cores fornecidas para estilizar a imagem SVG var {color1, color2, color3}=req.body;//carregue a imagem SVG original do sistema de arquivos do servidor var svgImageXML=loadSVGImageXML (‘paint.svg’);//use svgson para converter o XML SVG em um objeto JSON svgsonParse (svgImageXML).then (json=> {//obtenha o contêiner de forma que contém o caminhos a serem manipulados gElement=json.children.find (elem=> elem.name==’g’&& elem.attributes.id==’g1727′);//atualizar estilos em formas de caminho específicas updatePathStyleById (gElement,’path995′,’preencher: # 000000′,’preencher:’+ color1); updatePathStyleById (gElement,’path996′,’preencher: #ffffff’,’preencher:’+ color2); updatePathStyleById (gElement,’path997′,’preencher: #ffffff’,’fill:’+ color3);//converter o objeto JSON de volta para XML SVG svgImageXML=svgsonStringify (json);//retornar res.status XML SVG ( 200).send (svgImageXML); }); }); função updatePathStyleById (containerElem, pathId, oldStyle, newStyle) {pathElem=containerElem.children.find (elem=> elem.attributes.id==pathId); pathElem.attributes.style=pathElem.attributes.style.replace (oldStyle, newStyle); } função loadSVGImageXML (nome do arquivo) {var svgImagePath=path.join (__ dirname,’..’,’public’,’imagens’, nome do arquivo); return fs.readFileSync (svgImagePath,’utf8′); }

Há muita coisa acontecendo neste código. Vamos dividir um pouco e destacar alguns conceitos-chave.

Nas linhas 5–8, os módulos parse e stringify são importados do pacote svgson. Esses nomes de módulos são bastante genéricos, mas podemos usar a desestruturação de objetos para dar-lhes nomes mais sucintos, como svgsonParse e svgsonStingify.

Na linha 17, a função loadSVGImageXML () é usada para carregar o conteúdo de um SVG predefinido. imagem do sistema de arquivos do servidor usando módulos Node.js nativos. A imagem que está sendo usada é paint.svg . Isso é o que parece para começar:

Imagem original paint.svg.

As linhas 20–36 são onde a mágica da manipulação de imagens acontece. O XML SVG é convertido em um objeto JSON. JavaScript padrão é usado para navegar na árvore de objetos para localizar três formas de caminho que desejamos manipular. Aqui está uma comparação da árvore de objetos JSON (lado esquerdo) com o XML SVG (lado direito) para ajudar a visualizá-lo. Observe que alguns elementos foram removidos por brevidade.

Árvore de objetos SVG JSON em comparação com SVG XML.

A função auxiliar updatePathStyleById () chamada nas linhas 27–29 localiza uma forma de caminho por seu ID e substitui seu estilo de preenchimento por um novo estilo de preenchimento construído usando as cores fornecidas na solicitação.

O SVG O objeto JSON é convertido de volta em uma string SVG XML na linha 32 e retornado ao cliente na linha 35.

Vamos escrever algum JavaScript do lado do cliente para testar a rota:

//views/svgson.pug fetch (‘/svgson/updateSVGImageColors’, {method:’POST’, headers: {‘Content-Type’:’application/json’}, body: JSON.stringify ({color1:’# FF0000′, color2:’# 00FF00′, color3:’# 0000FF’})}).then (response=> response.text ()).then (svgImageXml=> console.log (svgImageXml)).catch (error=> console.log (‘Erro detectado:’+ erro));

Se renderizássemos o XML SVG retornado, seria assim:

Imagem paint.svg atualizada.

Resumindo: você já teve XML suficiente?

Cobrimos alguns usos comuns do XML, que vão desde a chata troca de dados baseados em XML até a manipulação de imagens SVG. Aqui está um resumo dos pacotes npm que vimos e os principais recursos que os diferenciam.

pacote npm Principais recursos xml2js Pode realizar conversão bidirecional entre XML e JavaScript Expõe várias opções que podem ser usadas para alterar como o XML é analisado. Pares bem com Express usando o middleware express-xml-bodyparser libxmljs2 Pode analisar e validar XML em relação a esquemas XML Cheerio Especializado em análise e manipulação de HTML Pode ser usado para carregar documentos HTML ou XML. Indulgente por design; adicionará tags HTML ausentes para garantir que o HTML seja válido svgson Pode realizar conversão bidirecional entre imagens SVG (XML) e JSON torna mais fácil manipular uma imagem SVG com JavaScript