我有两种类型的XML文件,其中包含复古游戏的细节.我正在try 动态编辑文件以搜索和替换游戏标题.

我不确定这个问题是否需要两个不同的解决方案,但我想我会在这里详细说明,希望您能够 bootstrap 我找到一个解决方案.

第一个XML文件具有这种类型的 struct :

<?xml version="1.0" standalone="yes"?>
<LaunchBox>
  <Game>
    <Status>Imported ROM</Status>
    <DatabaseID>22190</DatabaseID>
    <Title>ME-Title Match Pro Wrestling</Title>
    <UseDosBox>false</UseDosBox>
    <Version>(USA)</Version>
  </Game>
  <Game>
    <Status>Imported ROM</Status>
    <DatabaseID>30128</DatabaseID>
    <Title>Skeet Shoot</Title>
    <UseDosBox>false</UseDosBox>
    <Version>(USA)</Version>
  </Game>
  <Game>
    <Status>Imported ROM</Status>
    <DatabaseID>28694</DatabaseID>
    <Title>Star Strike</Title>
    <UseDosBox>false</UseDosBox>
    <Version>(USA)</Version>
  </Game>
</LaunchBox>

第二个文件(扩展名为.dat,但内部看起来像一个XML)的 struct 如下:

<?xml version="1.0"?>
<!DOCTYPE datafile PUBLIC "-//Logiqx//DTD ROM Management Datafile//EN">
<datafile>
    <header>
        <name>MatchingDATTest</name>
    </header>
    <game name="KeepAway (USA)">
        <category>Games</category>
    </game>
    <game name="Super Football Another Game">
        <category>Games</category>
    </game>
    <game name="River Raid II (USA)">
        <category>Games</category>
    </game>
    <game name="London Blitz (USA)">
        <category>Games</category>
    </game>
</datafile>

我希望能有这样的代码,基本上就是...

查找<Title>Star Strike</Title>并替换为<Title>Star Wars</Title>

或者,在其他类型的文件的情况下...

查找<game name="KeepAway (USA)">并替换为<game name="KeepAway 2 (USA)">

我不确定解决这个问题的最好方法是什么?是否fopen个文件,然后以某种方式逐行通过它,但这些文件可能会变得非常大(多达fopen,000个游戏),所以我想这可能不实用?在过go 的几天里,经过大量的谷歌搜索和实验,我从其他类似问题的解决方案中拼凑出了这段代码.

这是在try 处理我发布的第一个XML示例.

$reader = new XMLReader();
$reader->open($fileToEdit);
$document = new DOMDocument();
$xpath = new DOMXpath($document);
$found = false;
// look for the document element
do {
  $found = $found ? $reader->next() : $reader->read();
} while (
  $found && 
  $reader->localName !== 'LaunchBox'
);
// go to first child of the document element
if ($found) {
    $found = $reader->read();
}
// found a node at depth 1 
while ($found && $reader->depth === 1) {
    // We need to check if we're dealing with an Element
    if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'Game')
    {
        // Let's inspect the node's content as well
        while ($reader->read())
        {
            if ($reader->nodeType == XMLReader::TEXT)
            {
                $thisValue = $reader->value;
                if( $thisValue == $gameTitleToSearchFor ){
                    $reader->value = $gameTitleToReplaceWith;
                    break 2;                      
                }

            }
        }
    }
    $found = $reader->next();
}
$dom->save($fileToEdit);
$reader->close();

但我收到一个错误,说是Cannot write to read-only property,所以我挥舞着白旗,因为目前这超出了我的能力范围,所以我想知道是否有人可以帮助我?

推荐答案

考虑使用PHPXSLTProcessor类的参数化XSLT,在转换之前将参数从PHP传递到样式表.

XSLT,(save as .xsl, a special .xml file).

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

    <!-- DEFINE PARAMS -->
    <xsl:param name="find-value"/>
    <xsl:param name="replace-value"/>

   <!-- UPDATE ANY ELEMENT TEXT -->
    <xsl:template match="*">
      <xsl:copy>
          <xsl:choose>
            <xsl:when test="text() = $find-value">
                <xsl:value-of select="$replace-value"/>
                <xsl:apply-templates select="@*"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:otherwise>
          </xsl:choose>
      </xsl:copy>
    </xsl:template>
    
    <!-- UPDATE ANY ATTRIBUTE VALUE -->
    <xsl:template match="@*">
      <xsl:attribute name="{name()}">
          <xsl:choose>
            <xsl:when test=". = $find-value">
                <xsl:value-of select="$replace-value"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="."/>
            </xsl:otherwise>
          </xsl:choose>
      </xsl:attribute>
    </xsl:template>
    
</xsl:stylesheet>

PHP


function UpdateXML($xmlFile, $findParam, $replaceParam) {
    // LOAD XML
    $xml = new DOMDocument('1.0', 'UTF-8');
    $xml->load($xmlFile);
    
    // LOAD XSLT 
    $xsl = new DOMDocument('1.0', 'UTF-8');   
    $xsl->load('XSLT_Script.xsl');
    
    // INITIALIZE TRANSFORMER
    $proc = new XSLTProcessor;
    $proc->importStyleSheet($xsl);
    
    // PASS PARAMETER VALUES
    $proc->setParameter('', 'find-value', $findParam);
    $proc->setParameter('', 'replace-value', $replaceParam);

    // TRANSFORM SOURCE
    $new_xml = $proc->transformToXML($xml);
    
    // RETURN OUTPUT
    return $new_xml;
}

// UPDATE XML
$new_launchbox_xml = UpdateXML("Launchbox.xml", "Star Strike", "Star Wars");
// SAVE TO FILE
file_put_contents("NewLaunchbox.xml", $new_launchbox_xml);


// UPDATE XML
$new_games_xml = UpdateXML("Games.dat", "KeepAway (USA)", "KeepAway 2 (USA)");
// SAVE TO FILE
file_put_contents("NewGames.xml", $new_games_xml);

如果需要 for each XML文档传递多个值,请更新该方法以接收相同长度的查找和替换值array.然后,将for循环合并到方法中以迭代调用setParameter.

Php相关问答推荐

为什么注册SIGHUP的处理程序函数会阻止在PHP CLI中等待输入时点击X关闭Xterm窗口?""

在WooCommerce产品变体SKU字段旁边添加自定义输入字段

由于某种原因,FILE_GET_CONTENTS从一天到另一天停止工作,并在PHP 7.2.34中返回一条&http/1.1 406不能接受&的错误

WooCommerce-在选定类别中每消费X个金额即可添加免费礼物

在WooCommerce管理中 for each 运输方法设置添加自定义字段

从AJAX响应中获取一个未定义的html

如何使用MS Graph PHP SDK V2下载文件

Laravel路由到控制器传输变量解决方案需要

在 php 中生成 MAC ISO/IEC 9797-1

在 WooCommerce wc_get_products 函数中处理自定义分类法

基于WooCommerce中的产品变体自定义感谢页面跳转

PHP文件如何从CSS执行

如何使用 WhatsApp Cloud API 向 WhatsApp 发送消息,而无需在发送消息之前注册收件人号码?

每个发布请求 Laravel 9 的 CSRF 令牌不匹配

在 laravel 中更新表单时如何在 Select 选项中保留旧值?

将样式文件或脚本引入WordPress模板

htaccess 无限循环问题

获取具有所有特定相关模型的模型

路由未定义的 laravel 导出

将加密/解密函数从 PHP 转换为 NodeJS