2009-09-21 25 views
6

Tengo que analizar XML provisto externamente que tiene atributos con saltos de línea en ellos. Con SimpleXML, los saltos de línea parecen estar perdidos. De acuerdo con another stackoverflow question, los saltos de línea deberían ser válidos (¡aunque mucho menos que ideales!) Para XML.PHP SimpleXML no conserva los saltos de línea en los atributos XML

¿Por qué se pierden? [edit] ¿Y cómo puedo preservarlos? [/ edit]

Aquí hay una secuencia de comandos del archivo de demostración (tenga en cuenta que cuando los saltos de línea no están en un atributo se conservan).

de archivos PHP con XML incrustado

$xml = <<<XML 
<?xml version="1.0" encoding="utf-8"?> 
<Rows> 
    <data Title='Data Title' Remarks='First line of the row. 
Followed by the second line. 
Even a third!' /> 
    <data Title='Full Title' Remarks='None really'>First line of the row. 
Followed by the second line. 
Even a third!</data> 
</Rows> 
XML; 

$xml = new SimpleXMLElement($xml); 
print '<pre>'; print_r($xml); print '</pre>'; 

salida de print_r

SimpleXMLElement Object 
(
    [data] => Array 
     (
      [0] => SimpleXMLElement Object 
       (
        [@attributes] => Array 
         (
          [Title] => Data Title 
          [Remarks] => First line of the row. Followed by the second line. Even a third! 
         ) 

       ) 

      [1] => First line of the row. 
Followed by the second line. 
Even a third! 
     ) 

) 
+0

Debe formular esta pregunta en la página de inicio de PHP. Supongo que es porque es SIMPLE xml parser. – jbasko

+0

¿Puedes explicar un poco más lo que quieres decir con la página de inicio de PHP? – Joshua

+0

Inicialmente su pregunta era "¿Por qué SimpleXML hace lo que hace?" Eso es lo que puedes pedirle a los desarrolladores, no a los usuarios. – jbasko

Respuesta

4

La entidad para una nueva línea es &#10;. Jugué con tu código hasta que encontré algo que hizo el truco. No es muy elegante, te lo advierto:

//First remove any indentations: 
$xml = str_replace("  ","", $xml); 
$xml = str_replace("\t","", $xml); 

//Next replace unify all new-lines into unix LF: 
$xml = str_replace("\r","\n", $xml); 
$xml = str_replace("\n\n","\n", $xml); 

//Next replace all new lines with the unicode: 
$xml = str_replace("\n","&#10;", $xml); 

Finally, replace any new line entities between >< with a new line: 
$xml = str_replace(">&#10;<",">\n<", $xml); 

El supuesto, en función de su ejemplo, es que las nuevas líneas que se producen dentro de un nodo o atributo tendrán más texto en la siguiente línea, no un < para abrir un nuevo elemento.

Esto, por supuesto, fallaría si su próxima línea tuviera texto envuelto en un elemento de nivel de línea.

+0

¡Muy listo! La única pega es que estoy trabajando con enormes volúmenes de XML envueltos en SOAP desde los servicios web de SharePoint, por lo que me pone un poco nervioso hacer algo tan brutal. Sin embargo, según la publicación de Bobince, parece que debería ir en esta dirección. Me pregunto si hay alguna forma más elegante de llevarlo a cabo. – Joshua

11

Al utilizar SimpleXML, los saltos de línea parecen haberse perdido.

Sí, eso es lo esperado ... de hecho, se requiere de cualquier analizador conforme que las líneas nuevas en los valores de los atributos representen espacios simples. Ver attribute value normalisation en la especificación XML.

Si se suponía que había un carácter de línea nueva real en el valor del atributo, el XML debería haber incluido una referencia de carácter &#10; en lugar de una nueva línea sin formato.

+2

