¿Hay alguna manera de escapar de un token final CDATA en xml?

129

Me preguntaba si hay alguna forma de escapar de un token final CDATA ( ]]>) dentro de una sección CDATA en un documento xml. O, más generalmente, si hay alguna secuencia de escape para usar dentro de un CDATA (pero si existe, supongo que probablemente tendría sentido escapar de los tokens de inicio o fin, de todos modos).

Básicamente, ¿puede tener un token de inicio o fin incrustado en un CDATA y decirle al analizador que no lo interprete sino que lo trate como una secuencia de caracteres más?

Probablemente, debería refactorizar su estructura xml o su código si intenta hacerlo, pero a pesar de que he estado trabajando con xml a diario durante los últimos 3 años más o menos y nunca he tenido este problema, Me preguntaba si era posible. Solo por curiosidad.

Editar:

Aparte de usar la codificación html ...

Juan pablo califano
fuente
44
En primer lugar, yo acepta la respuesta correcta, pero como nota: nada impide que alguien de codificación >como >dentro programable CData para asegurar incrustado ]]>no se analiza como CDEnd. Simplemente significa que es inesperado y que &PRIMERO debe codificarse &también para que los datos puedan decodificarse correctamente. Los usuarios del documento también deben saber decodificar este CData. No es desconocido ya que parte del propósito de CData es contener contenido que un consumidor específico entiende cómo manejar. Tal CData simplemente no puede ser interpretado correctamente por ningún consumidor genérico.
nix
1
@nix, CDATA solo proporciona una forma explícita de declarar el contenido del nodo de texto de tal manera que los tokens de idioma dentro (que no sean]]>) no se analicen. Específicamente no expande referencias de entidades como & gt; Por esta razón, en un bloque CDATA, eso solo significa esos cuatro caracteres, no '>'. Para ponerlo en perspectiva: en la especificación xml, todo el contenido del texto se llama "cdata", no solo estas secuencias ("datos de caracteres"). Tampoco se trata de agentes consumidores específicos. (Sin embargo, existe tal cosa: instrucciones de procesamiento (<? Instrucción de destino?>).
Semicolon
(Debo agregar, incluso si este tipo de cosas va en contra de la intención original del nodo, todo es justo en la larga y tortuosa batalla con XML. Simplemente siento que podría ser útil para los lectores saber que <! [CDATA [ ]]> en realidad no fue diseñado para ese propósito.)
Semicolon
1
@Semicolon CDATAfue diseñado para permitir cualquier cosa : se usan para escapar de bloques de texto que contienen caracteres que de otro modo se reconocerían como marcado. Eso implica CDATAtambién, ya que también es marcado. Pero, de hecho, no necesita la doble codificación que implicaba. ]]&gt;es un medio aceptable para codificar a CDEnddentro de a CDATA.
nix
Es cierto que no necesitaría una doble codificación, pero aún necesitaría que el agente tenga un conocimiento especial, ya que el analizador no analizaría & gt; como>. Eso es lo que quieres decir, creo. ¿Que podría reemplazarlos como mejor le parezca, después de analizar?
Punto

Respuestas:

141

Claramente, esta pregunta es puramente académica. Afortunadamente, tiene una respuesta muy definitiva.

No puede escapar de una secuencia final de CDATA. La regla de producción 20 de la especificación XML es bastante clara:

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

EDITAR: Esta regla del producto significa literalmente "Una sección de CData puede contener lo que desee PERO la secuencia ']]>'. Sin excepción".

EDIT2: La misma sección también lee:

Dentro de una sección CDATA, solo la cadena CDEnd se reconoce como marcado, de modo que los corchetes angulares izquierdos y los signos de unión pueden aparecer en su forma literal; no necesitan (y no pueden) escapar usando " &lt;" y " &amp;". Las secciones CDATA no pueden anidar.

En otras palabras, no es posible utilizar referencias de entidades, marcas o cualquier otra forma de sintaxis interpretada. El único texto analizado dentro de una sección CDATA es ]]>, y termina la sección.

Por lo tanto, no es posible escapar ]]>dentro de una sección CDATA.

EDITAR3: La misma sección también lee:

2.7 Secciones CDATA

[Definición: las secciones CDATA pueden aparecer en cualquier lugar donde puedan aparecer datos de caracteres; se usan para escapar de bloques de texto que contienen caracteres que de otro modo se reconocerían como marcado. Las secciones CDATA comienzan con la cadena "<! [CDATA [" y terminan con la cadena "]]>":]

Entonces puede haber una sección CDATA en cualquier lugar donde puedan aparecer datos de caracteres, incluidas múltiples secciones CDATA adyacentes en lugar de una sola sección CDATA. Eso permite dividir el ]]>token y colocar las dos partes en secciones adyacentes de CDATA.

ex:

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

debe escribirse como

