Я ищу несколько советов о том, как лучше всего изменить 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. Для каждой точки отслеживания я ищу в массиве расстояние и затем добавляю соответствующие данные о положении в тег точки отслеживания.
Файлы, с которыми я имею дело, могут иметь размер от 5 до 20 МБ, поэтому я не уверен, что это лучший способ сделать это.
Я понимаю, что анализатор DOM будет самым простым, но при этом самым интенсивным и медленным.
Я размышлял об использовании XMLREADER для анализа файла, а затем с помощью XMLWRITER записал все элементы trackpoint + данные о местоположении в другой файл.
У меня нет доступа к серверу, поэтому я могу тестировать только на локальной машине.
Любые советы высоко ценится.
Джеймс.
Update1
У меня есть многомерный массив, загруженный из CSV-файла, содержащего данные о расстоянии, широте, долготе и высоте. Я читаю расстояние от тега trackpoint и использую его как ключ для массива (key = distance), а затем возвращаю связанные данные lat и lon. Затем я создаю элементы позиции, широты и долготы + текстовые узлы и добавляю к тегу trackpoint.
Update2
Использовал предложение Парфе — используйте 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>
Рассматривать XSLT, язык специального назначения, предназначенный для преобразования файлов XML и файлов указанных размеров, должен быть быстрым рендерингом. PHP может запустить XSLT 1.0 с его PHP-XSL учебный класс.
А поскольку вы выполняете итерацию по файлам 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());
Других решений пока нет …