【问题标题】:Get xml file to find and replace text. PHP获取 xml 文件以查找和替换文本。 PHP
【发布时间】:2019-01-25 12:43:54
【问题描述】:

我需要使用 PHP 代码更改 XML 文件中的文本。然后我创建了一个代码:

1- 获取文件

2- 替换文本

3- 用其他名称保存文件。

问题是我在替换 xml 文件中的某些文本时遇到了一些问题。

我可以替换简单的字符串,但不能用“

原始 XML 路径:http://www.csainmobiliaria.com/imagenes/fotos/pisos-NOK.xml

1) 此代码只是将文本 Inmuebles 更改为 xxxxxxxx这很好用

    $xml_external_path = 'http://www.csainmobiliaria.com/imagenes/fotos/pisos-NOK.xml';
$xml = file_get_contents($xml_external_path);

$response = strtr($xml, array(
    'Inmuebles' => 'xxxxxxxx'
));

$newXml = $response;

$newXml = simplexml_load_string( $newXml );
$newXml->asXml('/home/csainmobiliaria/www/pisos-NEW.xml');

2) 现在,如果我使用此代码将文本 <Table Name="Inmuebles"> 更改为 <xxxxxxxx> 我收到 ERROR 500。

    $xml_external_path = 'http://www.csainmobiliaria.com/imagenes/fotos/pisos-NOK.xml';
$xml = file_get_contents($xml_external_path);

$response = strtr($xml, array(
    '<Table Name="Inmuebles">' => '<xxxxxxxx>'
));

$newXml = $response;

$newXml = simplexml_load_string( $newXml );
$newXml->asXml('/home/csainmobiliaria/www/pisos-NEW.xml');

3) 以同样的方式,如果我使用此代码删除文本 Publicacion 我会收到 ERROR 500。

    $xml_external_path = 'http://www.csainmobiliaria.com/imagenes/fotos/pisos-NOK.xml';
$xml = file_get_contents($xml_external_path);

$response = strtr($xml, array(
    '<Publicacion>' => ''
));

$newXml = $response;

$newXml = simplexml_load_string( $newXml );
$newXml->asXml('/home/csainmobiliaria/www/pisos-NEW.xml');

这是我需要得到的最终结果:http://www.csainmobiliaria.com/imagenes/fotos/pisos-OK.xml

捕获:

【问题讨论】:

  • &lt;Table Name="Inmuebles"&gt;&lt;xxxxxxxx&gt; 使关闭 &lt;/Table&gt; 无效,关闭 &lt;xxx..&gt; 不存在。使用解析器并执行此操作。此外,当您get a ERROR 500 检查您的错误日志时,它会告诉您出了什么问题。如果它不查看错误报告功能的手册。 &lt;Publicacion&gt; 方法也有同样的问题。不要对结构化数据(CSV、JSON、XML 等)使用字符串函数,使用适当的解析器。
  • @user3783243 恐怕我不知道什么是“解析器”。你的意思是string int搜索功能吗?
  • simplexml 是一个解析器。您应该将文件原样放入其中,根据需要对其进行重组,然后输出。 (如果您不喜欢其他解析器,也可以使用其他解析器)
  • XSLT 是一种仅用于此用例的模板语言 - 它将一个 XML 转换为另一个 XML、HTML 或文本。 PHP 有一个扩展 (ext/xsl)。

标签: php xml xslt xml-parsing simplexml


【解决方案1】:

DOMDocument 允许您复制节点的结构,因此不必单独复制所有细节(当规范更改时可能容易丢失数据),您可以复制整个节点(例如&lt;Inmueble&gt; ) 使用importNode() 从一个文档到另一个文档,它有一个参数来指示应该复制元素的全部内容。这种方法还允许您使用相同的函数复制任何表,而无需更改代码...

function extractData ( $sourceFile, $table )    {
    // Load source data
    $source = new DOMDocument();
    $source->load($sourceFile);
    $xp = new DOMXPath($source);

    // Create new data document
    $newFile = new DOMDocument();
    $newFile->formatOutput = true;
    // Create base element with the table name in new document
    $newRoot = $newFile->createElement($table);
    $newFile->appendChild($newRoot);

    // Find the records to copy
    $records = $xp->query('//Table[@Name="'.$table.'"]/*');
    foreach ( $records as $record ) {
        // Import the node to copy and append it to new document
        $newRoot->appendChild();
    }
    // Return the source of the XML
    return $newFile->saveXML();
}

echo extractData ($xml_external_path, "Inmuebles");

如果您希望进一步处理文档,您可以更改方法以将文档返回为 DOMDocument 甚至是 SimpleXML 版本。

对于 SimpleXML,将返回更改为...

return simplexml_import_dom($newRoot);

然后你可以称它为...

