Existe uma maneira de escapar de um token final CDATA em xml?

129

Eu queria saber se existe alguma maneira de escapar de um token final CDATA ( ]]>) dentro de uma seção CDATA em um documento xml. Ou, de maneira mais geral, se houver alguma sequência de escape para uso em um CDATA (mas, se existir, acho que provavelmente só faria sentido escapar dos tokens iniciais ou finais).

Basicamente, você pode ter um token de início ou fim incorporado em um CDATA e dizer ao analisador para não interpretá-lo, mas tratá-lo como apenas mais uma sequência de caracteres.

Provavelmente, você deve refatorar sua estrutura xml ou seu código se tentar fazer isso, mas mesmo que eu trabalhe com xml diariamente nos últimos 3 anos ou mais e nunca tenha tido esse problema, Fiquei me perguntando se era possível. Apenas por curiosidade.

Editar:

Além de usar a codificação html ...

Juan Pablo Califano
fonte
4
Primeiro, aceito a resposta como correta, mas observe: nada impede que alguém codifique >como >no CData para garantir que o incorporado ]]>não seja analisado como CDEnd. Significa simplesmente que é inesperado e que também &deve ser primeiro codificado &para que os dados possam ser decodificados corretamente. Os usuários do documento também devem saber decodificar esse CData. Não é algo inédito, pois parte do objetivo do CData é conter o conteúdo que um consumidor específico entende como lidar. Não se pode esperar que esse CData seja interpretado adequadamente por qualquer consumidor genérico.
Nix 16/05
1
@nix, o CDATA apenas fornece uma maneira explícita de declarar o conteúdo do nó de texto, de modo que os tokens de idioma em (que não sejam]]>) não sejam analisados. Especificamente, não expande referências de entidades como o & gt; por esse motivo, portanto, em um bloco CDATA, isso significa apenas esses quatro caracteres, não '>'. Para colocar em perspectiva: na especificação xml, todo o conteúdo do texto é chamado "cdata", não apenas essas seqüências ("dados dos caracteres"). Também não se trata de agentes consumidores específicos. Porém, existe uma coisa dessas - instruções de processamento (<? Target instructions?>).
Ponto-
(Devo acrescentar que, mesmo que esse tipo de coisa seja contrária à intenção original do nó, tudo é justo na longa e torturante batalha com o XML. Apenas acho que seria útil para os leitores saberem que <! [CDATA [ ]]> não foi realmente projetado para esse fim.)
Ponto-
1
O @Semicolon CDATAfoi projetado para permitir qualquer coisa : eles são usados ​​para escapar de blocos de texto contendo caracteres que, de outra forma, seriam reconhecidos como marcação Isso CDATAtambém implica, já que também é marcação. Mas, na verdade, você não precisa da codificação dupla que eu impliquei. ]]&gt;é um meio aceitável de codificar a CDEnddentro de a CDATA.
Nix 11/11
É verdade que você não precisaria de codificação dupla - mas ainda precisaria que o agente tivesse conhecimento especial, pois o analisador não analisaria o & gt; como>. É isso que você quer dizer, eu acho? Que você pode substituí-los como achar melhor, depois de analisar?
Ponto-

Respostas:

141

Claramente, esta questão é puramente acadêmica. Felizmente, tem uma resposta muito definida.

Você não pode escapar de uma sequência final CDATA. A regra de produção 20 da especificação XML é bastante clara:

[20]    CData      ::=      (Char* - (Char* ']]>' Char*))

EDIT: Esta regra do produto significa literalmente "Uma seção CData pode conter o que você deseja, mas a sequência ']]>'. Sem exceção.".

EDIT2: A mesma seção também lê:

Dentro de uma seção CDATA, somente a seqüência CDEnd é reconhecida como marcação, para que colchetes ee comercial do ângulo esquerdo possam ocorrer em sua forma literal; eles não precisam (e não podem) ser escapados usando " &lt;" e " &amp;". As seções CDATA não podem ser aninhadas.

Em outras palavras, não é possível usar referência de entidade, marcação ou qualquer outra forma de sintaxe interpretada. O único texto analisado dentro de uma seção CDATA é ]]>e termina a seção.

Portanto, não é possível escapar ]]>dentro de uma seção CDATA.

EDIT3: A mesma seção também lê:

2.7 Seções CDATA

[Definição: as seções CDATA podem ocorrer em qualquer lugar em que dados de caracteres possam ocorrer; eles são usados ​​para escapar de blocos de texto contendo caracteres que, de outra forma, seriam reconhecidos como marcação. As seções CDATA começam com a sequência "<! [CDATA [" e terminam com a sequência "]]>":]

Pode haver uma seção CDATA em qualquer lugar em que dados de caracteres possam ocorrer, incluindo várias seções CDATA adjacentes no lugar de uma única seção CDATA. Isso permite que seja possível dividir o ]]>token e colocar as duas partes em seções CDATA adjacentes.

ex:

<![CDATA[Certain tokens like ]]> can be difficult and <invalid>]]> 

deve ser escrito como

<![CDATA[Certain tokens like ]]]]><![CDATA[> can be difficult and <valid>]]> 
ddaa
fonte
1
De fato. Bem, eu não sou do tipo acadêmico, mas como eu disse na pergunta, estou curioso sobre isso. Para ser sincero, vou apenas aceitar sua palavra, porque mal consigo entender a sintaxe usada para a regra. Obrigado pela sua resposta.
Juan Pablo Califano
39
Esta não é uma questão acadêmica. Pense em um feed RSS de uma postagem de blog que contenha uma discussão sobre CDATA.
usr
4
Eu quis dizer "acadêmico" no sentido: "interessante discutir, mas sem uso prático". Geralmente, o CDATA não é útil, é apenas uma maneira de serializar texto XML e é semanticamente equivalente a escapar caracteres especiais usando entidades de caracteres & lt; & gt; e ". Entidades de caracteres é a solução mais simples, mais robusta e mais geral; portanto, use isso em vez das seções CDATA. Se você usa uma biblioteca XML adequada (em vez de criar XML a partir de cadeias), nem precisa pensar nisso.
DDAA
5
Acabei de ser mordido por este porque estou tentando codificar algum Javascript compactado em uma tag <script> como: <script>/*<![CDATA[*/javascript goes here/*]]>*/</script>e meu javascript inclui exatamente essa sequência! Eu gosto da idéia de dividir em várias seções CDATA ...
NickZoic
3
Eu experimentei isso no mundo real. Ao ler o dump da wikipedia e escrever outro arquivo xml, encontrei isso na página do National Transportation Safety Board . Continha US $> 100 milhões (2013) para o orçamento na caixa de informações. O xml de origem contido, [[United States dollar|US$]]&gt;100 million (2013)traduzido [[United States dollar|US$]]>100 million (2013)pelo leitor e pelo escritor, optou por usar o CDATA para escapar do texto e falhou.
Paul Jackson
169

Você precisa dividir seus dados em pedaços para ocultar o arquivo ]]>.

Aqui está a coisa toda:

<![CDATA[]]]]><![CDATA[>]]>

O primeiro <![CDATA[]]]]>tem o ]]. O segundo <![CDATA[>]]>tem o >.

S.Lott
fonte
1
Obrigado pela sua resposta. Eu estava procurando algo parecido com uma barra invertida equivalente (dentro de strings em C, PHP, Java, etc). De acordo com a regra citada pela ddaa, parece que não existe.
Juan Pablo Califano
28
Essa deve ser a resposta aceita. Escapar é um termo um pouco ambíguo, mas essa resposta definitivamente aborda o espírito de escapar . Pena que não se encaixa na concepção estreita de fuga do OP , que arbitrariamente exige que o caractere de barra invertida esteja envolvido por algum motivo.
G-Wiz
5
Então, em resumo, escape ]]>como ]]]]><![CDATA[>. 5 vezes o comprimento ... uau. Mas então, é uma sequência incomum.
Brilliand
5
O comprimento de 5x não é apenas hilário, nem é uma sequência incomum no código, que é o principal caso de uso do CDATA! Assumindo JavaScript compactado que remove espaços, você pode acessar um campo por nome de uma matriz de nomes por índice, como "if (fields [fieldnames [0]]> 3)" e agora é necessário alterá-lo para "if ( fields [fieldnames [0]]]]> <! [CDATA [> 3) ", que anula o propósito de usar o CDATA para torná-lo mais legível, LOL. Eu gostaria de dar um tapa verbalmente em quem surgiu com a sintaxe CDATA.
Triynko
1
Escapar, ou mais corretamente, citar significa inserir algum texto em um contexto em que o texto bruto tenha significado SEM sair do contexto. Não tem nada a ver com barras invertidas. E essa resposta não está escapando ou está sendo citada, pois produz duas seções CDATA em vez de uma.
Ddaa
17

Você não escapa do, ]]>mas escapa do >depois ]]inserindo ]]><![CDATA[antes do >, pense nisto como uma \string C / Java / PHP / Perl, mas necessário apenas antes >e depois de a ]].

