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() < 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() < 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() < 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() < 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() < 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, '"')">
<xsl:value-of select="substring-before($target, '"')"/>
<xsl:call-template name="sanitize-text">
<xsl:with-param name="target" select="substring-after($target, '"')"/>
</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(/&/g, '&').replace(/>/g, '>').replace(/</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です。
あくまでサンプルですが、どなたかの参考になれば...
以下実行例