2012/05/07

2009/08/16 追記

furyu-teiさんの記事でXSLTの場合はendpointを変えないといけない事が分かったので修正。動くようになった。



2009/07/16 追記

AWS認証制限に対応しましたがXSLTの処理において正しく動きません。手元だとXSLT変換出来ていますがAmazonサーバを通すと空の処理結果が戻ってきてしまいます。とりあえずは正しく動くはずだろうコードで置いて置きますが、動作しない事をご了承下さい。

またXSLTも使わないバージョンもありますのでそちらもご覧下さい。



気付いたらAmazon Ecommerce Web Serviceのバージョン3.0が終わっていました
それにともないmalaさんの「Amazon最速検索β」も動かなくなってしまっていました。
残念だったので気分だけでも...と思い作ってみました。
今回のポイントとしてはJSONではなくJSONPであること。malaさんのバージョンでは"end"というマーカーを使い、かつcallbackは指定出来ませんでしたが、以下のサンプルではcallbackパラメータを受け取りJSONPします。
AWS4では、与えられたパラメータがItemSearchResponse/OperationRequest/Arguments/Argumentに格納されるので、この属性Nameが"callback"の物を関数名となる様にしてあります。
まずXSLT <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:aws="http://webservices.amazon.com/AWSECommerceService/2005-10-05"
  exclude-result-prefixes="aws">

  <xsl:output method="text" media-type="text/javascript" encoding="UTF-8"/>
  <xsl:variable name="lcletters">abcdefghijklmnopqrstuvwxyz</xsl:variable>
  <xsl:variable name="ucletters">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>

  <xsl:template match="/aws:ItemSearchResponse">
    <xsl:value-of select="aws:OperationRequest/aws:Arguments/aws:Argument[@Name='callback']/@Value"/>
    <xsl:text>({isvalid:</xsl:text>
    <xsl:value-of select="translate(aws:Items/aws:Request/aws:IsValid/text(),$ucletters,$lcletters)" />
    <xsl:text>,errors:[</xsl:text>
    <xsl:for-each select="aws:Items/aws:Request/aws:Errors/aws:Error">
      <xsl:text>{code:'</xsl:text>
      <xsl:value-of select="aws:Code" />
      <xsl:text>',message:'</xsl:text>
      <xsl:call-template name="sanitize-text">
        <xsl:with-param name="target" select="aws:Message"/>
      </xsl:call-template>
      <xsl:text>'}</xsl:text>
      <xsl:choose>
        <xsl:when test='position() &lt; last()'>
          <xsl:text>,</xsl:text>
        </xsl:when>
      </xsl:choose>
    </xsl:for-each>
    <xsl:text>],result:</xsl:text>
    <xsl:apply-templates select="aws:Items"/>
    <xsl:text>})</xsl:text>
  </xsl:template>

  <xsl:template match="aws:Items">
    <xsl:text>[</xsl:text>
    <xsl:for-each select="aws:Item">
      <xsl:apply-templates select="."/>
      <xsl:choose>
        <xsl:when test='position() &lt; last()'>
          <xsl:text>,</xsl:text>
        </xsl:when>
      </xsl:choose>
    </xsl:for-each>
    <xsl:text>]</xsl:text>
  </xsl:template>

  <xsl:template match="aws:Item">
    <xsl:text>{</xsl:text>

    <xsl:text>asin:'</xsl:text>
    <xsl:value-of select="aws:ASIN"/>
    <xsl:text>'</xsl:text>

    <xsl:text>,url:'</xsl:text>
    <xsl:value-of select="aws:DetailPageURL"/>
    <xsl:text>'</xsl:text>

    <xsl:text>,title:'</xsl:text>
    <xsl:call-template name="sanitize-text">
      <xsl:with-param name="target" select="normalize-space(aws:ItemAttributes/aws:Title)"/>
    </xsl:call-template>
    <xsl:text>'</xsl:text>

    <xsl:text>,publisher:'</xsl:text>
    <xsl:call-template name="sanitize-text">
      <xsl:with-param name="target" select="normalize-space(aws:ItemAttributes/aws:Publisher)"/>
    </xsl:call-template>
    <xsl:text>'</xsl:text>

    <xsl:text>,date:'</xsl:text>
    <xsl:call-template name="sanitize-text">
      <xsl:with-param name="target" select="aws:ItemAttributes/aws:PublicationDate"/>
    </xsl:call-template>
    <xsl:text>'</xsl:text>

    <xsl:text>,authors:[</xsl:text>
    <xsl:for-each select="aws:ItemAttributes/aws:Author">
      <xsl:text>'</xsl:text>
      <xsl:call-template name="sanitize-text">
        <xsl:with-param name="target" select="text()"/>
      </xsl:call-template>
      <xsl:text>'</xsl:text>
      <xsl:choose>
        <xsl:when test='position() &lt; last()'>
          <xsl:text>,</xsl:text>
        </xsl:when>
      </xsl:choose>
    </xsl:for-each>
    <xsl:text>]</xsl:text>

    <xsl:text>,label:'</xsl:text>
    <xsl:call-template name="sanitize-text">
      <xsl:with-param name="target" select="aws:ItemAttributes/aws:Label"/>
    </xsl:call-template>
    <xsl:text>'</xsl:text>

    <xsl:text>,price:'</xsl:text>
    <xsl:call-template name="sanitize-text">
      <xsl:with-param name="target" select="aws:ItemAttributes/aws:ListPrice/aws:FormattedPrice"/>
    </xsl:call-template>
    <xsl:text>'</xsl:text>

    <xsl:text>,availability:'</xsl:text>
    <xsl:call-template name="sanitize-text">
      <xsl:with-param name="target" select="aws:Offers//aws:Availability"/>
    </xsl:call-template>
    <xsl:text>'</xsl:text>

    <xsl:text>,image:{</xsl:text>
    <xsl:for-each select="aws:*[local-name()='SmallImage' or local-name()='MediumImage' or local-name()='LargeImage']">
      <xsl:call-template name="image">
        <xsl:with-param name="type" select="substring-before(local-name(), 'Image')"/>
      </xsl:call-template>
      <xsl:choose>
        <xsl:when test='position() &lt; last()'>
          <xsl:text>,</xsl:text>
        </xsl:when>
      </xsl:choose>
    </xsl:for-each>
    <xsl:text>}</xsl:text>

    <xsl:text>,reviews:[</xsl:text>
    <xsl:for-each select="aws:CustomerReviews/aws:Review">
      <xsl:text>{summary:'</xsl:text>
      <xsl:call-template name="sanitize-text">
        <xsl:with-param name="target" select="normalize-space(aws:Summary)"/>
      </xsl:call-template>
      <xsl:text>',content:'</xsl:text>
      <xsl:call-template name="sanitize-text">
        <xsl:with-param name="target" select="normalize-space(aws:Content)"/>
      </xsl:call-template>
      <xsl:text>'}</xsl:text>
      <xsl:choose>
        <xsl:when test='position() &lt; last()'>
          <xsl:text>,</xsl:text>
        </xsl:when>
      </xsl:choose>
    </xsl:for-each>
    <xsl:text>]</xsl:text>

    <xsl:text>}</xsl:text>
  </xsl:template>

  <xsl:template name="image">
    <xsl:param name="type"/>

    <xsl:value-of select="translate($type,$ucletters,$lcletters)"/>
    <xsl:text>:{</xsl:text>
    <xsl:text>src:'</xsl:text>
    <xsl:value-of select="aws:URL"/>
    <xsl:text>',</xsl:text>

    <xsl:text>width:</xsl:text>
    <xsl:value-of select="aws:Width"/>
    <xsl:text>,</xsl:text>

    <xsl:text>height:</xsl:text>
    <xsl:value-of select="aws:Height"/>
    <xsl:text>}</xsl:text>
  </xsl:template>

  <xsl:template name="sanitize-text">
    <xsl:param name="target"/>
    <xsl:choose>
      <xsl:when test="contains($target, '&quot;')">
        <xsl:value-of select="substring-before($target, '&quot;')"/>
        <xsl:call-template name="sanitize-text">
          <xsl:with-param name="target" select="substring-after($target, '&quot;')"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise><xsl:value-of select="$target"/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>
ソース:aws2json.xmsl
ポイントとしては"apply-template"のselectを使うのではなく"xsl:for-each"を使うことで"position()"および"last()"を使った最終要素時のカンマ制御を行っている所。FirefoxではokですがIEだとエラーになっちゃいますからね。
そしてjavascript部分 $(document).ready(function() {
  var unsanitize = function(text) {
    return (text||'').replace(/&amp;/g, '&').replace(/&gt;/g, '>').replace(/&lt;/g, '<');
  }
  $('#aws-word').keydown(function(e) { if (e.keyCode == 13) $('#aws-search').click() });
  $('#aws-search').click(function() {
    if (!$('#aws-word').val()) {
      $('#aws').html('');
      return;
    }
    $('#aws').html('<img src="http://mattn.kaoriya.net/images/ajax-loader.gif"/>');
    $.ajaxSettings.cache = true;
    $.getJSON('http://xml-jp.amznxslt.com/onca/xml?callback=?',
      {
        Service: 'AWSECommerceService',
        SubscriptionId: '1GXPBVS13GJVA58PKVG2',
        AssociateTag: 'bigsky-22',
        Operation: 'ItemSearch',
        SearchIndex: $('#aws-kind').val(),
        ResponseGroup: 'Medium,Offers,Reviews',
        Version: '2005-10-05',
        Keywords: $('#aws-word').val(),
        ContentType: 'text/plain',
        Style: 'http://mattn.kaoriya.net/misc/aws2json.xsl'
      }, function(data) {
        $('#aws').html('');
        $.each(data.result, function(index, item) {
          $('<div>')
            .css('border', '1px dotted black')
            .css('border', '1px dotted black')
            .css('background-color', '#eeeeee')
            .css('padding', '0.5em')
            .css('margin', '0.5em')
            .attr('id', 'aws' + index)
            .hide()
            .appendTo('#aws');
          var c = $('#aws' + index);
      $('<a/>')
        .appendTo(c)
        .attr('href', item.url)
            .text(unsanitize(item.title))
            .appendTo(c);
          var a = $('a', c);
          if (item.image.medium) {
            $('<img>')
              .css('vertical-align', 'top')
              .css('padding', '0.5em')
              .css('border', '0px')
              .css('float', 'left')
              .attr('title', item.title)
              .attr('src', item.image.medium.src)
              .prependTo(a);
          }
          a.after('<br />');

          $.each(item.authors, function(index, item) {
            $('<b>')
              .text(item)
              .appendTo(c)
              .after('<br />');
          });
          $('#aws' + index)
            .append('<span>ASIN: ' + item.asin + '</span>')
            .append('<br />')
            .append('<br />')
            .append(item.publisher + '/' + item.price + ' (' + item.date + ')')
            .append('<br />')
            .append(item.availability);
          $(c).append('<br clear="all" /><br />');
          if (item.reviews.length) {
            $('<a href="#">review comments</a>')
              .css('font-size', 'small')
              .css('color', 'blue')
              .appendTo(c)
              .click(function() { $('.reviews', c).toggle('slow'); return false; });
            $('<div>')
              .attr('class', 'reviews')
              .css('display', 'none')
              .css('font-size', 'small')
              .appendTo(c);
            $.each(item.reviews, function(index, item) {
              $('.reviews', c)
                .append('<span class="name"><strong>' + item.summary + '</strong></span><br />')
                .append('<div class="comment">' + unsanitize(item.content) + '</div>')
                .append('<br />');
              $('.comment', c)
                .css('border', '1px dotted gray')
                .css('background-color', 'white')
                .css('padding', '0.5em')
            });
          }
        });
        $('div', '#aws').fadeIn('slow', function() {
          $('.reviews').hide();
        });
      }
    );
  });
});
amazon web serviceのXSLTプロセッサにcallback付きのXSLを処理させる事でjQueryのcallbackを呼び出させています。
getJSONのデータ部を弄る事である程度動きが変えられるかと思います。ただしOperationのItemSearchを変えると動かなくなりますので要注意です。
今回のサンプルとしてはレビューコメントも表示されるようになっています。"review comments"をクリックすると展開されます。
jQueryとXSLだけで動くのでCGIの動かないサーバでもokです。
あくまでサンプルですが、どなたかの参考になれば...

以下実行例




Posted at 16:48 | WriteBacks () | Edit
Edit this entry...

wikieditish message: Ready to edit this entry.






















A quick preview will be rendered here when you click "Preview" button.