Developer Console

Step 7: Contents Recipe: Query Parameters

Let's continue configuring the Contents recipe by populating the query and queryResultType parameters.

query Parameter

The syntax for JSON feeds differs significantly from the syntax for XML feeds, so the two formats are treated in separate tabs. Note that although the query syntax in this part of the configuration might seem a little complex, remember that Fire App Builder lets you use any feed structure you want, without requiring a specific order or specification. With this flexibility, it's unavoidable that you'll need to use a more advanced query syntax to target the elements in your feed.

JSON

In the sample app in Fire App Builder, the value for query is $[?(@.categories[0] in [$$par0$$])]. As with the query parameter in the Categories recipe, this syntax is (mostly) Jayway JsonPath syntax. This syntax uses a Jayway JsonPath filter operator to select the items inside the categories array that have at least one item at the 0 position.

Let's take a step back to unpack this syntax with more clarity, because you'll need to customize this query to fit your own feed syntax. The sample Lightcast feed (which is what the sample Fire App Builder uses) looks like this:

[  
   {  
      "id":"169313",
      "title":"Beautiful Whale Tail Uvita Costa Rica",
      "description":"Beautiful Whale Tail Uvita Costa Rica",
      "duration":"86",
      "thumbURL":"http://le2.cdn01.net/videos/0000169/0169313/thumbs/0169313__007f.jpg",
      "imgURL":"http://le2.cdn01.net/videos/0000169/0169313/thumbs/0169313__007f.jpg",
      "videoURL":"http://edge-vod-media.cdn01.net/encoded/0000169/0169313/video_1880k/T7J66Z106.mp4?source=firetv&channel_id=13454",
      "categories":[  
         "Costa Rica Islands"
      ],
      "channel_id":"13454"
   },
   {  
      "id":"169322",
      "title":"Scuba Diving - Costa Rica",
      "description":"Scuba Diving - Costa Rica (Playa Ocotal & The Bat Islands)",
      "duration":"205",
      "thumbURL":"http://le1.cdn01.net/videos/0000169/0169322/thumbs/0169322__002f.jpg",
      "imgURL":"http://le1.cdn01.net/videos/0000169/0169322/thumbs/0169322__002f.jpg",
      "videoURL":"http://edge-vod-media.cdn01.net/encoded/0000169/0169322/video_1880k/S6IZ6M1QM.mp4?source=firetv&channel_id=13455",
      "categories":[  
         "Costa Rica Underwater"
      ],
      "channel_id":"13455"
   },
   {  
      "id":"169312",
      "title":"San Lucas Trip",
      "description":"Isla San Lucas, Puntarenas, Costa Rica",
      "duration":"192",
      "thumbURL":"http://le1.cdn01.net/videos/0000169/0169312/thumbs/0169312__003f.jpg",
      "imgURL":"http://le1.cdn01.net/videos/0000169/0169312/thumbs/0169312__003f.jpg",
      "videoURL":"http://edge-vod-media.cdn01.net/encoded/0000169/0169312/video_1880k/M0CAAG18G.mp4?source=firetv&channel_id=13454",
      "categories":[  
         "Costa Rica Islands"
      ],
      "channel_id":"13454"
   },
   {  
      "id":"169309",
      "title":"San Jose, Costa Rica",
      "description":"San Jose, Costa Rica",
      "duration":"130",
      "thumbURL":"http://le2.cdn01.net/videos/0000169/0169309/thumbs/0169309__009f.jpg",
      "imgURL":"http://le2.cdn01.net/videos/0000169/0169309/thumbs/0169309__009f.jpg",
      "videoURL":"http://edge-vod-media.cdn01.net/encoded/0000169/0169309/video_1880k/88HFXX0IL.mp4?source=firetv&channel_id=13453",
      "categories":[  
         "Costa Rica Attractions"
      ],
      "channel_id":"13453"
   }
]

(This isn't the full feed, but it shows the repeating structure.)

Plug this feed into the Jayway JsonPath Evaluator. Then run the following query:

$[?(@.categories[0] in ["Costa Rica Islands"])]

This query returns the following:

[
   {
      "id" : "136216",
      "title" : "Falmouth Jamaica Nature's Lullaby ",
      "description" : "Falmouth Jamaica Nature's Lullaby",
      "duration" : "1813",
      "thumbURL" : "http://l4.cdn01.net/_thumbs/0000136/0136216/0136216__001f" type="jpg",
      "imgURL" : "http://l4.cdn01.net/_thumbs/0000136/0136216/0136216__001f" type="jpg",
      "videoURL" : "http://media.cdn01.net/802E1F/process/encoded/video_1880k/0000136/0136216/L2XGJI1LM.mp4?source=firetv&channel_id=13672",
      "categories" : [
         "The Country Jamaica"
      ],
      "channel_id" : "13672"
   }
]

Here's what each part of query syntax selects in the feed:

Query Syntax What It Matches
$[ Starts at the root and selects the unnamed array.
?(@.categories[0] in ["The Country Jamaica"])] Creates a filter for all categories arrays that contain ["The Country Jamaica"] at the 0 index position.

Note that there's one small difference in the query syntax used in the sample Fire App Builder app. Instead of ["The Country Jamaica"], the query parameter uses $$par0$$ instead:

"query": "$[?(@.categories[0] in [$$par0$$])]"

$$par0$$ is a custom variable defined in Fire App Builder. In the recipe code, the keyDataType parameter from the Categories recipe populates the $par0$$ variable in the Contents recipe with a list of categories.

Let's go through one more example to clarify how this works. Suppose your JSON looks like this:

{
    "titles": {
        "video": [
            {
                "category": "reference",
                "publisher": "Jess",
                "title": "Video title 1",
                "price": 3.95
            },
            {
                "category": "history",
                "publisher": "John",
                "title": "Video title 2",
                "price": 2.99
            },
            {
                "publisher": "Dave",
                "title": "Video title 3",
                "price": 8.99
            },
            {
                "category": "science",
                "publisher": "Jess",
                "title": "Video title 4",
                "price": 3.99
            }
        ]
     }
}

To select all arrays containing science as category, you would use this query:

$.titles.video[?(@.category contains "science")]

This returns:

[
   {
      "category" : "science",
      "publisher" : "Jess",
      "title" : "Video title 4",
      "price" : 3.99
   }
]

However, we need all items in the array that contain category, so we remove the conditions around the @.category:

$.titles.video[?(@.category)]

Now add the [$$par0$$] variable:

$.titles.video[?(@.category in [$$par0$$])]

Here's a summary of the syntax:

Query Syntax What It Matches
$.titles.video[ Selects the title object and the video array.
?(@.category in [$$par0$$] Filters the array to match on all items that have a category.

Note that when you add in [$$par0$$] in the Jayway JsonPath Evaluator, the Evaluator will not understand this syntax because it's specific to Fire App Builder rather than part of Jayway JsonPath. The query can only be tested by running your Fire App Builder app on Fire TV.

Also note that your query will look different based on your feed and the syntax necessary to match the content objects. For example, here's a more complex query:

$.assets[?(@.type == 'movie.Container' && @.assetId in $$par0$$)]

This query starts at the root ($), looks in the first directory level (.) to the object named assets, and filters the array to type objects that are equal to movie.Container and which contain an assetId element. This more complex example demonstrates the power of Jayway JsonPath — you can use it to target the elements of almost any feed structure.

XML

If your feed is XML, instead of using Jayway JsonPath, you will use XPath expressions to target the specific elements in your feed. XPath reduces your XML document into various "nodes." The XPath syntax allows you to target the location of specific nodes.

In the sample MRSS XML app, the value for the query parameter in the contents recipe is as follows:

"query": "//item[./category='$$par0$$']"

Let's take a step back to unpack this syntax with more clarity, because you'll need to customize this query to fit your own feed syntax.

A sample XML feed looks like this:

<rss>
    <channel>
        <item>
            <title>Sample Title 1</title>
            <pubDate>Wed, 26 Oct 2016 20:34:22 PDT</pubDate>
            <link>https://example.com/myshow/episodes/110</link>
            <author>Sample Author name</author>
            <category>Gadgets</category>
        </item>

        <item>
            <title>Sample Title 2</title>
            <pubDate>Mon, 24 Oct 2016 09:24:12 PDT</pubDate>
            <link>https://example.com/myshow/episodes/109</link>
            <author>Sample Author name</author>
            <category>Technology</category>
        </item>
    </channel>
</rss>

With this feed structure, the query for your content recipe would be as follows:

//item[./category='$$par0$$']

Plug this sample XML feed into the XPath Tester. Then run the following query:

//item[./category='Technology']

The result is as follows:

Element='<item>
        <title>Sample Title 2</title>
        <pubDate>Mon, 24 Oct 2016 09:24:12 PDT</pubDate>
        <link>https://example.com/myshow/episodes/109</link>
        <author>Sample Author name</author>
        <category>Technology</category>
        </item>'

Here's what each part of this query syntax matches:

Query Syntax What It Matches
//item Matches all instances of the item element regardless of its position in the XML structure.
?[./category='Technology'] Selects all categories that are attributes of the item element where the category is equal to Technology.

Note that there's one small difference in query syntax used in the sample Fire App Builder app. Instead of ["Technology"], the query parameter uses $$par0$$ instead:

"query": "//item[./category='$$par0$$']"

$$par0$$ is a custom variable defined in Fire App Builder. In the recipe code, the keyDataType parameter from the Categories recipe populates the $par0$$ variable in the Contents recipe with a list of categories.

After you confirm that your query targets a specific category in your feed, replace your category (for example, Technology) with the variable $$par0$$.

Here's a summary of the syntax:

Query Syntax What It Matches
//item Selects all the item elements in the feed.
[./category='$$par0$$'] Filters the selection to match on all items that have a category.

See Querying XML for more details about constructing XPath queries.

Targeting Elements with Namespaces

As you create the syntax for the query parameter, note the following limitations with namespaces. If you're trying to target elements with namespaces such as <media:category> (as the Media RSS specification requires) or <itunes: category text="Apples"/> (as the iTunes RSS tags require), not all XPath expressions will work. The following two sections provide some guidance for these scenarios.

Media RSS Categories Example

Suppose your XML feed follows the Media RSS specification and looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
   <channel>
      <title>Sample MRSS Feed</title>
      <link>https://some-site.com/mrss.xml</link>
      <language>en-us</language>
      <description>MRSS Feed For Amazon Test</description>
      <atom:link href="https://some-site.com/mrss.xml" rel="self" type="application/rss+xml" />
      <item>
         <title>May 18, 2018 - Apples</title>
         <description>May 18, 2018. Apples are a delicious fruit. Watch this fun documentary about apple growing and making.</description>
         <pubDate>Thu, 31 May 2018 22:45:38 GMT</pubDate>
         <guid>https://some-site.com/apples.mp4</guid>
         <itunes:subtitle>May 18, 2018. Apples are a delicious fruit. Watch this fun documentary about apple growing and making.</itunes:subtitle>
         <media:category>Farming</media:category>
         <media:content url="https://some-site.com/apples.mp4" type="application/mp4" medium="video" duration="2610.688" isDefault="true">
            <media:title>May 18, 2018 - Apples</media:title>
            <media:description>May 18, 2018.  Apples are a delicious fruit. Watch this fun documentary about apple growing and making.</media:description>
            <media:thumbnail url="http://some-site.com/thumbnails/apples.jpg" height="393" width="699" />
         </media:content>
      </item>
      <item>
         <title>May 23, 2018 - Bananas</title>
         <description>May 23, 2018. Bananas are a bunch of fun. Watch this interesting documentary about how to grow bananas.</description>
         <pubDate>Thu, 31 May 2018 22:45:41 GMT</pubDate>
         <guid>https://sample-server..net/3023434001/2345/2335/30349384989301.mpd</guid>
         <itunes:subtitle>May 23, 2018. Bananas are a bunch of fun. Watch this interesting documentary about how to grow bananas..</itunes:subtitle>
         <media:category>Lifestyle</media:category>
         <media:content url="https://some-site.com/bananas.mp4" type="application/mp4" medium="video" duration="2610.688" isDefault="true">
            <media:title>May 18, 2018 - Apples</media:title>
            <media:description>May 18, 2018. Bananas are a bunch of fun. Watch this interesting documentary about how to grow bananas.</media:description>
            <media:thumbnail url="http://some-site.com/thumbnails/bananas.jpg" height="393" width="699" />
         </media:content>
      </item>
   </channel>
</rss>

To target the items in your Contents recipe, you cannot use this query:

"query": "//item/media:category/text() in $$par0$$",

Although this will work in XPath Tester, Fire App Builder doesn't use the same XPath parser. Instead, you must use this syntax:

"query": "//item[*[name()='media:category']='$$par0$$']"

This query looks for all nodes with the name item. In those nodes, Fire App Builder uses a wildcard to look for the name media:category.

Note that this special syntax restriction with namespaces applies only to the query parameter. The matchList parameter doesn't use XPath expressions, so no special treatment is needed with matchList.

iTunes RSS Tags Example

Suppose your feed follows the iTunes RSS tags and looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:media="http://search.yahoo.com/mrss/" version="2.0" xml:base="http://www.nasa.gov/">
   <channel>
      <title>Sample 1</title>
      <description>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tincidunt vehicula placerat. Nunc eget auctor leo. Donec vitae neque vehicula, fermentum risus et, scelerisque felis. </description>
      <link>http://www.example.org/</link>
      <itunes:subtitle>Quisque egestas nec metus ac ullamcorper. In a semper ex, vulputate pellentesque massa.</itunes:subtitle>
      <itunes:summary>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tincidunt vehicula placerat. Nunc eget auctor leo. Donec vitae neque vehicula, fermentum risus et, scelerisque felis. </itunes:summary>
      <itunes:category text="Fruits" />
      <itunes:keywords>fruit, baskets, farms</itunes:keywords>
      <itunes:image href="https://www.example.org/images/somelogo.png" />
      <item>
         <title>Weekly News, August 11, 2018</title>
         <link>http://www.example.org/weeklynews/august-18-2018.html</link>
         <description>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tincidunt vehicula placerat. Nunc eget auctor leo. Donec vitae neque vehicula, fermentum risus et, scelerisque felis. </description>
         <enclosure url="https://amzndevresources.com/fire-app-builder/media/bunny.mp4" length="44842035" type="video/mp4" />
         <guid isPermaLink="false">august-11-2018</guid>
         <pubDate>Sat, 11 Aug 2018 09:00 EDT</pubDate>
         <source url="http://example.org.rss">Example Video</source>
         <itunes:category text="Apples" />
         <itunes:image href="https://amzndevresources.com/fire-app-builder/media/card.png" />
      </item>     
      <item>
         <title>Weekly News, August 30, 2018</title>
         <link>http://www.example.org/weeklynews/august-18-2018.html</link>
         <description>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tincidunt vehicula placerat. Nunc eget auctor leo. Donec vitae neque vehicula, fermentum risus et, scelerisque felis. </description>
         <enclosure url="https://amzndevresources.com/fire-app-builder/media/bunny.mp4" length="44842333" type="video/mp4" />
         <guid isPermaLink="false">august-12-2018</guid>
         <pubDate>Sat, 12 Aug 2018 09:00 EDT</pubDate>
         <source url="http://example.org.rss">Example Video</source>
         <itunes:category text="Oranges" />
         <itunes:image href="https://amzndevresources.com/fire-app-builder/media/card.png" />
      </item>  
   </channel>
</rss>

Note that with iTunes feeds, there's a general category for the feed (such as <itunes:category text="Fruits">) as well as categories for each item (<itunes:category text="Apples" />). When you target categories for your recipe, you want to target the categories for each item in the feed, not the general feed categories.

To select <itunes:category text="Apples" />, you would use this in your Contents recipe:

"query": "//item[./*[name()='itunes:category'][@text='$$par0$$']]"

This will return more than just the category name. It will return all items that match the parameter:

<item>
   <title>Weekly News, August 11, 2018</title>
   <link>http://www.example.org/weeklynews/august-18-2018.html</link>
   <description>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tincidunt vehicula placerat. Nunc eget auctor leo. Donec vitae neque vehicula, fermentum risus et, scelerisque felis. </description>
   <enclosure length="44842035"
        type="video/mp4"
        url="https://amzndevresources.com/fire-app-builder/media/bunny.mp4"/>
   <guid isPermaLink="false">august-11-2018</guid>
   <pubDate>Sat, 11 Aug 2018 09:00 EDT</pubDate>
   <source url="http://example.org.rss">Example Video</source>
   <itunes:category xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" text="Apples"/>
   <itunes:image xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
           href="https://amzndevresources.com/fire-app-builder/media/card.png"/>
</item>

You can then use the matchList parameters in the recipe (described in the next step) to select the actual attributes. For example, the Categories recipe would look like this:

{
  "cooker": "DynamicParser",
  "format": "xml",
  "model": "com.amazon.android.model.content.Content",
  "translator": "ContentTranslator",
  "modelType": "array",
  "query": "//item[./*[name()='itunes:category'][@text='$$par0$$']]",
  "matchList": [
    "title/#text@mTitle",
    "guid/#text@mId",
    "description/#text@mDescription",
    "enclosure/#attributes/url@mUrl",
    "itunes:image/#attributes/href@mCardImageUrl",
    "itunes:image/#attributes/href@mBackgroundImageUrl"
  ]
}

queryResultType Parameter

Fire App Builder needs to take the result from the query parameter and convert it into a HashMap to process in a Java class. The next parameter (queryResultType) is used to convert the result into an array of objects. In the sample Fire App Builder application, the value for queryResultType is as follows:

"queryResultType": "[]$",

In this case, the result from the query parameter is a list of strings. This syntax, []$, will convert these strings into a HashMap for processing.

If the result from your query parameter returns a list of strings, include the queryResultType parameter in your Categories recipe and set it equal to []$. If the query's result already an object (a map), omit this parameter altogether.

Next Steps

To finish up with the contents recipe, you need to configure the matchlist and keyDataType parameters. Continue to the next step: Contents Recipe: matchList Parameter.


Last updated: Jan 05, 2020