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



如何删除XML字段前后的所有空格字符?

<data version="2.0">
  <field> 
     1 
  </field>        
  <field something=" some attribute here... "> 
     2  
  </field>
</data>

注意1和2之前的空格和这里的'some attribute…',我想用PHP删除它。

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

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

您可能想使用这样的内容:

$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>之间的空格相反),因此我建议您使用的源代码应该少一些空格。

要在PHP中做到这一点,您首先必须将文档转换为DOMDocument,以便您可以通过DOMXPath寻址您想要规范化空白的节点。(xpath in) SimpleXMLElement访问文本节点的限制太大,无法满足此操作的需要。

访问叶子元素和所有属性中的所有文本节点的xpath查询是:

//*[not(*)]/text() | //@*

考虑到$xmlSimpleXMLElement,您可以像以下示例一样进行空白归一化:

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

您也许可以将其扩展到所有文本节点(如相关问题A中建议的那样),但在特定情况下,这可能需要文档规范化。由于Xpath中的text()在文本节点和Cdata-sections之间没有区别,因此您可能希望跳过这些类型的节点(DOMCdataSection),或者在加载文档时将它们扩展为文本节点(为此使用LIBXML_NOCDATA选项),以获得更有用的结果。


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

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

  • 将SimpleXML对象强制为字符串,而不考虑上下文

最后但并非最不重要的是:当您在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);

即使转换为string通常会应用(例如echo), print_r的输出仍然不会反映这些变化。所以最好不要依赖它,它永远不能反映全貌。


这个答案的完整示例代码(在线演示):

<?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();

由于simplexml_load_file()将数据读取到数组中,因此您可以这样做:

function TrimArray($input){
    if (!is_array($input))
        return trim($input);
    return array_map('TrimArray', $input);
}

最新更新