<![CDATA[Certain tokens like ]]]]><![CDATA[> can be difficult and <valid>]]> 
ddaa
fuente
1
En efecto. Bueno, no soy un tipo académico, pero como dije en la pregunta, solo tengo curiosidad por esto. Para ser honesto, tomaré su palabra sobre esto, porque apenas puedo entender la sintaxis utilizada para la regla. Gracias por tu respuesta.
Juan Pablo Califano
39
Esta no es una pregunta académica. Piense en una fuente RSS de una publicación de blog que contenga una discusión sobre CDATA.
usr
44
Quise decir "académico" en el sentido: "interesante para discutir, pero sin uso práctico". Generalmente, CDATA no es útil, es solo una forma de serializar texto XML, y es semánticamente equivalente a escapar caracteres especiales usando entidades de caracteres & lt; & gt; y ''. Las entidades de caracteres son la solución más simple, robusta y general, así que úsela en lugar de las secciones CDATA. Si usa una biblioteca XML adecuada (en lugar de construir XML a partir de cadenas), ni siquiera tiene que pensar en ello.
ddaa
55
Acabo de ser mordido por este porque estoy tratando de codificar un Javascript comprimido en una etiqueta <script> como: ¡ <script>/*<![CDATA[*/javascript goes here/*]]>*/</script>y mi javascript incluye solo esa secuencia! Me gusta la idea de dividir en múltiples secciones CDATA ...
NickZoic
3
Experimenté esto en el mundo real. Mientras leía el volcado de wikipedia y escribía otro archivo xml, me encontré con esto en la página de la Junta Nacional de Seguridad del Transporte . Contenía US $> 100 millones (2013) para el presupuesto en el cuadro de información. La fuente xml contenida [[United States dollar|US$]]&gt;100 million (2013)que fue traducida [[United States dollar|US$]]>100 million (2013)por el lector y el escritor optó por usar CDATA para escapar del texto y falló.
Paul Jackson
169

Tienes que dividir tus datos en pedazos para ocultar el ]]>.

Aquí está todo:

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

El primero <![CDATA[]]]]>tiene el ]]. El segundo <![CDATA[>]]>tiene el >.

S.Lott
fuente
1
Gracias por tu respuesta. Estaba buscando algo como una barra invertida equivalente (dentro de cadenas en C, PHP, Java, etc.). Según la regla citada por ddaa, parece que no existe tal cosa.
Juan Pablo Califano
28
Esta debería ser la respuesta aceptada. Escapar es un término un poco ambiguo, pero esta respuesta definitivamente aborda el espíritu de escapar . Lástima que no se ajuste a la estrecha concepción de escape del OP , que arbitrariamente requiere que el carácter de barra invertida esté involucrado por alguna razón.
G-Wiz
55
En resumen, escapar ]]>como ]]]]><![CDATA[>. 5 veces la longitud ... wow. Pero entonces, es una secuencia poco común.
Brilliand
55
La longitud de 5x no solo es graciosa, ¡ni siquiera es una secuencia poco común en el código, que es el principal caso de uso de CDATA! Suponiendo que JavaScript comprimido elimina espacios, puede acceder a un campo por nombre desde una matriz de nombres por índice, como "if (fields [fieldnames [0]]> 3)" "y ahora tiene que cambiarlo a" if ( fields [fieldnames [0]]]]> <! [CDATA [> 3) ", que no sirve para usar CDATA para que sea más legible, LOL. Me gustaría abofetear verbalmente a quien se le ocurrió la sintaxis CDATA.
Triynko
1
Escapar, o más correctamente, entre comillas, significa insertar texto en un contexto en el que el texto sin formato tiene significado SIN salir del contexto. No tiene nada que ver con barras invertidas. Y esta respuesta no se escapa ni se cita, ya que produce dos secciones CDATA en lugar de una.
ddaa
17

No se escapa del ]]>pero se escapa >después ]]insertando ]]><![CDATA[antes del >, piense en esto como una \cadena C / Java / PHP / Perl, pero solo se necesita antes >y después de a ]].

Por cierto,

La respuesta de S.Lott es la misma, simplemente redactada de manera diferente.

Jason Pyeron
fuente
2
Prefiero esta redacción. :)
Brilliand
3
Esta forma de decirlo le da a la gente una idea equivocada. Esto no se escapa. ]]]]><![CDATA[>no es una secuencia mágica para ]]>. ]]]]>tiene ]]caracteres como datos y ]]>finaliza la sección CDATA actual. <![CDATA[>inicia una nueva sección CDATA y la coloca >. En realidad, son dos elementos diferentes y serán tratados de manera diferente cuando se trabaje con un analizador DOM. Deberías ser consciente de eso. Esta forma de hacerlo es similar ]]]><![CDATA[]>, excepto que incluye ]el primer ]>CDATA y el segundo. La diferencia permanece.
Aidiakapi
La diferencia es exagerada, ya que el contenido de CDATA se trata como un tramo literal de texto escapado. Solo cuando juegas con el DOM realmente importa, y en ese nivel estás lidiando con otros límites invisibles de todos modos, como texto, comentarios y nodos de instrucciones de procesamiento.
Beejor
7