BTW,

A resposta de S.Lott é a mesma, mas com palavras diferentes.

Jason Pyeron
fonte
2
Eu prefiro essa redação. :)
Brilliand
3
Essa maneira de dizer dá às pessoas a idéia errada. Isso não está escapando. ]]]]><![CDATA[>não é uma sequência mágica para ]]>. ]]]]>possui ]]caracteres como dados e ]]>termina a seção CDATA atual. <![CDATA[>inicia uma nova seção CDATA e a coloca >. Na verdade, são dois elementos diferentes e serão tratados de maneira diferente ao trabalhar com um analisador DOM. Você deve estar ciente disso. Essa maneira de fazer isso é semelhante a ]]]><![CDATA[]>, exceto que coloca ]o primeiro e ]>o segundo CDATA. A diferença permanece.
Aidiakapi
A diferença é exagerada, pois o conteúdo CDATA é tratado como uma extensão literal de texto de escape. Somente ao mexer com o DOM isso realmente importa e, nesse nível, você está lidando com outros limites invisíveis de qualquer maneira, como texto, comentários e nós de instruções de processamento.
Beejor
7

A resposta de S. Lott está certa: você não codifica a tag final, divide-a em várias seções CDATA.

Como enfrentar esse problema no mundo real: usando um editor XML para criar um documento XML que será alimentado em um sistema de gerenciamento de conteúdo, tente escrever um artigo sobre as seções CDATA. Seu truque comum de incorporar exemplos de código em uma seção CDATA falhará aqui. Você pode imaginar como eu aprendi isso.

Mas na maioria das circunstâncias, você não encontrará isso, e aqui está o porquê: se você deseja armazenar (digamos) o texto de um documento XML como o conteúdo de um elemento XML, provavelmente usará um método DOM, por exemplo:

XmlElement elm = doc.CreateElement("foo");
elm.InnerText = "<[CDATA[[Is this a problem?]]>";

E o DOM escapa razoavelmente do <e do>, o que significa que você não incorporou inadvertidamente uma seção CDATA ao seu documento.

Ah, e isso é interessante:

XmlDocument doc = new XmlDocument();

XmlElement elm = doc.CreateElement("doc");
doc.AppendChild(elm);

string data = "<![[CDATA[This is an embedded CDATA section]]>";
XmlCDataSection cdata = doc.CreateCDataSection(data);
elm.AppendChild(cdata);

Provavelmente é uma ideosincrasia do .NET DOM, mas isso não gera uma exceção. A exceção é lançada aqui:

Console.Write(doc.OuterXml);

Eu acho que o que está acontecendo nos bastidores é que o XmlDocument está usando um XmlWriter produz sua saída, e o XmlWriter verifica se há boa formação durante a gravação.

