PHP SimpleXML не сохраняет разрывы строк в атрибутах XML - PullRequest
7 голосов
/ 22 сентября 2009

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

Почему они потерялись? [edit] А как их сохранить? [/ edit]

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

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

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

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

)

Ответы [ 6 ]

12 голосов
/ 22 сентября 2009

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

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

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

4 голосов
/ 22 сентября 2009

Объект для новой строки - &#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);

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

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

1 голос
/ 27 января 2017

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

$replaceFunction = function ($matches) {
    return str_replace("\n", "&#10;", $matches[0]);
};
$xml = preg_replace_callback(
    "/<data Title='[^']+' Remarks='[^']+'/i",
    $replaceFunction, $xml);
1 голос
/ 23 февраля 2011

Предполагая, что $ 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 <

Возможно, это можно сделать с помощью регулярных выражений, но для меня это не сильная сторона.

0 голосов
/ 27 ноября 2011

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

Внутри 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, это может быть любой уникальный символ.

0 голосов
/ 29 октября 2010

Вот что у меня сработало:

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

    $xml = file_get_contents($urlXml);

Затем сделайте замену:

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

"." и "" были там, потому что мне нужно было добавить разрывы в этом случае. Новые строки "\ n" можно заменить на все, что вам нравится.

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

    $xmlo = new SimpleXMLElement( $xml );

Et Voilà

...