Как обрабатывать несколько xpath одновременно (на основе структуры каналов) или создавать собственные каналы с одинаковой структурой - PullRequest
4 голосов
/ 09 июня 2011

код, приведенный ниже, протестирован и работает, он печатает содержимое канала с такой структурой.

<rss>
    <channel>
        <item>
            <pubDate/>
            <title/>
            <description/>
            <link/>
            <author/>
        </item>
    </channel>
</rss>

Что мне не удалось успешно сделать, так это распечатать каналы, которые следуют этой структуре ниже(разница на <feed><entry><published>), хотя я изменил xpath на /feed//entry.Вы можете увидеть структуру на странице источника.

<feed>
    <entry>
        <published/>
        <title/>
        <description/>
        <link/>
        <author/>
    </entry>
</feed>

Я должен сказать, что код сортирует все item по его pubDate.Во втором структурном фиде, я думаю, он должен отсортировать все entry по его published.

Я, вероятно, допустил ошибку в xPath, которую не смог найти.Однако, если в конце этого мне удастся правильно напечатать этот канал, как я могу изменить код для одновременной обработки различных структур?

Существует ли какая-либо служба, позволяющая мне создавать и размещать свои собственные каналына основе этих каналов, так что я буду иметь одинаковую структуру для всех?Надеюсь, я ясно дал понять ... Спасибо.

<?php

$feeds = array();

// Get all feed entries
$entries = array();
foreach ($feeds as $feed) {
    $xml = simplexml_load_file($feed);
    $entries = array_merge($entries, $xml->xpath(''));
}

?>

Ответы [ 3 ]

3 голосов
/ 19 июня 2011

Этот вопрос на самом деле состоит из двух вопросов: «Как обрабатывать несколько xpath одновременно» и «[Как] создавать собственные каналы с одинаковой структурой».

На второй блестяще ответил Дмитрий Новачев. Если вы хотите «объединить» или преобразовать один или несколько XML-документов, я определенно рекомендую это.

А пока я пойду по простому пути и отвечу на первый вопрос: «Как обрабатывать несколько xpath одновременно». Это просто, для этого есть оператор: |. Если вы хотите запросить все узлы, которые соответствуют /feed//entry или /rss//item, тогда вы можете использовать /feed//entry | /rss//item.

3 голосов
/ 19 июня 2011

Основным вкладом этого ответа является решение (в конце), которое можно использовать с бесконечным числом форматов , просто указав все «входные» альтернативные имена во внешнем (глобальном) параметре $postElements и все альтернативные имена «дата публикации» во внешнем (глобальном) параметре $pub-dateElements.

Кроме этого , здесь описано, как указать выражение XPath, которое выбирает все /rss//itemи все /feed//entry элементов.

В простом случае только двух возможных форматов документов это (как предложено @Josh Davis) выражение Xpath работает правильно:

/rss//item  |   /feed//entry

Более общее выражение XPath позволяет выбирать нужные элементы из набора неограниченного числа форматов документов :

