使用XSLT和PHP修改大型XML文件并返回给用户

时间:2018-01-29 16:41:01

标签: php xml gpx

我正在寻找一些关于修改上传到服务器的xml文件(Garmin TCX文件)的最佳方法的建议,然后将修改后的版本返回给用户,而不需要浏览器超时。我需要解析上传的文件并添加一些额外的元素。例如,我需要每个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元素+位置数据写入另一个文件。 我无法访问服务器,因此只能在本地计算机上进行测试。 任何意见是极大的赞赏。 詹姆斯。

UPDATE1 我有一个从包含距离,纬度,经度和高程数据的csv文件加载的多维数组。我读取了跟踪点标记的距离,并将其用作数组的键(键=距离),然后返回关联的lat和lon数据。然后我创建位置,纬度和经度元素+文本节点并附加到trackpoint标记。

UPDATE2 使用Parfait的建议 - 使用xsl添加额外的元素。进入默认命名空间的问题。我无法选择任何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>

1 个答案:

答案 0 :(得分:0)

考虑XSLT,这是专门用于转换XML文件的特殊用途语言,应该是快速渲染的指定文件大小。 PHP可以使用php-xsl类运行XSLT 1.0。

由于您正在迭代CSV文件到条件集XSLT值,因此请考虑将PHP中的值作为参数传递到匹配距离的指定占位符。

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