Синтаксический анализ XML: извлечение отдельного значения на основе IDREF / ID

Я боролся за это весь день, и на самом деле это, вероятно, очень просто … но я — полный новичок в мире PHP и XML, поэтому мог бы действительно с некоторой помощью.

Я использую SimpleXML для анализа моих данных и имею две группы второго уровня — (yearlist) и (eplist). Я вложил (год) внутрь (годовой список), у которого есть атрибут «yid», установленный как идентификатор в моем DTD. Он также имеет (год) внутри (год), который содержит более подробное описание, которое будет отображаться в качестве выходных данных. Я вложил (ep) внутрь (eplist) с атрибутом yearid (который напрямую связан с yid), установленным как IDREF в моем DTD.

В основном, когда я анализирую данные для (eplist), я хочу использовать (yearname) в качестве заголовка группы — используя yearid = yid> yearname в качестве пути.

Я создал пример моих данных, которые могут помочь лучше объяснить мою проблему.

Вот мой DTD:

<?xml encoding="UTF-8"?>

<!ELEMENT besteplist (yearlist,eplist)>

<!ELEMENT yearlist (year)+>
<!ELEMENT year (yearname)>
<!ATTLIST year
yid ID #REQUIRED>
<!ELEMENT yearname (#PCDATA)>

<!ELEMENT eplist (ep)+>
<!ELEMENT ep (eptitle,eptnumber)>
<!ATTLIST ep
eid ID #REQUIRED
yearid IDREF #IMPLIED>
<!ELEMENT eptitle (#PCDATA)>
<!ELEMENT eptnumber (#PCDATA)>

Вот мой XML:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE besteplist SYSTEM "example.dtd">
<besteplist>
<yearlist>
<year yid="y1">
<yearname>1995, Season 1</yearname>
</year>
<year yid="y2">
<yearname>1996, Season 2</yearname>
</year>
<year yid="y3">
<yearname>1997, Season 3</yearname>
</year>
</yearlist>
<eplist>
<ep yearid="y1" eid="e1">
<eptitle>The First Episode</eptitle>
<eptnumber>1</eptnumber>
</ep>
<ep yearid="y2" eid="e2">
<eptitle>Bla bla bla</eptitle>
<eptnumber>21</eptnumber>
</ep>
<ep yearid="y2" eid="e3">
<eptitle>Rar rar rar</eptitle>
<eptnumber>39</eptnumber>
</ep>
<ep yearid="y2" eid="e4">
<eptitle>Tra la la</eptitle>
<eptnumber>45</eptnumber>
</ep>
<ep yearid="y3" eid="e5">
<eptitle>Donkey</eptitle>
<eptnumber>126</eptnumber>
</ep>
</eplist>
</besteplist>

Вот пример того, как я хотел бы, чтобы вывод выглядел:

SEASON: 1995, Season 1

EPISODE TITLE: The First Episode
EPISODE NUMBER: 1

SEASON: 1996, Season 2

EPISODE TITLE: Bla bla bla
EPISODE NUMBER: 21

EPISODE TITLE: Rar rar rar
EPISODE NUMBER: 39

EPISODE TITLE: Tra la la
EPISODE NUMBER: 45

SEASON: 1997, Season 3

EPISODE TITLE: Donkey
EPISODE NUMBER: 126

Я не думаю, что будет много пользы опубликовать код, который я уже пытался, поскольку он, вероятно, довольно бесполезен … что я иметь удалось сделать это самые основы. Как только у меня получится, я могу перейти к следующему этапу … форматированию …

Я никоим образом не привязан к SimpleXML, поэтому, если кто-то может предложить более эффективный способ ведения дел, я весь в ушах.

Большое спасибо заранее всем, кто нашел время, чтобы помочь мне. 🙂

Сэм


В ответ на @michi я сидел, пытаясь отработать xpath и читая всевозможные синтаксические / учебные пособия в Интернете, и, похоже, не могу прийти в себя. Это то, что я имею до сих пор … но я закомментировал xpath, поскольку это явно неправильно.

<?php
$xml=simplexml_load_file("example.xml") or die("Error: Cannot create object");

foreach($xml->yearlist->children() as $years) {
$xyid=$years[yid];
echo "_____________________________________________<br>";
echo "(yid= " . $xyid . " )<br>";
echo "SEASON: " . $years->yearname . "<br>";
echo "_____________________________________________<br>";
foreach($xml->eplist->children() as $episodes) {
echo "EPISODE TITLE: " . $episodes->eptitle . "<br>";
echo "EPISODE NUMBER: " . $episodes->eptnumber . "<br>";
$xyearid=$episodes[yearid];
echo "(yearid= " . $xyearid . " )<br>";
// echo $xml->xpath('//year[@yid="$episodes[yearid]"]/yearname');
echo "</p>";
}
}

?>

Я надеюсь, что вы можете направить меня в правильном направлении!

Спасибо
Сэм


Спасибо за помощь, Мичи — это определенно шаг в правильном направлении!

Я пытаюсь придумать способы отображения названия сезона только один раз … натолкнулся на итерации и массивы, но все они выглядят слишком сложными для меня. Можно ли включить xpath в команду foreach? Я подумал, может быть, если бы я вложил эпизоды foreach в течение сезонов foreach и использовал xpath для сопоставления идентификатора, который мог бы работать, но я не могу заставить его показать элементы. Я на правильном пути?

<?php
$xml=simplexml_load_file("example.xml") or die("Error: Cannot create object");

foreach ($xml->yearlist->year as $season) {
echo "SEASON: " . $season->yearname . PHP_EOL;
foreach ($xml->xpath("//ep[@yearid='$season[yid]']")[0] as $episode) {
echo "EPISODE TITLE: " . $episode->eptitle . PHP_EOL;
echo "EPISODE NUMBER: " . $episode->eptnumber . PHP_EOL;
echo PHP_EOL;
}
}

?>

Еще раз спасибо!

2

Решение

Вы освоили основные SimpleXml техника, хорошая работа. Теперь давайте поработаем над этим:

  1. Предлагаю перебрать <eplist> и повторить все <ep> только:

    $xml = simplexml_load_string($x); // assume XML in $x
    
    foreach ($xml->eplist->ep as $episode) {
    echo $episode['yearid'] . PHP_EOL;
    echo "EPISODE TITLE: " . $episode->eptitle . PHP_EOL;
    echo "EPISODE NUMBER: " . $episode->eptnumber . PHP_EOL;
    echo PHP_EOL;
    }
    

    PHP_EOL генерирует новую линию на разных платформах, см. Когда я использую константу PHP "PHP_EOL"?

    увидеть это в действии: https://eval.in/464970

    Это похоже на то, что вы хотите, не так ли?

  2. Использовать <ep> yearid атрибут в качестве ключа для доступа и отображения соответствующего <yearname>использовать xpath() для этого.

    Ваш xpathВыражение в основном правильно, но требует некоторых изменений:

    // old:
    echo $xml->xpath('//year[@yid="$episode[yearid]"]/yearname');
    
    // new:
    echo $xml->xpath("//year[@yid='$episode[yearid]']/yearname")[0];
    

    Своп " а также ' так $episode будет оцениваться. Обратите внимание, что я изменил название $episodes в $episode в моем коде.
    Увидеть В чем разница между одинарными и двойными кавычками в PHP?

    xpath() возвращает array из SimpleXml элементы, чтобы получить доступ к 1улица значение нам нужно разыменовать массив с [0],

    Конечно, этот код не защищен от ошибок, он не проверяет, является ли массив пустым и т. Д. Вы должны добавить это для производства, но это усложнит ситуацию в этих примерах.

    замещать echo $episode['yearid'] (...) с правильным xpath,

    увидеть это работает: https://eval.in/464992

  3. далее: группировка эпизодов с тем же СЕЗОНОМ = эхо СЕЗОНА только для 1улица эпизод, принадлежащий тому сезону. (твоя работа)

    Обновить:

    Вы разместили почти идеальный код, смотрите мой комментарий.

    По сути, у вас есть две таблицы, связанные по yearid. 1 эпизод связан с 1 годом, а 1 год связан со многими эпизодами. Вы можете сделать это либо с помощью итерации по годам и выбрать связанные эпизоды (= ваш последний пример кода), либо перебрать эпизоды и выбрать связанный год (= мои примеры кода).

    Вот способ группового построения на предыдущих примерах:

    $xml = simplexml_load_string($x); // assume XML in $x
    $yid = "";
    
    foreach ($xml->eplist->ep as $episode) {
    
    // check if last yearid is different from current yearid
    // only if yes, echo the yearname
    if ($yid != (string)$episode['yearid']) {
    echo "SEASON: " . $xml->xpath("//year[@yid='$episode[yearid]']/yearname")[0] . PHP_EOL . PHP_EOL;
    }
    echo "  EPISODE TITLE: " . $episode->eptitle . PHP_EOL;
    echo "  EPISODE NUMBER: " . $episode->eptnumber . PHP_EOL . PHP_EOL;
    
    // store current yearid in $yid for next iteration
    $yid = (string)$episode['yearid'];
    }
    

    Замечания: (string) заботится о том, что оценка является строкой, а не SimpleXml объект.

    Выход:

    SEASON: 1995, Season 1
    
    EPISODE TITLE: The First Episode
    EPISODE NUMBER: 1
    
    SEASON: 1996, Season 2
    
    EPISODE TITLE: Bla bla bla
    EPISODE NUMBER: 21
    
    EPISODE TITLE: Rar rar rar
    EPISODE NUMBER: 39
    
    EPISODE TITLE: Tra la la
    EPISODE NUMBER: 45
    
    SEASON: 1997, Season 3
    
    EPISODE TITLE: Donkey
    EPISODE NUMBER: 126
    

    увидеть это работает: https://eval.in/465044

    Дальнейшее обсуждение: кодекс считает само собой разумеющимся, что <ep> узлы уже сгруппированы в вашем XML. Если бы у вас был <ep> с у1 после у3 …

0

Другие решения

Вы можете использовать XSLT для реструктуризации вашего XML в нужный вам формат. Как информация, XSLT это специальный язык декларативного программирования, используемый для реструктуризации, изменения стиля, переформатирования документов XML для различных целей конечного использования. Практически все языки общего назначения поддерживают процессоры XSLT: Java, C #, Python, Perl, VB, даже PHP.

Скрипт XSLT (сохранить отдельно как файл .xsl для использования ниже)

<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="besteplist">
<besteplist>

<xsl:for-each select="yearlist/year">
<xsl:variable name="yearvar" select="@yid"/>
SEASON: <xsl:value-of select="yearname"/>
<xsl:for-each select="../../eplist/ep[@yearid=$yearvar]">
EPISODE TITLE: <xsl:value-of select="eptitle"/>
EPISODE NUMEBR: <xsl:value-of select="eptnumber"/>
<xsl:text>&#xa;</xsl:text>
</xsl:for-each>
</xsl:for-each>

</besteplist>
</xsl:template>

</xsl:stylesheet>

PHP скрипт

<?php

// Set current directory
$cd = dirname(__FILE__);

// Load the XML source and XSLT file
$xml = new DOMDocument('1.0', 'UTF-8');
$xml->formatOutput = true;
$xml->preserveWhiteSpace = false;
$xml->load($cd.'/SeasonEpisodes.xml');

$xsl = new DOMDocument;
$xsl->load($cd.'/SeasonEpisodes.xsl');

// Configure transformer
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);

// Transform XML source
$newXML = new DOMDocument;
$newXML = $proc->transformToXML($xml);

// Save output to file
$xmlfile = $cd.'/NewSeasonEpisodes.xml';
file_put_contents($xmlfile, $newXML);

?>

Новый вывод XML (теперь просто анализируем данные корневого узла)

<?xml version="1.0"?>
<besteplist>
SEASON: 1995, Season 1
EPISODE TITLE: The First Episode
EPISODE NUMEBR: 1

SEASON: 1996, Season 2
EPISODE TITLE: Bla bla bla
EPISODE NUMEBR: 21

EPISODE TITLE: Rar rar rar
EPISODE NUMEBR: 39

EPISODE TITLE: Tra la la
EPISODE NUMEBR: 45

SEASON: 1997, Season 3
EPISODE TITLE: Donkey
EPISODE NUMEBR: 126
</besteplist>
1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector