使用 XSLT 和 PHP 和 return 修改大型 XML 文件给用户

Modify large XML file with XSLT and PHP and return to user

我正在寻求一些关于修改上传到服务器的 xml 文件(Garmin TCX 文件)然后 return 修改后的版本在浏览器不超时的情况下向用户提供的最佳方法的建议。我需要解析上传的文件并添加一些额外的元素。例如,我需要这些 Trackpoint 标签中的每一个:

 <?xml version="1.0"?>
<TrainingCenterDatabase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">
  <Activities>
    <Activity Sport="Biking">
      <Id>2018-01-08T18:15:32Z</Id>
      <Lap StartTime="2017-12-16T12:43:09Z">
        <TotalTimeSeconds>5023.91015625</TotalTimeSeconds>
        <DistanceMeters>39999.578125</DistanceMeters>
        <MaximumSpeed>15</MaximumSpeed>
        <Calories>0</Calories>
        <Intensity>Active</Intensity>
        <Cadence>75</Cadence>
        <TriggerMethod>Manual</TriggerMethod>
        <Track>
          <Trackpoint>
            <Time>2017-12-16T12:43:10Z</Time>
            <DistanceMeters>0</DistanceMeters>
            <Cadence>1</Cadence>
            <Extensions>
              <TPX xmlns="http://www.garmin.com/xmlschemas/ActivityExtension/v2">
                <Speed>0</Speed>
                <Watts>1</Watts>
                <Slope>-1.49</Slope>
              </TPX>
            </Extensions>
          </Trackpoint>
          .....  

成为:

<Trackpoint>
    <Time>2017-12-16T12:43:11Z</Time>
    <DistanceMeters>0</DistanceMeters>
    <Cadence>1</Cadence>
    <Extensions>
        <TPX xmlns="http://www.garmin.com/xmlschemas/ActivityExtension/v2">
            <Speed>0</Speed>
            <Watts>1</Watts>
            <Slope>-1.49</Slope>
        </TPX>
    </Extensions>
    <AltitudeMeters>106.6</AltitudeMeters>
    <Position>
        <LatitudeDegrees>55.02935</LatitudeDegrees>
        <LongitudeDegrees>-8.140617</LongitudeDegrees>
    </Position>
</Trackpoint>

我已将位置数据和距离加载到数组中 - 已从 GPX 文件转换而来。对于每个跟踪点,我在数组中搜索距离,然后将相关位置数据附加到跟踪点标签。 我正在处理的文件可能是 5MB-20MB,所以我不确定最好的方法是什么。 我知道 DOM 解析器是最简单的,但也是内存最密集和最慢的。 我正在考虑使用 XMLREADER 来解析文件,然后使用 XMLWRITER 将所有 trackpoint 元素 + 位置数据写入另一个文件。 我无权访问服务器,因此只能在本地计算机上进行测试。 任何意见是极大的赞赏。 詹姆斯.

更新1 我有一个从包含距离、纬度、经度和高程数据的 csv 文件加载的多维数组。我从 trackpoint 标签读取距离并将其用作 array(key=distance) 的键,然后 return 相关的纬度和经度数据。然后我创建位置、纬度和经度元素 + 文本节点并附加到跟踪点标签。

更新2 使用 Parfait 的建议 - 使用 xsl 添加额外的元素。 运行 进入默认命名空间的问题。我不能 select 任何 Trackpoint 元素。使用以下方法修复此问题:

 <?xml version="1.0" ?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" 
xmlns:e="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">

 <xsl:output method="xml" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <!-- Identity Transform -->
 <xsl:template match="@* | node()" name="identity-copy">
  <xsl:copy>
   <xsl:apply-templates select="@* | node()"/>
  </xsl:copy> 
 </xsl:template>

 <xsl:template match="e:TrainingCenterDatabase/e:Activities/e:Activity/e:Lap/e:Track/e:Trackpoint">
  <xsl:copy>   
  <xsl:copy-of select="*"/> 
    <AltitudeMeters xmlns = "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">xxxx</AltitudeMeters>
    <Position xmlns = "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">
    <LatitudeDegrees xmlns = "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">laaa</LatitudeDegrees>
    <LongitudeDegrees xmlns = "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">looo</LongitudeDegrees>
    </Position>
  </xsl:copy>
 </xsl:template>    
</xsl:stylesheet>

考虑 XSLT, the special-purpose language designed to transform XML files and at those specified file sizes should be fast rendering. PHP can run XSLT 1.0 with its php-xsl class。

并且由于您正在遍历 CSV 文件以获取条件集 XSLT 值,请考虑将来自 PHP 的值作为参数传递到匹配 Distance.[= 时指定的占位符中13=]

XSLT (另存为 .xsl 文件)

<?xml version="1.0" ?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
                xmlns:doc="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"              
                exclude-result-prefixes="doc">  
   <xsl:output method="xml" indent="yes"/>
   <xsl:strip-space elements="*"/>

   <!-- Initializing Parameters -->
   <xsl:param name="DistanceParam"/>
   <xsl:param name="AltParam"/>
   <xsl:param name="LatParam"/>
   <xsl:param name="LngParam"/>

    <!-- Copy elements -->
    <xsl:template match="*">
        <xsl:element name="{local-name()}">
            <xsl:apply-templates select="@* | node()"/>
        </xsl:element>
    </xsl:template>

   <!-- Walk Down Tree Levels -->
   <xsl:template match="doc:TrainingCenterDatabase|doc:Activities|doc:Activity">
      <xsl:apply-templates select="*"/>
   </xsl:template>

    <xsl:template match="doc:Id"/>
    <xsl:template match="doc:Lap">
      <xsl:apply-templates select="doc:Track"/>
    </xsl:template>

    <!-- Extract Specific TrackPoint -->    
    <xsl:template match="doc:Track">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="doc:Trackpoint[doc:DistanceMeters = $DistanceParam]"/>
    </xsl:element>
    </xsl:template> 

    <xsl:template match="doc:Trackpoint">
      <xsl:element name="{local-name()}">        
         <xsl:apply-templates select="*"/> 
         <AltitudeMeters><xsl:value-of select="$AltParam"/></AltitudeMeters>
         <Position>
            <LatitudeDegrees><xsl:value-of select="$LatParam"/></LatitudeDegrees>
            <LongitudeDegrees><xsl:value-of select="$LngParam"/></LongitudeDegrees>
         </Position> 
      </xsl:element>
    </xsl:template>

</xsl:stylesheet>

PHP

// LOAD XML
$xml = new DOMDocument('1.0', 'UTF-8');
$xml->load('Input.xml');

// LOAD XSLT 
$xsl = new DOMDocument('1.0', 'UTF-8');   
$xsl->load('XSLT_Script.xml');

// INITIALIZE NEW DOM TREE
$dom = new DOMDocument();
$dom->appendChild($dom->createElement('Data'));

// INITIALIZE TRANSFORMER
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);

// ITERATE THROUGH CSV ARRAY
foreach($csvdata as $row){

    // BIND LOOP VALUES TO XSLT PARAMETERS
    $proc->setParameter('', 'DistanceParam', $row[0]);
    $proc->setParameter('', 'AltParam', $row[1]);
    $proc->setParameter('', 'LatParam', $row[2]);
    $proc->setParameter('', 'LngParam', $row[3]);

    // TRANSFORM SOURCE
    $newXML = $proc->transformToDoc($xml);

    // IMPORT OUTPUT
    $dom->importNode($newXML->Track->Trackpoint, TRUE);
}

// SAVE NEW DOM TREE TO FILE
file_put_contents('Output.xml', $dom->saveXML());