it-swarm.dev

Como analisar XML inválido (incorreto / mal formado)?

Atualmente, estou trabalhando em um recurso que envolve a análise de XML que recebemos de outro produto. Decidi executar alguns testes em relação a alguns dados reais do cliente e parece que o outro produto está permitindo a entrada de usuários que devem ser considerados inválidos. De qualquer forma, ainda tenho que tentar descobrir uma maneira de analisá-lo. Estamos usando javax.xml.parsers.DocumentBuilder E estou recebendo um erro na entrada que se parece com o seguinte.

<xml>
  ...
  <description>Example:Description:<THIS-IS-PART-OF-DESCRIPTION></description>
  ...
</xml>

Como você pode ver, a descrição tem o que parece ser uma tag inválida dentro dela (<THIS-IS-PART-OF-DESCRIPTION>). Agora, essa tag de descrição é conhecida por ser uma folha e não deve ter nenhuma tag aninhada dentro dela. Independentemente disso, isso ainda é um problema e gera uma exceção em DocumentBuilder.parse(...)

Eu sei que esse XML é inválido, mas é previsivelmente inválido. Alguma idéia de como analisar essa entrada?

16
jvhashe

Esse "XML" é pior que inválido - é não está bem formado; veja XML bem formado versus XML válido.

Uma avaliação informal da previsibilidade das transgressões não ajuda. Esses dados textuais não são XML. Nenhuma ferramenta ou biblioteca XML compatível pode ajudá-lo a processá-lo.

Opções, mais desejáveis ​​primeiro:

  1. Peça ao provedor que corrija o problema. Exija XML bem formado. (Tecnicamente, a frase XML ​​bem formado é redundante, mas pode ser útil para dar ênfase. )
  2. Use um analisador de marcação tolerante para limpar o problema antes da análise como XML:

  3. Processe os dados como texto manualmente usando um editor de texto ou programaticamente usando funções de caracteres/cadeia de caracteres. Fazer isso programaticamente pode variar de complicado a impossível , pois o que parece previsível geralmente não é - a quebra de regras é raramente vinculado por regras .

    • Para erros de caracteres inválidos , use regex para remover/substituir caracteres inválidos:
      • PHP:preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $s);
      • Ruby:string.tr("^\u{0009}\u{000a}\u{000d}\u{0020}-\u{D7FF}\u{E000‌​}-\u{FFFD}", ' ')
      • JavaScript:inputStr.replace(/[^\x09\x0A\x0D\x20-\xFF\x85\xA0-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/gm, '')
    • Para e comercial , use regex para substituir correspondências por &amp;crédito: blhsin , demo

      &(?!(?:#\d+|#x[0-9a-f]+|\w+);)
      

    Observe que as expressões regulares acima não levarão em consideração comentários ou seções CDATA.

20
kjhughes

Um analisador XML padrão NUNCA aceita XML inválido, por design.

Sua única opção é pré-processar a entrada para remover o conteúdo "previsivelmente inválido" ou envolvê-lo no CDATA, antes de analisá-lo.

1
Jim Garrison

Na IMO, esses casos devem ser resolvidos usando JSoup .

Abaixo está uma resposta realmente não para este caso específico, mas encontrou isso na web (graças a inuyasha82 no Coderwall). Esse bit de código me inspirou a outro problema semelhante ao lidar com XMLs malformados, então eu o compartilho aqui.

Por favor, não edite o que está abaixo, como está no site original.

O formato XML requer que seja válido um elemento raiz exclusivo declarado no documento. Por exemplo, um xml válido é:

<root>
     <element>...</element>
     <element>...</element>
</root>

Mas se você tiver um documento como:

<element>...</element>
<element>...</element>
<element>...</element>
<element>...</element>

Isso será considerado um XML malformado; portanto, muitos analisadores xml lançam uma exceção reclamando sobre nenhum elemento raiz. Etc.

Neste exemplo, há uma solução sobre como resolver esse problema e analisar com êxito o xml malformado acima.

Basicamente, o que faremos é adicionar programaticamente um elemento raiz.

Portanto, primeiro você precisa abrir o recurso que contém seu xml "malformado" (ou seja, um arquivo):

File file = new File(pathtofile);

Em seguida, abra um FileInputStream:

FileInputStream fis = new FileInputStream(file);

Se tentarmos analisar esse fluxo com qualquer biblioteca XML nesse ponto, aumentaremos o Exceção de documento malformado.

Agora, criamos uma lista de objetos InputStream com três elementos:

Um elemento ByteIputStream que contém a sequência: "" Nosso FileInputStream Um ByteInputStream com a sequência: "" Portanto, o código é:

List<InputStream> streams = 
    Arrays.asList(
        new ByteArrayInputStream("<root>".getBytes()),
    fis,
    new ByteArrayInputStream("</root>".getBytes()));

Agora, usando um SequenceInputStream, criamos um contêiner para a lista criada acima:

InputStream cntr = 
new SequenceInputStream(Collections.enumeration(str));

Agora podemos usar qualquer biblioteca XML Parser, no cntr, e ela será analisada sem nenhum problema. (Verificado com a biblioteca Stax);

1
Benj

A resposta aceita é um bom conselho e contém links muito úteis.

Gostaria de acrescentar que isso e muitosoutros casos de XML não-bem-formado e/ou com DTD inválido podem ser reparados usando SGML, o superconjunto padronizado de ISO da HTML e XML. No seu caso, o que funciona é declarar o falso THIS-IS-PART-OF-DESCRIPTION elemento como elemento vazio SGML e use, por exemplo, o programa osx (parte do pacote OpenSP/OpenJade SGML) para convertê-lo em XML. Por exemplo, se você fornecer o seguinte para osx

<!DOCTYPE xml [
  <!ELEMENT xml - - ANY>
  <!ELEMENT description - - ANY>
  <!ELEMENT THIS-IS-PART-OF-DESCRIPTION -  - EMPTY>
]>
<xml>
  <description>blah blah
    <THIS-IS-PART-OF-DESCRIPTION>
  </description>
</xml>

ele produzirá XML bem formado para processamento adicional com as ferramentas XML de sua escolha.

Observe, no entanto, que seu snippet de exemplo tem outro problema nos nomes dos elementos que começam com as letras xml ou XML ou Xml etc. estão reservados em XML e não ser aceito pelos analisadores XML em conformidade.

1
imhotap