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

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

Почему они потерялись? [edit] И как их сохранить? [/ 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!
        )

)

person Joshua    schedule 21.09.2009    source источник
comment
Вы должны задать этот вопрос на домашней странице PHP. Думаю, это потому, что это ПРОСТОЙ синтаксический анализатор xml.   -  person jbasko    schedule 22.09.2009
comment
Не могли бы вы подробнее объяснить, что вы имеете в виду под домашней страницей PHP?   -  person Joshua    schedule 22.09.2009
comment
Изначально ваш вопрос был в том, почему SimpleXML делает то, что делает? Вот что вы можете спросить, это разработчики, а не пользователи.   -  person jbasko    schedule 22.09.2009
comment
Попался - спасибо за рекомендацию, Зилупе. Теперь этот bobince ответил. Почему SimpleXML делает то, что делает? Я думаю, что сохраню это в stackoverflow, чтобы, надеюсь, кто-то мог добавить, какие еще параметры у меня есть, чтобы сохранить разрывы строк!   -  person Joshua    schedule 22.09.2009


Ответы (6)


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

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

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

person Anthony    schedule 21.09.2009
comment
Очень умный!!! Единственная загвоздка в том, что я работаю с огромным количеством XML-данных в оболочке SOAP, извергаемых веб-службами SharePoint, поэтому я немного нервничаю, делая что-то столь грубое. Однако, основываясь на сообщении Бобинса, похоже, что мне, возможно, придется пойти в этом направлении. Интересно, есть ли более элегантный способ справиться с этим. - person Joshua; 22.09.2009

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

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

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

person bobince    schedule 22.09.2009
comment
Чтобы немного прояснить: новые строки VALID, но синтаксический анализатор XML (для соответствия спецификации) ДОЛЖЕН сократить их до одного символа пробела ( см. пункт 3 ссылки bobince). - person TML; 22.09.2009
comment
Спасибо за ссылку bobince и за пояснение TML. Итак, я полагаю, теперь мой вопрос заключается в том, как я могу сохранить эти разрывы строк? Я получаю эти данные из веб-службы SharePoint, поэтому я не могу изменить XML, чтобы включить & # 10. Есть ли способ переопределить соответствие парсера в этом отношении? - person Joshua; 22.09.2009
comment
К сожалению, нет, XML в этом отношении весьма негибкий; если веб-служба создает \n, когда это означает &#10;, что это ошибка. (И удивительно, поскольку это фундаментальная функция, которую можно ожидать от любого сериализатора XML ... если, конечно, служба не возится с регулярными выражениями или шаблонами строк вместо использования правильной библиотеки XML!) - person bobince; 22.09.2009
comment
Если у вас нет доступа к подклассу или обезьяньему исправлению вашего XML-парсера, вы не сможете это изменить ... и я думаю, что SimpleXML использует libxml, с которым вы не надеетесь возиться с PHP. Предварительная обработка общего ввода XML для вставки &#10;s также не является стартовой задачей, так как вам придется уже написать большую часть синтаксического анализатора XML, чтобы иметь возможность различать новую строку в значении атрибута и единицу. непосредственно внутри тега (где &#10; было бы недопустимо). Такие хаки, как Anthony, могут работать как временное исправление, если точное форматирование в настоящий момент сильно заблокировано. - person bobince; 22.09.2009
comment
(извините за code там, похоже, это недостаток в разметке SO вокруг &...; или что-то в этом роде ...) - person bobince; 22.09.2009

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

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

person Ryan    schedule 23.02.2011
comment
Собственно проблема в том, что новые строки технически недопустимы в атрибутах XML. Однако парсеры, как правило, многое исправляют. Во всех случаях недопустимые объекты должны быть закодированы. Лучшим решением было бы исправить источник, но это кажется законным, если он недоступен. - person Kevin Peno; 29.11.2012

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

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

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

Сначала получите 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 );

И вуаля

person German    schedule 29.10.2010

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

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

person Community    schedule 27.11.2011