Qual analisador de XML devo usar em C ++? [fechadas]

344

Tenho documentos XML que preciso analisar e / ou preciso criar documentos XML e gravá-los em texto (arquivos ou memória). Como a biblioteca padrão C ++ não possui uma biblioteca para isso, o que devo usar?

Nota: Esta pretende ser uma pergunta definitiva ao estilo C ++ - FAQ para isso. Então, sim, é uma duplicata dos outros. Simplesmente não me apropriei dessas outras perguntas porque elas tendiam a pedir algo um pouco mais específico. Esta questão é mais genérica.

Nicol Bolas
fonte
Gosto do tiCpp code.google.com/p/ticpp , os documentos ainda não são ótimos (ainda?), Mas eu amo a biblioteca, um bom código limpo.
Eu escrevi meu próprio github.com/igagis/mikroxml
igagis

Respostas:

679

Assim como nos contêineres de biblioteca padrão, qual biblioteca você deve usar depende de suas necessidades. Aqui está um fluxograma conveniente:

insira a descrição da imagem aqui

Portanto, a primeira pergunta é a seguinte: O que você precisa?

Preciso de total conformidade com XML

OK, então você precisa processar o XML. Não brinquedo XML, XML real . Você precisa ler e gravar toda a especificação XML, não apenas os bits mais baixos e fáceis de analisar. Você precisa de Namespaces, DocTypes, substituição de entidade, os trabalhos. A especificação XML do W3C, na sua totalidade.

A próxima pergunta é: sua API precisa estar em conformidade com DOM ou SAX?

Preciso de conformidade exata com DOM e / ou SAX

OK, então você realmente precisa que a API seja DOM e / ou SAX. Ele não pode ser apenas um analisador push no estilo SAX ou um analisador retido no estilo DOM. Ele deve ser o DOM real ou o SAX real, na extensão permitida pelo C ++.

Você escolheu:

Xerces

Essa é sua escolha. É praticamente o único analisador / gravador XML de C ++ que possui total (ou o mais próximo que o C ++ permite) conformidade com DOM e SAX. Ele também possui suporte ao XInclude, suporte ao esquema XML e uma infinidade de outros recursos.

Não possui dependências reais. Ele usa a licença Apache.

Não ligo para a conformidade com DOM e / ou SAX

Você escolheu:

LibXML2

O LibXML2 oferece uma interface no estilo C (se isso realmente o incomoda, vá usar o Xerces), embora a interface seja pelo menos um pouco baseada em objetos e facilmente envolvida. Ele fornece muitos recursos, como suporte ao XInclude (com retornos de chamada para que você possa saber de onde o arquivo é obtido), um reconhecedor XPath 1.0, suporte para RelaxNG e Schematron (embora as mensagens de erro deixem muito a desejar) e assim por diante.

Ele depende do iconv, mas pode ser configurado sem essa dependência. Embora isso signifique que você terá um conjunto mais limitado de possíveis codificações de texto que ele pode analisar.

Ele usa a licença do MIT.

Não preciso de conformidade total com XML

OK, então a total conformidade com XML não importa para você. Seus documentos XML estão totalmente sob seu controle ou são garantidos para usar o "subconjunto básico" do XML: sem espaços para nome, entidades, etc.

Então, o que importa para você? A próxima pergunta é: Qual é a coisa mais importante para você no seu trabalho XML?

Desempenho máximo de análise de XML

Seu aplicativo precisa pegar o XML e transformá-lo em estruturas de dados C ++ o mais rápido possível.

Você escolheu:

RapidXML

Esse analisador XML é exatamente o que diz no estanho: XML rápido. Nem sequer trata de puxar o arquivo para a memória; como isso acontece depende de você. O que ele lida é analisar isso em uma série de estruturas de dados C ++ que você pode acessar. E faz isso o mais rápido possível para verificar o arquivo byte a byte.

Claro, não existe almoço grátis. Como a maioria dos analisadores XML que não se importam com a especificação XML, o Rapid XML não toca em espaços para nome, DocTypes, entidades (com exceção das entidades de caractere e das 6 XML básicas) e assim por diante. Então, basicamente nós, elementos, atributos e outros.

Além disso, é um analisador no estilo DOM. Portanto, é necessário que você leia todo o texto. No entanto, o que ele não faz é copiar esse texto (geralmente). A maneira como o RapidXML obtém a maior parte de sua velocidade é consultando as strings no local . Isso requer mais gerenciamento de memória de sua parte (você deve manter essa string ativa enquanto o RapidXML estiver olhando para ela).

O DOM do RapidXML é básico. Você pode obter valores de sequência para as coisas. Você pode procurar atributos por nome. É sobre isso. Não há funções de conveniência para transformar atributos em outros valores (números, datas, etc.). Você acabou de pegar as cordas.

Outra desvantagem do RapidXML é que é doloroso escrever XML. Exige que você faça muita alocação de memória explícita dos nomes de cadeias para criar seu DOM. Ele fornece um tipo de buffer de cadeia, mas isso ainda exige muito trabalho explícito do seu lado. Certamente é funcional, mas é difícil de usar.

