使用PHP对XML数据进行排序/分组?

时间:2010-03-29 20:25:20

标签: php xml sorting grouping simplexml

我正在尝试使用discogs.com(XML)-API中的数据制作页面。我一直用simpleXML解析它并且它工作得很好但是有些东西我不知道该怎么做。

以下是XML的一部分:

<releases>
  <release id="1468764" status="Accepted" type="Main">
    <title>Versions</title>
    <format>12", EP</format>
    <label>Not On Label</label>
    <year>1999</year>
  </release>
  <release id="72246" status="Accepted" type="Main">
    <title>The M.O.F Blend</title>
    <format>LP</format>
    <label>Blenda Records</label>
    <year>2002</year>
  </release>
  <release id="890064" status="Accepted" type="Main">
    <title>The M.O.F Blend</title>
    <format>CD</format>
    <label>Blenda Records</label>
    <year>2002</year>
  </release>
  <release id="1563561" status="Accepted" type="TrackAppearance">
    <title>Ännu En Gång Vol. 3</title>
    <trackinfo>Backtrack</trackinfo>
    <format>Cass, Comp, Mix</format>
    <label>Hemmalaget</label>
    <year>2001</year>
  </release>
</releases>

我想要实现的是类似于discogs如何呈现版本的内容:http://www.discogs.com/artist/Mics+Of+Fury其中同一版本的不同版本被排序在一起。 (参见我的链接中的M.O.F Blend)这是在具有主要版本的discogs上完成的,其中包含其他版本的主要版本。遗憾的是,这些信息不存在于API数据中,所以我想通过将<release> - 节点与相同的<title> - 标签分组来做同样的事情,或者为{{1}添加一个标志没有唯一的<releases>?有关最佳方法的任何好主意吗?

我还想知道是否可以计算具有相同类型属性的<title> - 节点(发布的子节点)?比如在这个例子中计算版本为“Main”的版本?

使用XMLReader或XPath做这件事可能更好吗?

3 个答案:

答案 0 :(得分:2)

您可以使用xsl(t)php's XSLTProcessor 在xslt 2.0中,您可以使用类似

的内容
<xsl:for-each-group select="release" group-by="title">

不幸的是libxslt不支持这个(至少在php 5.3.2的php.net win32版本中使用的版本不支持)。
但您可以使用Muenchian grouping method

test.xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="4.0" encoding="utf-8" indent="yes"/>

<xsl:key name="release-by-title" match="release" use="title" />

<xsl:template match="/">
  <html>
    <head><title>...</title></head>
    <body>
      <xsl:apply-templates />
    </body>
  </html>
</xsl:template>

<xsl:template match="releases">
  <table border="1">
    <xsl:for-each select="release[count(. | key('release-by-title', title)[1]) = 1]">
      <xsl:sort select="title" />
      <tr>
        <th colspan="3"><xsl:value-of select="title" /></th>
      </tr>
      <xsl:for-each select="key('release-by-title', title)">
        <xsl:sort select="year" />
        <tr>
          <td><xsl:value-of select="year" /></td>
          <td><xsl:value-of select="label" /></td>
          <td><xsl:value-of select="format" /></td>
        </tr>
      </xsl:for-each>
    </xsl:for-each>
  </table>
</xsl:template>

</xsl:stylesheet>

包含您提供的xml文档的test.xml 和test.php:

<?php
$doc = new DOMDocument;
$doc->load('test.xsl');
$stylesheet = new XSLTProcessor;
$stylesheet->importStyleSheet($doc);
$doc->load('test.xml');

header('Content-type: text/html; charset=utf-8');
echo $stylesheet->transformToXML($doc);

输出(只有&lt; table&gt; -part)是:

<table border="1">
  <tr><th colspan="3">The M.O.F Blend</th></tr>
  <tr>
    <td>2002</td>
    <td>Blenda Records</td>
    <td>LP</td>
  </tr>
  <tr>
    <td>2002</td>
    <td>Blenda Records</td>
    <td>CD</td>
  </tr>
  <tr><th colspan="3">Versions</th></tr>
  <tr>
    <td>1999</td>
    <td>Not On Label</td>
    <td>12", EP</td>
  </tr>
  <tr><th colspan="3">Ännu En Gång Vol. 3</th></tr>
  <tr>
    <td>2001</td>
    <td>Hemmalaget</td>
    <td>Cass, Comp, Mix</td>
  </tr>
</table>

现在,这不是一个解释。但也许它会为你提供一些搜索内容的提示。

答案 1 :(得分:0)

我最近构建了一个用于创建和排序对象HTML表的类,这里有一些静态方法,你可能会发现它对排序关联数组很有用。我删除了self :: reference,因此您可以将这些方法用作函数。

用法:$array = array_sort($array, 'sort_key_name');

function array_sort(&$array)
    {
        if(!$array) return $keys;
        $keys = func_get_args();
        array_shift($keys);
        array_sort_func($keys);
        usort($array,array("listview","array_sort_func"));
        return $array;
    }


function array_sort_func($a, $b = NULL)
    {
        static $keys;
        if($b === NULL) return $keys = $a;

        foreach($keys as $k)
        {
            $aval = hod($a, '$a->' . $k);
            $bval = hod($b, '$b->' . $k);

            // modify string to compate
            if(!is_numeric($aval)){$aval = strtolower($aval);}
            if(!is_numeric($bval)){$bval = strtolower($bval);}

            if($k[0]=='!')
            {
                $k=substr($k,1);

                if($aval!== $bval)
                {
                    if(is_numeric($aval) and is_numeric($bval))
                    {
                        return $aval - $bval;
                    }
                    else
                    {
                        return strcmp($bval, $aval);
                    }
                }
            }
            else if($aval !== $bval)
            {
                if(is_numeric($aval) and is_numeric($bval))
                {
                    $compare = $aval - $bval;

                    if($compare > 0)
                    {
                        return 1;
                    }
                    elseif($comare < 0)
                    {
                        return -1;
                    }
                }
                else
                {
                    return strcmp($aval, $bval);
                }
            }
        }

        return 0;
    }

function hod(&$base, $path)
    {
        $licz = '';
        $keys = explode("->", $path);
        $keys[0] = str_replace('$', '', $keys[0]);
        $expression = '$ret = ';
        $expression.= '$';

        foreach ($keys as $key)
        {
            if (++$licz == 1)
            {
                $expression.= 'base->';
            }
            else
            {
                $expression.= $key.'->';
            }
        }

        $expression = substr($expression, 0, -2);
        $expression.= ';';
        eval($expression);
        return $ret;
    }

答案 2 :(得分:0)

我想xslt 2.0可能对您有所帮助,这里是使用xsl 2.0对XML数据进行分组和排序的文档: