PHP SimpleXML не сохраняет разрывы строк в атрибутах XML

Я должен разбирать XML, содержащий внешние ссылки, в которых есть атрибуты с разрывами строк. Используя SimpleXML, разрывы строк кажутся потерянными. В соответствии с другим вопросом stackoverflow , разрывы строк должны быть действительными (хотя и гораздо менее идеальными!) Для XML.

Почему они потеряны? [edit] И как я могу их сохранить? [/редактировать]

Вот сценарий демонстрационного файла (обратите внимание, что когда разрывы строк не находятся в атрибуте, они сохраняются).

PHP-файл со встроенным XML-файлом

$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>'; 

Выход из 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! ) ) 

Объект для новой строки: &#10; , Я играл с вашим кодом, пока не нашел что-то, что делало трюк. Это не очень элегантно, я предупреждаю вас:

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

Предположение, основанное на вашем примере, состоит в том, что любые новые строки, которые встречаются внутри узла или атрибута, будут иметь больше текста в следующей строке, а не < чтобы открыть новый элемент.

Это, конечно, потерпит неудачу, если в следующей строке есть текст, который был обернут элементом линейного уровня.

Используя SimpleXML, разрывы строк кажутся потерянными.

Да, это ожидается … на самом деле это требуется от любого согласованного XML-синтаксического анализатора, который newlines в значениях атрибута представляет собой простые пробелы. См. Нормализацию значения атрибута в спецификации XML.

Если в значении атрибута должен был быть подлинный символ новой строки, XML должен был включать в себя &#10; вместо ссылки на новую строку.

Предполагая, что $ xmlData – это ваша строка XML перед отправкой в ​​парсер, это должно заменить все символы новой строки в атрибутах правильной сущностью. У меня возникла проблема с XML, поступающим с 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 < 

Наверное, можно сделать проще с регулярным выражением, но это не очень важно для меня.

Вот код для замены новых строк соответствующей ссылкой на символ в этом конкретном фрагменте XML. Запустите этот код до разбора.

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

Это то, что сработало для меня:

Сначала введите xml как строку:

  $xml = file_get_contents($urlXml); 

Затем выполните замену:

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

«.» и "<as: eol />" были там, потому что мне нужно было добавить перерывы в этом случае. Новые строки «\ n» могут быть заменены на все, что вам нравится.

После замены просто загрузите xml-строку как объект SimpleXMLElement:

  $xmlo = new SimpleXMLElement( $xml ); 

И вуаля

Ну, этот вопрос старый, но, как и я, кто-то может прийти на эту страницу в конце концов. У меня был несколько иной подход, и я думаю, что из них было изящнее.

Внутри xml вы помещаете уникальное слово, которое вы будете использовать для новой строки.

Измените xml на

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

И тогда, когда вы получите путь к нужному узлу в SimpleXML в выводе строки, напишите что-то вроде этого:

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

Это не обязательно должно быть \ n, это может быть любой уникальный символ.