Para aclarar un poco: las líneas nuevas son * VÁLIDAS *, pero el analizador XML (para cumplir con la especificación) ** DEBE ** reducirlas a un único carácter de espacio (consulte el elemento 3 del enlace de bobince) . – TML

+0

Gracias por la bobina de enlace, y la aclaración TML. Entonces, supongo que mi pregunta ahora es: ¿cómo puedo retener esos saltos de línea? Recibo esta información de un servicio web de SharePoint, por lo que no puedo cambiar el XML para incluir & # 10. ¿Hay alguna manera de anular el cumplimiento del analizador en este sentido? – Joshua

+0

Desafortunadamente no, XML es bastante inflexible en este punto; si el servicio web está produciendo '\ n' cuando significa' 'es un error. (Y algo sorprendente ya que esta es una característica fundamental que se esperaría que cualquier serializador XML hiciera bien ... a menos que, por supuesto, el servicio se esté volteando con plantillas regex o string en lugar de usar una biblioteca XML adecuada) – bobince

0

Esto es lo que funcionó para mí:

primer lugar, obtener el código XML como una cadena:

$xml = file_get_contents($urlXml); 

Después, realice la sustitución: ""

$xml = str_replace(".\xe2\x80\xa9<as:eol/>",".\n\n<as:eol/>",$xml); 

El y "< como: eol />" estaban allí porque necesitaba agregar descansos en ese caso. Las nuevas líneas "\ n" se pueden reemplazar con lo que quieras.

Después de reemplazar, simplemente carga el xml-cadena como un objeto SimpleXMLElement:

$xmlo = new SimpleXMLElement($xml); 

Et Voilà

1

Suponiendo $ xmlData es su cadena XML antes de ser enviada al analizador, esto debe reemplazar todo nuevas líneas en atributos con la entidad correcta. Tuve el problema con XML proveniente de SQL Server.

$parts = explode("<", $xmlData); //split over < 
array_shift($parts); //remove the blank array element 
$newParts = array(); //create array for storing new parts 
foreach($parts as $p) 
{ 
    list($attr,$other) = explode(">", $p, 2); //get attribute data into $attr 
    $attr = str_replace("\r\n", "&#10;", $attr); //do the replacement 
    $newParts[] = $attr.">".$other; // put parts back together 
} 
$xmlData = "<".implode("<", $newParts); // put parts back together prefixing with < 

Probablemente se puede hacer más simplemente con una expresión regular, pero ese no es un punto fuerte para mí.

+0

Exactamente, el problema es que las nuevas líneas técnicamente no son válidas en los atributos XML. Sin embargo, los analizadores tienden a arreglar las cosas mucho. En todos los casos, las entidades inválidas deberían estar codificadas.La mejor solución sería arreglar la fuente, pero esto parece legítimo si eso no está disponible. –

0

Bueno, esta pregunta es antigua, pero como yo, alguien podría llegar a esta página con el tiempo. Tenía un enfoque ligeramente diferente y creo que el más elegante de estos mencionados.

Dentro del xml, pone una palabra única que usará para la nueva línea.

Cambio xml para

<data Title='Data Title' Remarks='First line of the row. \n 
Followed by the second line. \n 
Even a third!' /> 

Y luego, cuando llegue a la ruta deseada en el nodo SimpleXML en cadena de salida escribir algo como esto:

$findme = '\n'; 
$pos = strpos($output, $findme); 
if($pos!=0) 
{ 
$output = str_replace("\n","<br/>",$output); 

No tiene por qué ser '\ n , puede ser cualquier char único.

1

Aquí está el código para reemplazar las nuevas líneas con la referencia de caracteres adecuada en ese fragmento de XML en particular. Ejecute este código antes de analizar.

$replaceFunction = function ($matches) { 
    return str_replace("\n", "&#10;", $matches[0]); 
}; 
$xml = preg_replace_callback(
    "/<data Title='[^']+' Remarks='[^']+'/i", 
    $replaceFunction, $xml); 
Cuestiones relacionadas