Удалить начальные и конечные пробелы из элементов XML - PullRequest
5 голосов
/ 07 сентября 2011

Как я могу удалить все пробелы до и после поля XML?

<data version="2.0">

  <field> 

     1 

  </field>        

  <field something=" some attribute here... "> 

     2  

  </field>

</data>

Обратите внимание, что интервал до 1 и 2 и «некоторый атрибут здесь ...», я хочу удалить это с помощью PHP.

if(($xml = simplexml_load_file($file)) === false) die();

print_r($xml);

Кроме того, данные не являются строковыми, мне нужно добавить (строку) перед каждой переменной.Почему?

Ответы [ 3 ]

2 голосов
/ 07 сентября 2011

Вы можете использовать что-то вроде этого:

$str = file_get_contents($file);
$str = preg_replace('~\s*(<([^>]*)>[^<]*</\2>|<[^>]*>)\s*~','$1',$str);
$xml = simplexml_load_string($xml,'SimpleXMLElement', LIBXML_NOCDATA);

Я не пробовал это, но вы можете найти больше на этом в http://www.lonhosford.com/lonblog/2011/01/07/php-simplexml-load-xml-file-preserve-cdata-remove-whitespace-between-nodes-and-return-json/.

Обратите внимание, что пробелы междуоткрывающие и закрывающие скобки (<x> _space_ </x>) и атрибуты (<x attr=" _space_ ">) фактически являются частью данных XML-документа (в отличие от пробелов между <x> _space_ <y>), поэтому я хотел бы предложить, чтобы используемый вами источник былнемного меньше беспорядка с пробелами.

1 голос
/ 03 августа 2015

Чтобы сделать это в 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();
1 голос
/ 07 сентября 2011

Поскольку simplexml_load_file() считывает данные в массив, вы можете сделать что-то вроде этого:

function TrimArray($input){

    if (!is_array($input))
        return trim($input);

    return array_map('TrimArray', $input);
}
...