解析XML:根据IDREF/ID拉取单独的值
Parsing XML: Pulling a separate value based on IDREF/ID
我整天都在为这个问题苦苦挣扎,实际上它可能真的很简单......但我是 PHP 和 XML 世界的完全初学者所以真的可以做到在一些帮助下。
我正在使用 SimpleXML 来解析我的数据并有两个 second-level 组 - (yearlist) 和 (eplist)。我将 (year) 嵌套在 (yearlist) 中,它有一个属性 "yid",在我的 DTD 中设置为 ID。它还将 (yearname) 嵌套在 (year) 中,其中包含要显示为输出的更详细的描述。我将 (ep) 嵌套在 (eplist) 中,属性 "yearid"(与 "yid" 直接相关),在我的 DTD 中设置为 IDREF。
基本上,当我为 (eplist) 解析数据时,我想使用 (yearname) 作为一个组 header - 使用 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 并在线阅读各种 syntax/tutorials,但似乎无法理解它。这是我目前所拥有的...但我已经注释掉了 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>";
}
}
?>
希望您能指导我正确的方向!
谢谢
山姆
感谢 michi 的帮助 - 这绝对是朝着正确方向迈出的一步!
我正在想办法只显示一次季节名称...遇到了迭代和数组,但它们对我来说都太复杂了。是否可以在 foreach 命令中包含 xpath?我想也许如果我在 foreach 季节内嵌套 foreach 剧集并使用 xpath 来匹配它可以工作的 ID,但我似乎无法让它显示元素。我在正确的轨道上吗?
<?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;
}
}
?>
再次感谢!
你掌握了基本的SimpleXml
技巧,干得好。现在让我们开始吧:
我建议迭代 <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
跨平台生成新行,见When do I use the PHP constant "PHP_EOL"?
查看实际效果:https://eval.in/464970
这看起来确实与您想要的相似,不是吗?
使用<ep>
yearid
属性作为key访问并回显对应的<yearname>
,使用xpath()
[=43] =]
您的xpath
表达基本正确,但需要一些修改:
// old:
echo $xml->xpath('//year[@yid="$episode[yearid]"]/yearname');
// new:
echo $xml->xpath("//year[@yid='$episode[yearid]']/yearname")[0];
交换 "
和 '
所以 $episode
将被评估。请注意,我在代码中将其名称从 $episodes
更改为 $episode
。
参见 What is the difference between single-quoted and double-quoted strings in PHP?
xpath()
returns array
个 SimpleXml
元素,要访问第 1st 个值,我们需要取消引用[0]
.
数组
当然,这段代码不是防错的,它不会检查数组是否为空等。您需要在生产中添加它,但这会使这些示例中的重点复杂化。
将 echo $episode['yearid'] (...)
替换为正确的 xpath
。
看到它工作:https://eval.in/464992
接下来:将具有相同 SEASON = echo SEASON 的剧集分组,仅适用于属于该季的第 1st 集。 (你的工作)
更新:
您发布了近乎完美的代码,请参阅我的评论。
基本上,您有两个通过 yearid 链接的表。 1集关联1年,1年关联多集。您可以通过迭代年份和 select 链接的剧集(= 您的最后一个代码示例)或迭代剧集和 select 链接的年份(= 我的代码示例)来解决它。
这里有一种在前面的例子的基础上进行分组的方法:
$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 中。如果你在 y3 之后有一个 <ep>
和 y1...
您可以使用 XSLT 将 XML 重组为您需要的格式。作为信息,XSLT 是一种特殊用途的声明性编程语言,用于为各种最终用途重组、重新样式化、重新格式化 XML 文档。几乎所有通用语言都维护 XSLT 处理器:Java、C#、Python、Perl、VB,甚至 PHP.
XSLT Script (单独保存为.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>
</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>
我整天都在为这个问题苦苦挣扎,实际上它可能真的很简单......但我是 PHP 和 XML 世界的完全初学者所以真的可以做到在一些帮助下。
我正在使用 SimpleXML 来解析我的数据并有两个 second-level 组 - (yearlist) 和 (eplist)。我将 (year) 嵌套在 (yearlist) 中,它有一个属性 "yid",在我的 DTD 中设置为 ID。它还将 (yearname) 嵌套在 (year) 中,其中包含要显示为输出的更详细的描述。我将 (ep) 嵌套在 (eplist) 中,属性 "yearid"(与 "yid" 直接相关),在我的 DTD 中设置为 IDREF。
基本上,当我为 (eplist) 解析数据时,我想使用 (yearname) 作为一个组 header - 使用 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 并在线阅读各种 syntax/tutorials,但似乎无法理解它。这是我目前所拥有的...但我已经注释掉了 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>";
}
}
?>
希望您能指导我正确的方向!
谢谢 山姆
感谢 michi 的帮助 - 这绝对是朝着正确方向迈出的一步!
我正在想办法只显示一次季节名称...遇到了迭代和数组,但它们对我来说都太复杂了。是否可以在 foreach 命令中包含 xpath?我想也许如果我在 foreach 季节内嵌套 foreach 剧集并使用 xpath 来匹配它可以工作的 ID,但我似乎无法让它显示元素。我在正确的轨道上吗?
<?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;
}
}
?>
再次感谢!
你掌握了基本的SimpleXml
技巧,干得好。现在让我们开始吧:
我建议迭代
<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
跨平台生成新行,见When do I use the PHP constant "PHP_EOL"?查看实际效果:https://eval.in/464970
这看起来确实与您想要的相似,不是吗?
使用
<ep>
yearid
属性作为key访问并回显对应的<yearname>
,使用xpath()
[=43] =]您的
xpath
表达基本正确,但需要一些修改:// old: echo $xml->xpath('//year[@yid="$episode[yearid]"]/yearname'); // new: echo $xml->xpath("//year[@yid='$episode[yearid]']/yearname")[0];
交换
"
和'
所以$episode
将被评估。请注意,我在代码中将其名称从$episodes
更改为$episode
。
参见 What is the difference between single-quoted and double-quoted strings in PHP?
数组xpath()
returnsarray
个SimpleXml
元素,要访问第 1st 个值,我们需要取消引用[0]
.当然,这段代码不是防错的,它不会检查数组是否为空等。您需要在生产中添加它,但这会使这些示例中的重点复杂化。
将
echo $episode['yearid'] (...)
替换为正确的xpath
。看到它工作:https://eval.in/464992
接下来:将具有相同 SEASON = echo SEASON 的剧集分组,仅适用于属于该季的第 1st 集。 (你的工作)
更新:
您发布了近乎完美的代码,请参阅我的评论。
基本上,您有两个通过 yearid 链接的表。 1集关联1年,1年关联多集。您可以通过迭代年份和 select 链接的剧集(= 您的最后一个代码示例)或迭代剧集和 select 链接的年份(= 我的代码示例)来解决它。
这里有一种在前面的例子的基础上进行分组的方法:
$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 中。如果你在 y3 之后有一个<ep>
和 y1...
您可以使用 XSLT 将 XML 重组为您需要的格式。作为信息,XSLT 是一种特殊用途的声明性编程语言,用于为各种最终用途重组、重新样式化、重新格式化 XML 文档。几乎所有通用语言都维护 XSLT 处理器:Java、C#、Python、Perl、VB,甚至 PHP.
XSLT Script (单独保存为.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>
</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>