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

发布于 2024-08-26 20:33:12 字数 1830 浏览 8 评论 0原文

我正在尝试使用来自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,其中同一版本的不同版本被排序在一起。 (请参阅我的链接中的 MOF Blend)这是在 discogs 上完成的,并拥有包含其他版本的主版本。不幸的是,此信息不存在于 API 数据中,因此我想通过将具有相同 </code> 的 <code><release></code>-节点分组来执行相同的操作-tags,或者向没有唯一 <code><title></code> 的 <code><releases></code> 添加标志?关于最好的方法有什么好的想法吗?

我还想知道是否可以计算具有相同类型属性的 节点(版本的子节点)?就像在这个例子中计算类型为“Main”的版本?

也许用 XMLReader 或 XPath 来做这件事更好?

I'm trying to make a page using data from the discogs.com (XML)-API. i've been parsing it with simpleXML and it's working pretty well but there is some things i'm not sure how to do.

Here is part of the 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>

What i want to achieve is something similair to how discogs presents the releases: http://www.discogs.com/artist/Mics+Of+Fury where diferent versions of the same release are sorted together. (see. The M.O.F Blend in my link) This is done on discogs with having a master release that features the other releases. unfortunately this information isn't present in the API data, so i want to do the same thing by grouping the <release>-nodes with the same <title>-tags, or add a flag to the <releases> that don't have a unique <title>? any good ideas on the best way of doing this?

i also like to know if it's possible to count the <release>-nodes (child of releases) that have the same type-attribute? like in this example count the releases with the type "Main"?

maybe it's better to do this things with XMLReader or XPath?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

你不是我要的菜∠ 2024-09-02 20:33:12

您可以使用 xsl(t)php 的 XSLTProcessor
在 xslt 2.0 中,您可以使用类似的内容

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

不幸的是 libxslt 不支持此功能(至少在php.net win32 版本的 php 5.3.2 没有)。
但您可以使用慕尼黑分组方法

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

输出(仅 部分)是:

<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>

现在,这不是太多解释。但也许它会给您一些搜索内容的提示。

You can use xsl(t) and php's XSLTProcessor.
In xslt 2.0 you could use something like

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

Unfortunately libxslt doesn't support this (at least the version used in the php.net win32 build of php 5.3.2 doesn't).
But you can use the 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>

test.xml containing the xml document you provided.
And 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);

And the output (onlye the <table>-part) is:

<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>

Now, that's not much of an explaination. But maybe it gives you some hints to what to search for.

伴我老 2024-09-02 20:33:12

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

用法:$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;
    }

I have recently built a class I use for creating and sorting HTML tables of objects, here are a some static methods from it you may find useful for sorting associative arrays. I have removed the self:: reference so you can just use the methods as functions.

Usage: $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;
    }
夏夜暖风 2024-09-02 20:33:12

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

I guess that xslt 2.0 may be able help you, here's the documentation for applying grouping and sorting on XML data using xsl 2.0:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文