Robert Rossney
fonte
Bem, eu tive um exemplo quase do "mundo real". Normalmente, carrego o Xml do Flash que contém a marcação html nas seções CDATA. Ter uma maneira de escapar disso poderia ser útil, eu acho. Mas, de qualquer forma, nesse caso, o conteúdo CDATA é geralmente XHTML válido e, portanto, o CDATA "externo" pode ser completamente evitado.
Juan Pablo Califano
2
CDATA quase sempre pode ser completamente evitado. Acho que as pessoas que lutam com o CDATA com muita frequência não entendem o que realmente estão tentando fazer e / ou como a tecnologia que estão usando realmente funciona.
Robert Rossney 24/10/08
Ah, devo acrescentar que a única razão pela qual o CMS que aludi na minha resposta usou o CDATA foi que o escrevi e não entendi o que estava realmente tentando fazer e / ou como a tecnologia funciona. Não precisei usar o CDATA.
Robert Rossney 24/10/08
Se você estiver usando o .net, o comentário anterior sobre o CDATA ser evitável é direto - basta escrever o conteúdo como uma string e o framework fará todo o possível para escapar do mundo real (e sem escape na leitura). ... xmlStream.WriteStartElement ("UnprocessedHtml"); xmlStream.WriteString (UnprocessedHtml); xmlStream.WriteEndElement ();
MarkMullin
6

simplesmente substitua ]]>por]]]]><![CDATA[>

Thomas Grainger
fonte
3

Aqui está outro caso em que ]]>precisa ser escapado. Suponha que precisamos salvar um documento HTML perfeitamente válido dentro de um bloco CDATA de um documento XML e que a origem HTML possua seu próprio bloco CDATA. Por exemplo:

<htmlSource><![CDATA[ 
    ... html ...
    <script type="text/javascript">
        /* <![CDATA[ */
        -- some working javascript --
        /* ]]> */
    </script>
    ... html ...
]]></htmlSource>

o sufixo CDATA comentado precisa ser alterado para:

        /* ]]]]><![CDATA[> *//

como um analisador XML não saberá como lidar com blocos de comentários javascript

Shawn Becker
fonte
Este não é um caso especial. Basta substituir ]]>por ]]]]><![CDATA[>ainda se aplica aqui. O fato de ser JavaScript ou comentar não é importante.
Thomas Grainger
1

Em PHP: '<![CDATA['.implode(explode(']]>', $string), ']]]]><![CDATA[>').']]>'

user2194495
fonte
1

Uma maneira mais limpa em PHP:

   function safeCData($string)
   {
      return '<![CDATA[' . str_replace(']]>', ']]]]><![CDATA[>', $string) . ']]>';
   }

Não se esqueça de usar um str_replace multibyte-safe, se necessário (não latin1 $string):

   function mb_str_replace($search, $replace, $subject, &$count = 0)
   {
      if (!is_array($subject))
      {
         $searches = is_array($search) ? array_values($search) : array ($search);
         $replacements = is_array($replace) ? array_values($replace) : array ($replace);
         $replacements = array_pad($replacements, count($searches), '');
         foreach ($searches as $key => $search)
         {
            $parts = mb_split(preg_quote($search), $subject);
            $count += count($parts) - 1;
            $subject = implode($replacements[$key], $parts);
         }
      }
      else
      {
         foreach ($subject as $key => $value)
         {
            $subject[$key] = mb_str_replace($search, $replace, $value, $count);
         }
      }
      return $subject;
   }
Alain Tiemblo
fonte
Você pode explicar o seu voto negativo? Dizer que cometi um erro não é tão útil quanto explicar onde está.
Alain Tiemblo
Não há necessidade de substituir multibyte com segurança se você estiver usando UTF-8. Porém, eu não
diminuí a votação
-1

Não acho que interromper o CDATA seja um bom caminho a percorrer. Aqui está a minha alternativa ...

Use ]para a sequência de escape seguida pelo valor hexadecimal do seu personagem. Como no &#xhhhh;=>]<unicode value>;

Dessa forma, se você tentar gravar ]]>sua codificação, o fn produzirá o ]005D;]005D;]003E;que está correto no CDATA.

É melhor do que escapar pelo nome da entidade, porque eles não são decodificados todas as vezes no seu aplicativo e você pode ter prioridades diferentes para escapar de entidades com e comercial e escapar de outros caracteres / seqüências. Como resultado, você tem mais controle sobre o conteúdo do CDATA.

honzar
fonte
-2

Veja esta estrutura:

<![CDATA[
   <![CDATA[
      <div>Hello World</div>
   ]]]]><![CDATA[>
]]>

Para as tags CDATA internas, você deve fechar com em ]]]]><![CDATA[>vez de ]]>. Simples assim.

Chad Kuehn
fonte