読者です 読者をやめる 読者になる 読者になる

JSONP ならぬ HTMLP を Amazon XSLT で試してみた

Amazon Web Services では Style 引数に XSL ファイルの URL を指定することでレスポンスの XML を Amazon のサーバ側でパースできる。これを使って、JSONP 形式に返すサンプルが色々あった。でも単純に表示するだけなら innerHTML に HTML を流し込むだけの方が楽だろう。
ということで JSON with Padding ならぬ HTML with Padding(で合ってる??)を試しに作ってみることにした。

function awsResult(html){
 document.getElementById('content').innerHTML = html;
}

と定義しておいて、Script タグの Padding によって、

awsResult('<ul><li> ・・・ </ul>');

最終的に上記のように返ってくれば、<ul><li> ・・・ </ul> の部分を content に表示できる。

awshtmlp.xsl

AWS に渡す XSL ファイルの内容は次のとおり。
ポイントは Callback 引数を拡張定義することで aws:OperationRequest/aws:Arguments/aws:Argument[@Name='Callback']/@Value から引っ張って出力していること。これでコールバック関数名(前述の awsResult)は固定せずに済む。
また HTML 出力としているが結果は JavaScript であるので、インデントはさせない(改行はさせない)で、「'」で囲う。そして escape-apos でタイトルとラベルに含まれている場合はエスケープするようにしている。

<?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="html" indent="no" encoding="UTF-8"/>
  
  <xsl:template match="/aws:ItemSearchResponse">
   <xsl:value-of select="aws:OperationRequest/aws:Arguments/aws:Argument[@Name='Callback']/@Value"/>
   <xsl:text>('</xsl:text>
   <xsl:apply-templates select="aws:Items"/>
   <xsl:text>')</xsl:text>
  </xsl:template>
  
  <xsl:template match="aws:Items">
    <ul class="amazonList">
     <xsl:apply-templates select="aws:Item"/>
    </ul>
  </xsl:template>
  
  <xsl:template match="aws:Item">
   <li>
    <a>
     <xsl:attribute name="href"><xsl:value-of select="aws:DetailPageURL"/></xsl:attribute>
     <xsl:call-template name="escape-apos">
	  <xsl:with-param name="target" select="aws:ItemAttributes/aws:Title"/>
     </xsl:call-template>
    </a>
    <xsl:apply-templates select="aws:SmallImage"/>
    <span class="label">
     <xsl:call-template name="escape-apos">
	  <xsl:with-param name="target" select="aws:ItemAttributes/aws:Label"/>
     </xsl:call-template>
    </span>
    <span class="price">
     <xsl:value-of select="aws:ItemAttributes/aws:ListPrice/aws:FormattedPrice"/>
    </span>
   </li>
  </xsl:template>
  
  <xsl:template match="aws:SmallImage">
   <img>
    <xsl:attribute name="src"><xsl:value-of select="aws:URL"/></xsl:attribute>
    <xsl:attribute name="width"><xsl:value-of select="aws:Width"/></xsl:attribute>
    <xsl:attribute name="height"><xsl:value-of select="aws:Height"/></xsl:attribute>
   </img>
  </xsl:template>
  
  <xsl:template name="escape-apos">
   <xsl:param name="target"/>
   <xsl:variable name="m"><xsl:text>&apos;</xsl:text></xsl:variable>
   <xsl:variable name="r"><xsl:text>&amp;apos;</xsl:text></xsl:variable>
   <xsl:choose>
	<xsl:when test="contains($target, $m)">
	 <xsl:value-of select="substring-before($target, $m)"/>
	 <xsl:value-of select="$r"/>
	 <xsl:call-template name="escape-apos">
	  <xsl:with-param name="target" select="substring-after($target, $m)"/>
	 </xsl:call-template>
	</xsl:when>
	<xsl:otherwise><xsl:value-of select="$target"/></xsl:otherwise>
   </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

サンプル HTML

下記は検索ボックスの内容を HTML with Padding(?) で投げて、結果を表示するサンプルである。

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Amazon XSLT HTMLP Sample</title>
<script type="text/javascript">
function awsResult(html){
 document.getElementById('content').innerHTML = html;
 document.getElementById('findButton').disabled = false;
 document.body.removeChild(document.getElementById('sc'));
}

function find(){
  var sc = document.createElement('script');
  sc.id = "sc";
  sc.src = 'http://webservices.amazon.co.jp/onca/xml?Service=AWSECommerceService'
   + '&AWSAccessKeyId=☆アクセスキー☆'
   + '&ResponseGroup=ItemAttributes,Images&Operation=ItemSearch'
   + '&SearchIndex=Blended'
   + '&Callback=awsResult'
   + '&Style=☆前述の XSL ファイルの URL ☆'
   + '&Keywords='
   + encodeURIComponent(document.getElementById('query').value);
  document.getElementById('findButton').disabled = true;
  document.body.appendChild(sc);
}
</script>
</head>
<body>
<form onsubmit="find(); return false;">
 <input id="query" type="text" size="40"><input type="submit" id="findButton" value=" 検索 ">
</form>
<div id="content"></div>
</body>
</html>

サンプルのイメージは

まとめ

JSONP の場合はブラウザサイドでコンテンツをダイナミックに扱いたいときに使い、HTMLP は決まった形式で出したいときに使うのがいいだろう。もちろん HTMLP でも CSS で出力スタイルのコントロールは十分できる。