$ret = extractData ($xml_external_path, "Inmuebles");
echo $ret->asXML();

或者如果您只是想要一种固定的方式来执行此操作,您可以删除 XPath 并使用 getElementsByTagName() 查找要复制的节点...

$source = new DOMDocument();
$source->load($xml_external_path);

$newFile = new DOMDocument();
$newRoot = $newFile->createElement("Inmuebles");
$newFile->appendChild($newRoot);

// Find the records to copy
foreach ( $source->getElementsByTagName("Inmueble") as $record ) {
    $newRoot->appendChild($newFile->importNode($record, true));
}
echo $newFile->saveXML();

为了添加保存文件名,我在函数中添加了一个新参数,这个新函数根本不返回任何内容 - 它只是加载文件并将结果保存到新文件名中......

function extractData ( $sourceFile, $table, $newFileName )    {
    // Load source data
    $source = new DOMDocument();
    $source->load($sourceFile);
    $xp = new DOMXPath($source);

    // Create new file document
    $newFile = new DOMDocument();
    $newFile->formatOutput = true;
    // Create base element with the table name in new document
    $newRoot = $newFile->createElement($table);
    $newFile->appendChild($newRoot);

    // Find the records to copy
    $records = $xp->query('//Table[@Name="'.$table.'"]/*');
    foreach ( $records as $record ) {
        // Import the node to copy and append it to new document
        $importNode = $newFile->importNode($record, true);
        // Add new content
        $importNode->appendChild($newFile->createElement("Title", "value"));
        $newRoot->appendChild();
    }

    // Update Foto elements
    $xp = new DOMXPath($newFile);
    $fotos = $xp->query("//*[starts-with(local-name(), 'Foto')]");
    foreach ( $fotos as $foto ) {
        $path = $foto->nodeValue;
        if( substr($path, 0, 5) == "/www/" )    {
            $path = substr($path,4);
        }
        // Replace node with new version
        $foto->parentNode->replaceChild($newFile->createElement("Foto1", $path), 
                  $foto);
    }  

    $newFile->save($newFileName);
}
$xml_external_path = 'http://www.csainmobiliaria.com/imagenes/fotos/pisos.xml';
$xml_external_savepath = 'saveFile.xml';

extractData ($xml_external_path, "Inmuebles", $xml_external_savepath);

【讨论】:

  • @Nige_Ren 我正在尝试您的第一个代码。我需要知道如何用其他名称保存新的 xml。
  • 如果您的意思是将 XML 保存到文件中,您可以使用 file_put_contents("outputFileName.xml", extractData ($xml_external_path, "Inmuebles")); 保存数据
  • @Nige_Ren 谢谢,我在哪里插入这一行。能不能封装完整的函数?
  • 我添加了一个新版本的函数,您可以在其中传递文件名以保存结果。
  • @Nige_Ren 谢谢,你的最后一个代码function extractData ( $sourceFile, $table, $newFileName )... 工作正常。
【解决方案2】:

您可以复制必要的节点而不是删除任何多余的元素。例如,您可以在 SimpleXML 的帮助下复制Inmuebles 节点:

$path = 'http://www.csainmobiliaria.com/imagenes/fotos/pisos-NOK.xml';
$content = file_get_contents($path);
$sourceXML = new SimpleXMLElement($content);

$targetXML = new SimpleXMLElement("<Inmuebles></Inmuebles>");

$items = $sourceXML->xpath('Table[@Name=\'Inmuebles\']');
foreach ($items as $item) {
    foreach ($item->Inmueble as $inmueble) {
        $node  = $targetXML->addChild('Inmueble');
        $node->addChild('IdInmobiliariaExterna', $inmueble->IdInmobiliariaExterna);
        $node->addChild('IdPisoExterno', $inmueble->IdPisoExterno);
        $node->addChild('FechaHoraModificado', $inmueble->FechaHoraModificado);
        $node->addChild('TipoInmueble', $inmueble->TipoInmueble);
        $node->addChild('TipoOperacion', $inmueble->TipoOperacion);
    }
}

echo $targetXML->asXML()

另外,正如@ThW 在 cmets 中所说,您可以使用XLST,例如:

$path = 'http://www.csainmobiliaria.com/imagenes/fotos/pisos-NOK.xml';
$content = file_get_contents($path);
$sourceXML = new SimpleXMLElement($content);

$xslt='<?xml version="1.0" encoding="ISO-8859-1"?>
         <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
         <xsl:output method="xml" indent="yes"/>

         <xsl:template match="Table[@Name=\'Inmuebles\']">
             <Inmuebles>
                 <xsl:copy-of select="node()"/>
             </Inmuebles>
         </xsl:template>

         <xsl:template match="Table[@Name=\'Agencias\']"/>
</xsl:stylesheet>';


