从XML元素中删除开始和结束空格

时间:2011-09-07 17:26:04

标签: php xml string simplexml

如何删除XML字段前后的所有间距字符?

<data version="2.0">

  <field> 

     1 

  </field>        

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

     2  

  </field>

</data>

请注意1和2之间的间距以及'some attribute here ...',我想用PHP删除它。

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

print_r($xml);

此外数据似乎不是字符串,我需要在每个变量之前追加(字符串)。为什么呢?

3 个答案:

答案 0 :(得分:2)

你可能想要使用这样的东西:

$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 :(得分:1)

由于simplexml_load_file()将数据读入数组,您可以执行以下操作:

function TrimArray($input){

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

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

答案 2 :(得分:1)

要在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), ' ');
}

您可以将其扩展到所有文本节点(as suggested in related Q&A),但这可能需要在环境下进行文档规范化。由于Xpath中的text()在文本节点和Cdata节之间没有区别,您可能希望跳过这些类型的节点(DOMCdataSection)或在加载文档时将它们扩展为文本节点(使用the LIBXML_NOCDATA option以获得更有用的结果。

  

此外数据似乎不是字符串,我需要在每个变量之前追加(字符串)。为什么呢?

因为它是 SimpleXMLElement 类型的对象,如果你想要这样一个对象(元素)的字符串值,你需要将它强制转换为字符串。请参阅以下参考问题:

最后但并非最不重要:当您在 SimpleXMLElement 上使用它时,请不要信任print_rvar_dump:它没有显示真相。例如。您可以覆盖__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();