La respuesta de S. Lott es correcta: no codifica la etiqueta final, la divide en varias secciones CDATA.

Cómo resolver este problema en el mundo real: utilizando un editor XML para crear un documento XML que se incorporará a un sistema de gestión de contenido, intente escribir un artículo sobre las secciones CDATA. Su truco habitual de incrustar ejemplos de código en una sección CDATA le fallará aquí. Puedes imaginar cómo aprendí esto.

Pero en la mayoría de las circunstancias, no encontrará esto, y he aquí por qué: si desea almacenar (por ejemplo) el texto de un documento XML como el contenido de un elemento XML, probablemente usará un método DOM, por ejemplo:

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

Y el DOM escapa razonablemente <y el>, lo que significa que no ha incrustado inadvertidamente una sección CDATA en su documento.

Ah, y esto es interesante:

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);

Esta es probablemente una ideosincrasia de .NET DOM, pero eso no arroja una excepción. La excepción se lanza aquí:

Console.Write(doc.OuterXml);

Supongo que lo que sucede debajo del capó es que el XmlDocument está utilizando un XmlWriter para producir su salida, y el XmlWriter verifica la buena forma a medida que escribe.

Robert Rossney
fuente
Bueno, tuve un ejemplo casi del "mundo real". Por lo general, cargo Xml desde Flash que contiene marcado html dentro de las secciones CDATA. Supongo que tener una forma de escapar podría ser útil. Pero de todos modos, en ese caso, el contenido de CDATA suele ser XHTML válido, por lo que podría evitarse por completo el CDATA "externo".
Juan Pablo Califano
2
CDATA casi siempre se puede evitar por completo. Encuentro que las personas que luchan con CDATA con mucha frecuencia no entienden lo que realmente están tratando de hacer y / o cómo funciona realmente la tecnología que están utilizando.
Robert Rossney
Ah, también debería agregar que la única razón por la que el CMS al que aludí en mi respuesta usó CDATA fue que lo escribí, y no entendí lo que realmente estaba tratando de hacer y / o cómo funciona la tecnología. No necesitaba usar CDATA.
Robert Rossney
Si está utilizando .net, el comentario anterior sobre que CDATA es evitable es perfecto: simplemente escriba el contenido como una cadena y el marco hará todo el escape (y la eliminación de escapes en la lectura) del mundo real ... ... xmlStream.WriteStartElement ("UnprocessedHtml"); xmlStream.WriteString (UnprocessedHtml); xmlStream.WriteEndElement ();
Mark Mullin
6

simplemente reemplace ]]>con]]]]><![CDATA[>

Thomas Grainger
fuente
3

Aquí hay otro caso en el que se ]]>debe escapar. Supongamos que necesitamos guardar un documento HTML perfectamente válido dentro de un bloque CDATA de un documento XML y la fuente HTML tiene su propio bloque CDATA. Por ejemplo:

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

el sufijo CDATA comentado debe cambiarse a:

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

ya que un analizador XML no sabrá cómo manejar los bloques de comentarios de JavaScript

Shawn Becker
fuente
Este no es un caso especial. Simplemente reemplace ]]>con ]]]]><![CDATA[>todavía se aplica aquí. El hecho de que sea JavaScript o esté comentado no es importante.
Thomas Grainger
1

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

user2194495
fuente
1

Una forma más limpia en PHP:

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

No olvide utilizar un str_replace seguro para múltiples bytes si es necesario (no 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
fuente
¿Puedes explicar tu voto negativo? Decir que cometí un error no es tan útil como explicar dónde está.
Alain Tiemblo
No es necesario hacer un reemplazo seguro de varios bytes si está utilizando UTF-8. Aunque no
voté en contra
-1

No creo que interrumpir CDATA sea un buen camino a seguir. Aquí está mi alternativa ...

Úselo ]para la secuencia de escape seguida del valor hexadecimal de su personaje. Como en el &#xhhhh;=>]<unicode value>;

De esta manera, si intenta grabar ]]>su codificación, fn producirá lo ]005D;]005D;]003E;que está bien en CDATA.

Es mejor que escapar por nombre de entidad, porque esos no están decodificados cada vez en su aplicación y es posible que tenga diferentes prioridades para escapar de entidades con ampersand frente a escapar de otros caracteres / secuencias. Como resultado, tiene más control sobre el contenido de CDATA.

honzar
fuente
-2

Ver esta estructura:

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

Para las etiquetas CDATA internas, debe cerrar con en ]]]]><![CDATA[>lugar de ]]>. Simple como eso.

Chad Kuehn
fuente