Чтобы сделать это в PHP, сначала нужно преобразовать документ в DOMDocument , чтобы вы могли обращаться к узлам, в которых вы хотите нормализовать пропуски, с помощью DOMXPath . (Xpath in) SimpleXMLElement слишком ограничен для доступа к текстовым узлам достаточно точно, как это потребуется для этой операции.
Xpath-запрос для доступа ко всем текстовым узлам, которые находятся в конечных элементах и ко всем атрибутам:
//*[not(*)]/text() | //@*
Учитывая, что $xml
является SimpleXMLElement , вы можете выполнить нормализацию пустого пространства, как в следующем примере:
$doc = dom_import_simplexml($xml)->ownerDocument;
$xpath = new DOMXPath($doc);
foreach ($xpath->query('//*[not(*)]/text()|//@*') as $node) {
/** @var $node DOMText|DOMAttr */
$node->nodeValue = trim(preg_replace('~\s+~u', ' ', $node->nodeValue), ' ');
}
Возможно, вы можете растянуть это на все текстовые узлы (, как предложено в соответствующих вопросах и ответах ), но это может потребовать нормализации документа при определенных обстоятельствах. Поскольку text()
в Xpath не отличается между текстовыми узлами и разделами Cdata, вы можете пропустить эти типы узлов ( DOMCdataSection ) или развернуть их в текстовые узлы при загрузке документа (для этого используйте параметр LIBXML_NOCDATA
), чтобы получить более полезные результаты.
Также данные не являются строковыми, мне нужно добавлять (строковые) перед каждой переменной. Почему?
Поскольку это объект типа SimpleXMLElement , если вам нужно строковое значение такого объекта (элемента), вам необходимо привести его к строковому значению. Смотрите также следующий справочный вопрос:
И последнее, но не менее важное: не доверяйте print_r
или var_dump
, когда вы используете его на SimpleXMLElement : это не показывает правду. Например. Вы можете переопределить __toString()
, что также может решить вашу проблему:
class TrimXMLElement extends SimpleXMLElement
{
public function __toString()
{
return trim(preg_replace('~\s+~u', ' ', parent::__toString()), ' ');
}
}
$xml = simplexml_load_string($buffer, 'TrimXMLElement');
print_r($xml);
Даже если приведение к строке обычно применяется (например, с echo
), вывод print_r
все равно не будет отражать эти изменения. Так что лучше не полагаться на это, оно никогда не сможет показать всю картину.
Полный пример кода к этому ответу ( Online Demo ):
<?php
/**
* Remove starting and ending spaces from XML elements
*
* @link https://stackoverflow.com/a/31793566/367456
*/
$buffer = <<<XML
<data version="2.0">
<field>
1
</field>
<field something=" some attribute here... ">
2 <![CDATA[ 34 ]]>
</field>
</data>
XML;
class TrimXMLElement extends SimpleXMLElement implements JsonSerializable
{
public function __toString()
{
return trim(preg_replace('~\s+~u', ' ', parent::__toString()), ' ');
}
function jsonSerialize()
{
$array = (array) $this;
array_walk_recursive($array, function(&$value) {
if (is_string($value)) {
$value = trim(preg_replace('~\s+~u', ' ', $value), ' ');
}
});
return $array;
}
}
$xml = simplexml_load_string($buffer, 'TrimXMLElement', LIBXML_NOCDATA);
print_r($xml);
echo json_encode($xml);
$xml = simplexml_load_string($buffer, null, LIBXML_NOCDATA);
$doc = dom_import_simplexml($xml)->ownerDocument;
$doc->normalizeDocument();
$doc->normalize();
$xpath = new DOMXPath($doc);
foreach ($xpath->query('//*[not(*)]/text()|//@*') as $node) {
/** @var $node DOMText|DOMAttr|DOMCdataSection */
if ($node instanceof DOMCdataSection) {
continue;
}
$node->nodeValue = trim(preg_replace('~\s+~u', ' ', $node->nodeValue), ' ');
}
echo $xml->asXML();