$xsl = new SimpleXMLElement($xslt);

$processor = new XSLTProcessor;
$processor->importStyleSheet($xsl);
$result = $processor->transformToXML($sourceXML);
$targetXML = new SimpleXMLElement($result);
echo $targetXML->asXML();

【讨论】:

  • 第一个代码效果很好。只有一个问题:其中一个元素包含 html 代码&lt;br/&gt;,它不会被复制(迁移)到新的 xml。我该如何解决这个问题?谢谢。
  • @JPashs 你能附上XML的例子吗?
  • 这里是真正的 xlm 的 url:csainmobiliaria.com/imagenes/fotos/pisos.xml 在这里你可以看到 html 标签:postimg.cc/XrQDw9Xt 在我运行代码后,&lt;br/&gt; html 选项卡从文本中删除.
  • @Maxim_Fedorov 你看到我最后的评论了吗。
  • @JPashs &lt;Descripcion&gt;3 DORMITORIOS,1 CUARTO DE BAÑO &lt;br&gt;&lt;/br&gt;&lt;/Descripcion&gt; 是无效的 XML。因此 SimpleXML 会截断 HTML 标记。元素必须在 块 中包含 HTML
【解决方案3】:

再次考虑XSLT,这是一种符合 W3C 标准的专用语言,旨在将 XML 文件修改为所需的用户规范,例如您的 #1-3 需求。与其他流行的声明性语言 SQL 一样,XSLT仅限于 PHP,而是可移植到其他应用程序层(Java、C#、Python、Perl、R)和专用的XSLT 1.0, 2.0, and 3.0 .exe processors

通过这种方法,XSLT 的递归样式允许您避免任何foreach 循环、if 逻辑以及重复行,如在应用程序层调用addChildappendChild

XSLT (保存为 .xsl 文件、特殊 .xml 文件或嵌入字符串;可移植到 PHP 以外的其他接口)

<?xml version="1.0"?>
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output method="xml" indent="yes" encoding="ISO-8859-1"/>
     <xsl:strip-space elements="*"/>

     <!-- WALK DOWN TREE FROM ROOT -->
     <xsl:template match="Publication">
        <xsl:apply-templates select="Table"/>
     </xsl:template>

     <xsl:template match="Table[@Name='Inmuebles']">
         <Inmuebles>
             <xsl:apply-templates select="*"/>
         </Inmuebles>
     </xsl:template>

     <!-- EMPTY TEMPLATE TO REMOVE SPECIFIED NODES -->
     <xsl:template match="Table[@Name='Agencias']"/>

     <!-- RETURN ONLY FIRST FIVE NODES -->
     <xsl:template match="Table/*">
         <Inmuebles>
             <xsl:copy-of select="*[position() &lt;= 5]"/>
         </Inmuebles>
     </xsl:template>

</xsl:stylesheet>

XSLT Demo

PHP (使用php_xsl 库)

// LOAD XML SOURCE
$url = 'http://www.csainmobiliaria.com/imagenes/fotos/pisos-NOK.xml';
$web_data = file_get_contents($url);
$xml = new SimpleXMLElement($web_data);

// LOAD XSL SCRIPT
$xsl = simplexml_load_file('/path/to/script.xsl');

// XSLT TRANSFORMATION
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl); 
$newXML = $proc->transformToXML($xml);

// OUTPUT TO CONSOLE
echo $newXML;

// SAVE TO FILE
file_put_contents('Output.xml', $newXML);

作为伟大的 XSLT 大师,@Dimitre Novatchev,通常会结束他的帖子:产生想要的正确结果

<?xml version="1.0" encoding="ISO-8859-1"?>
<Inmuebles>
   <Inmuebles>
      <IdInmobiliariaExterna>B45695855</IdInmobiliariaExterna>
      <IdPisoExterno>100002</IdPisoExterno>
      <FechaHoraModificado>30/11/2018</FechaHoraModificado>
      <TipoInmueble>PISO</TipoInmueble>
      <TipoOperacion>3</TipoOperacion>
   </Inmuebles>
   <Inmuebles>
      <IdInmobiliariaExterna>B45695855</IdInmobiliariaExterna>
      <IdPisoExterno>100003</IdPisoExterno>
      <FechaHoraModificado>30/11/2018</FechaHoraModificado>
      <TipoInmueble>CHALET</TipoInmueble>
      <TipoOperacion>4</TipoOperacion>
   </Inmuebles>
</Inmuebles>

【讨论】:

    猜你喜欢
    • 2019-10-16
    • 1970-01-01
    • 1970-01-01
    • 2014-08-17
    • 1970-01-01
    • 2014-07-05
    • 2021-11-06
    • 2020-07-29
    • 2023-03-26
    相关资源
    最近更新 更多