/*[contains($topElements, concat('|',name(),'|'))]
    //*[contains($postElements, concat('|',name(),'|'))]

, где переменная $topElements должна быть заменена настрока с разделителями в виде трубы всех возможных имен для верхнего элемента, и $postElements должна быть заменена на строку с разделителями в виде трубы всех возможных имен для элемента "entry".Мы также допускаем, чтобы элементы "entry" находились на разной глубине в разных форматах документа.

В частности, для этого конкретного случая выражение XPath будет:

/*[contains('|feed|rss|', concat('|',name(),'|'))]
    //*[contains('|item|entry|', concat('|',name(),'|'))]

В оставшейся части этого поста показано, как полностью и полностью выполнить требуемую обработку можно в XSLT - легко и элегантно.


I.A осторожный введение

Такая обработка легко и просто с XSLT :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
  <myFeed>
   <xsl:apply-templates/>
  </myFeed>
 </xsl:template>

 <xsl:template match="channel|feed">
  <xsl:apply-templates select="*">
   <xsl:sort select="pubDate|published" order="descending"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="item|entry">
  <post>
    <xsl:apply-templates mode="identity"/>
  </post>
 </xsl:template>

 <xsl:template match="pubDate|published" mode="identity">
  <publicationDate>
   <xsl:apply-templates/>
  </publicationDate>
 </xsl:template>

  <xsl:template match="node()|@*" mode="identity">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*" mode="identity"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

, когда применяется это преобразованиек этому XML-документу (в формате 1):

<rss>
    <channel>
        <item>
            <pubDate>2011-06-05</pubDate>
            <title>Title1</title>
            <description>Description1</description>
            <link>Link1</link>
            <author>Author1</author>
        </item>
        <item>
            <pubDate>2011-06-06</pubDate>
            <title>Title2</title>
            <description>Description2</description>
            <link>Link2</link>
            <author>Author2</author>
        </item>
        <item>
            <pubDate>2011-06-07</pubDate>
            <title>Title3</title>
            <description>Description3</description>
            <link>Link3</link>
            <author>Author3</author>
        </item>
    </channel>
</rss>

и когда он применяется к этому эквивалентному документу (в формате 2):

<feed>
        <entry>
            <published>2011-06-05</published>
            <title>Title1</title>
            <description>Description1</description>
            <link>Link1</link>
            <author>Author1</author>
        </entry>
        <entry>
            <published>2011-06-06</published>
            <title>Title2</title>
            <description>Description2</description>
            <link>Link2</link>
            <author>Author2</author>
        </entry>
        <entry>
            <published>2011-06-07</published>
            <title>Title3</title>
            <description>Description3</description>
            <link>Link3</link>
            <author>Author3</author>
        </entry>
</feed>

в обоих случаях требуется один и тот же требуемый, правильный результат :

<myFeed>
   <post>
      <publicationDate>2011-06-07</publicationDate>
      <title>Title3</title>
      <description>Description3</description>
      <link>Link3</link>
      <author>Author3</author>
   </post>
   <post>
      <publicationDate>2011-06-06</publicationDate>
      <title>Title2</title>
      <description>Description2</description>
      <link>Link2</link>
      <author>Author2</author>
   </post>
   <post>
      <publicationDate>2011-06-05</publicationDate>
      <title>Title1</title>
      <description>Description1</description>
      <link>Link1</link>
      <author>Author1</author>
   </post>
</myFeed>

II.Полное решение

Это может быть обобщено до параметризованного решения :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="postElements" select=
 "'|entry|item|'"/>
 <xsl:param name="pub-dateElements" select=
  "'|published|pubDate|'"/>

  <xsl:template match="node()|@*" name="identity">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*" mode="identity"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/">
  <myFeed>
   <xsl:apply-templates select=
   "//*[contains($postElements, concat('|',name(),'|'))]">
    <xsl:sort order="descending" select=
     "*[contains($pub-dateElements, concat('|',name(),'|'))]"/>
   </xsl:apply-templates>
  </myFeed>
 </xsl:template>

 <xsl:template match="*">
  <xsl:choose>
   <xsl:when test=
    "contains($postElements, concat('|',name(),'|'))">
    <post>
      <xsl:apply-templates/>
    </post>
   </xsl:when>
   <xsl:when test=
   "contains($pub-dateElements, concat('|',name(),'|'))">
    <publicationDate>
     <xsl:apply-templates/>
    </publicationDate>
   </xsl:when>
   <xsl:otherwise>
    <xsl:call-template name="identity"/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

</xsl:stylesheet>

Это преобразование может использоваться с бесконечным числом форматов , просто указав все «входные» альтернативные имена во внешнем (глобальном) параметре $postElements и все «опубликованные» альтернативные имена во внешнем (глобальном) параметре $pub-dateElements.

Каждый может попробоватьэто преобразование для проверки того, что при применении к двум вышеуказанным XML-документам оно снова дает тот же, требуемый и правильный результат.

1 голос
/ 19 июня 2011

Вот решения.

Проблема заключается в том, что во многих каналах RSS или Atom определены пространства имен, которые плохо сочетаются с SimpleXML. В приведенном ниже примере я использую str_replace для замены xmlns= на ns=. Затем я использую имя корневого элемента, чтобы определить тип канала (будь то RSS или Atom).

Вызов array_push заботится о добавлении всех записей в массив $entries, который вы затем сможете использовать позже.

$entries = array();

foreach ( $feeds as $feed )
{
  $xml = simplexml_load_string(str_replace('xmlns=', 'ns=', $feed));

  switch ( strtolower($xml->getName()) )
  {
    // Atom
    case 'feed':
      array_push($entries, $xml->xpath('/feed//entry'));

      break;

    // RSS
    case 'rss':
      array_push($entries, $xml->xpath('/rss//item'));

      break;
  }

  // Unset the namespace variable.
  unset($namespaces);
}

var_dump($entries);

Другим решением может быть использование Google Reader для объединения всех ваших каналов и использования этих каналов вместо всех ваших отдельных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...