Ele usa a licença do MIT. É uma biblioteca somente de cabeçalho sem dependências.

Eu me importo com o desempenho, mas não tanto assim

Sim, o desempenho é importante para você. Mas talvez você precise de algo um pouco menos básico. Talvez algo que possa lidar com mais Unicode ou não exija muito gerenciamento de memória controlado pelo usuário. O desempenho ainda é importante, mas você deseja algo um pouco menos direto.

Você escolheu:

PugiXML

Historicamente, isso serviu de inspiração para o RapidXML. Mas os dois projetos divergiram, com o Pugi oferecendo mais recursos, enquanto o RapidXML está totalmente focado na velocidade.

O PugiXML oferece suporte à conversão Unicode; portanto, se você possui alguns documentos UTF-16 e deseja lê-los como UTF-8, o Pugi fornecerá. Ele ainda tem uma implementação XPath 1.0, se você precisar desse tipo de coisa.

Mas Pugi ainda é bastante rápido. Como o RapidXML, ele não possui dependências e é distribuído sob a licença MIT.

Lendo documentos enormes

Você precisa ler documentos que são medidos em gigabytes de tamanho. Talvez você os esteja recebendo do stdin, sendo alimentado por outro processo. Ou você está lendo-os de arquivos enormes. Como queiras. O ponto é que você precisa não ter que ler o arquivo inteiro na memória de uma só vez para processá-lo.

Você escolheu:

LibXML2

A API no estilo SAX do Xerces funcionará nessa capacidade, mas o LibXML2 está aqui porque é um pouco mais fácil de trabalhar. Uma API no estilo SAX é uma API push: ela começa a analisar um fluxo e apenas dispara os eventos que você precisa capturar. Você é forçado a gerenciar contexto, estado e assim por diante. O código que lê uma API no estilo SAX é muito mais espalhado do que se poderia esperar.

O xmlReaderobjeto do LibXML2 é uma API pull. Você pede para ir para o próximo nó ou elemento XML; você não é informado. Isso permite que você armazene o contexto como achar melhor, para lidar com diferentes entidades de uma maneira muito mais legível no código do que um monte de retornos de chamada.

Alternativas

Expatriados

Expat é um analisador C ++ conhecido que usa uma API pull-parser. Foi escrito por James Clark.

Seu status atual está ativo. A versão mais recente é 2.2.9, que foi lançada em (2019-09-25).

LlamaXML

É uma implementação de uma API no estilo StAX. É um analisador de pull, semelhante ao xmlReaderanalisador do LibXML2 .

Mas não é atualizado desde 2005. Mais uma vez, Caveat Emptor.

Suporte XPath

XPath é um sistema para consultar elementos dentro de uma árvore XML. É uma maneira prática de nomear efetivamente um elemento ou coleção de elementos por propriedades comuns, usando uma sintaxe padronizada. Muitas bibliotecas XML oferecem suporte ao XPath.

Existem efetivamente três opções aqui:

  • LibXML2 : Fornece suporte completo ao XPath 1.0. Novamente, é uma API C, portanto, se isso lhe incomoda, existem alternativas.
  • PugiXML : Ele também vem com o suporte ao XPath 1.0. Como acima, é mais uma API C ++ que LibXML2, então você pode se sentir mais confortável com ela.
  • TinyXML : Ele não vem com suporte para XPath, mas existe a biblioteca TinyXPath que o fornece. O TinyXML está passando por uma conversão para a versão 2.0, que altera significativamente a API, portanto, o TinyXPath pode não funcionar com a nova API. Como o próprio TinyXML, o TinyXPath é distribuído sob a licença zLib.

Apenas comece o trabalho

Portanto, você não se importa com a correção de XML. O desempenho não é um problema para você. Streaming é irrelevante. Tudo o que você deseja é algo que coloque XML na memória e permita que você o cole novamente no disco. O que você importa é API.

Você deseja um analisador XML que seja pequeno, fácil de instalar, trivial de usar e pequeno o suficiente para ser irrelevante para o tamanho do seu eventual executável.

Você escolheu:

TinyXML

Coloquei o TinyXML neste slot porque é tão simples de usar quanto os analisadores XML. Sim, é lento, mas é simples e óbvio. Possui muitas funções de conveniência para converter atributos e assim por diante.

Escrever XML não é problema no TinyXML. Você apenas newcoloca alguns objetos, os anexa, envia o documento para a std::ostreame todos ficam felizes.

Também existe um ecossistema construído em torno do TinyXML, com uma API mais amigável ao iterador e até mesmo uma implementação XPath 1.0 em camadas.

O TinyXML usa a licença zLib, que é mais ou menos a Licença MIT com um nome diferente.

Nicol Bolas
fonte
6
Isso se parece um pouco com um copiar e colar. Você pode vincular o documento de origem?
Joel
28
@ Joel: muitas vezes quando alguém responde sua própria pergunta com um bom post longo, é porque está seguindo o espírito do conselho de Jeff - especialmente porque o que parece ser uma pergunta mais ou menos assim pode ser fechado antes que uma boa resposta possa ser publicado, se a pessoa estiver escrevendo a resposta naquele momento. Dedicando um tempo para preparar uma resposta antes que ele fizesse a pergunta :) Nicol está oferecendo a todos nós um excelente candidato para Fechar-> Perguntas duplicadas no futuro.
22612 sarnold
28
@ Joel: Receio não poder. Era apenas um documento temporário do qual copiei no Notepad ++. Eu nunca salvou, então eu não posso ligá-lo a ele;)
Nicol Bolas
6
Vale a pena mencionar a versão mais recente do TinyXML: O TinyXML-2 usa uma API semelhante ao TinyXML-1 e os mesmos casos de teste avançados. Mas a implementação do analisador é completamente reescrita para torná-lo mais apropriado para uso em um jogo. Ele usa menos memória, é mais rápido e usa muito poucas alocações de memória.
johnbakers
6
Gosto dessa pergunta e resposta, mas acho muito parcial para o Unix. Nenhuma menção ao MSXML e XmlLite? Se a portabilidade multi-paltform é o seu motivo para excluí-las, isso deve ser claramente mencionado na pergunta e resposta. (Caso contrário, algumas pessoas podem acabar escolhendo por exemplo libxml2 para um projeto somente para Windows, que está pedindo para dores de cabeça que poderia ter sido facilmente evitados.)
Scrontch
17

Há outra abordagem para lidar com XML que você pode considerar, chamada ligação de dados XML. Especialmente se você já possui uma especificação formal do seu vocabulário XML, por exemplo, no Esquema XML.

A ligação de dados XML permite que você use XML sem realmente executar qualquer análise ou serialização de XML. Um compilador de ligação de dados gera automaticamente todo o código de baixo nível e apresenta os dados analisados ​​como classes C ++ que correspondem ao domínio do aplicativo. Em seguida, você trabalha com esses dados chamando funções e trabalhando com tipos C ++ (int, double, etc) em vez de comparar seqüências de caracteres e analisar texto (que é o que você faz com APIs de acesso XML de baixo nível, como DOM ou SAX).

Veja, por exemplo, uma implementação de ligação de dados XML de código aberto que eu escrevi, CodeSynthesis XSD e, para uma versão mais leve e sem dependência, CodeSynthesis XSD / e .

Boris Kolpackov
fonte
13
Não me importo com o post, mas a política da SO afirma que, se você sugerir algo que escreveu, deve mencionar que o escreveu, no interesse da divulgação completa.
Nicol Bolas
@ Nicol eu editei na resposta.
JBentley
Talvez seja útil esta lista mas não consegui descobrir quem são os autores dessa lista (sem divulgação pública, não vejo se as descrições e classificações são significativas). Talvez se possa observar o grupo de trabalho de ligação de dados do W3C, que lista várias ferramentas de ligação de dados que são de domínio público e foram usadas para testes e relatórios (divulgação completa: não sou afiliado ao CodeSynthesis, ajudei o gsoap listado no W3C Ferramentas).
Dr. Alex RE
1

Outra observação sobre o Expat: vale a pena examinar o trabalho dos sistemas embarcados. No entanto, a documentação que você provavelmente encontrará na Web é antiga e está errada. O código fonte, na verdade, possui comentários bastante detalhados no nível da função, mas será necessário examinar atentamente.

ponto de interrupção
fonte
0

OK então. Eu criei uma nova, já que nenhuma lista não era estatística para minhas necessidades.

Benefícios:

  1. API de streaming de analisador de pull no nível baixo ( como Java StAX )
  2. Exceções e modos RTTI de suporte
  3. Limite para uso de memória, suporte para arquivos grandes (testado com arquivo XMark de 100 mib , a velocidade depende do hardware)
  4. Suporte a UNICODE e detecção automática para codificação da fonte de entrada
  5. API de alto nível para leitura em estruturas / POCO
  6. API de metaprogramação para escrever e gerar XSD a partir de estruturas / POCO com suporte para estrutura xml (atributos e tags de aninhamento) (a geração XSD precisa de RTTI, mas pode ser usada apenas na depuração para fazer isso uma vez)
  7. C ++ 11 - GCC e VC ++ 15+

Desvantagens:

  1. Validação DTD e XSD ainda não fornecida
  2. Obtenção de XML / XSD por HTTP / HTTPS em andamento, ainda não concluída
  3. Nova biblioteca

Página inicial do projeto

Victor Gubin
fonte
11
Você poderia adicionar referências?
Vadim Peretokin
-1

Na Secured Globe , Inc., usamos rapidxml . Tentamos todos os outros, mas o quickxml parece ser a melhor escolha para nós.

Aqui está um exemplo:

 rapidxml::xml_document<char> doc;
    doc.parse<0>(xmlData);
    rapidxml::xml_node<char>* root = doc.first_node();

    rapidxml::xml_node<char>* node_account = 0;
    if (GetNodeByElementName(root, "Account", &node_account) == true)
    {
        rapidxml::xml_node<char>* node_default = 0;
        if (GetNodeByElementName(node_account, "default", &node_default) == true)
        {
            swprintf(result, 100, L"%hs", node_default->value());
            free(xmlData);
            return true;
        }
    }
    free(xmlData);
Michael Haephrati
fonte