RDFa with schema.org codelab: Library holdings

By Dan Scott,

About this codelab

In a previous exercise, you marked up a catalog page with the schema.org vocabulary using RDFa attributes. In this exercise, you will focus on marking up the library holdings so that they can be recognized as Products with associated Offers by search engines (analogous to products available in online stores).

Audience: Intermediate

Prerequisites: To complete this codelab, you should already be familiar with HTML, RDFa, and schema.org. The previous exercise offers a practical introduction to those concepts.

Turning library holdings into Product

schema.org adopted the existing GoodRelations vocabulary as a way of expressing products and services that are available for sale. As it turns out, although some of the property and type names reflect a commercial bias, you can use the same vocabulary elements can to express non-commercial offers--such as the offer to lend a library resource to a patron.

In this exercise, you will turn the existing Book entity into a multi-typed entity by adding Product to the type declaration. This enables the described resource to satisfy properties expecting a value of either Book or Product, and also makes a union of the properties from those types available to further describe the resource.

View the page source HTML

Open step1/rdfa_book.html in a text editor. You should see something like the following HTML source for the web page:

<!DOCTYPE html>
<html>
<head>
  <title>Las Vegas-Clark County Library District /All Locations</title>
</head>

<body vocab="http://schema.org/" typeof="Book" resource="#book">
  <link property="sameAs" href="https://www.googleapis.com/freebase/v1/rdf/m/01069fkb">
  <link property="sameAs" href="http://worldcat.org/entity/work/id/1782516719">
  <link property="sameAs" href="http://www.scholastic.ca/books/view/spirit-animals-book-three-blood-ties">
  <meta property="typicalAgeRange" content="8-12">
  <div id="coverImage">
...

Note: In a pinch, you can use the browser development tools to view and edit the source of the web page (CTRL-Shift-i in Chrome or Firefox, in the Elements or Inspector tab respectively).

Add Product to the type declaration

Add Product to the typeof attribute in the <body> element. Check the results with one or more of the structured data testing tools.

You should see that both the Book and Product types are recognized. You may note, however, that Google's SDTT only recognizes the first declared type; this is a limitation of their tool and does not necessarily represent the results of their actual search engine.

Check your markup
<!DOCTYPE html>
<html>
<head>
  <title>Las Vegas-Clark County Library District /All Locations</title>
  <style>...</style>
</head>

<body vocab="http://schema.org/" typeof="Book Product" resource="#book">
...

Declare each holding as a separate Offer

Products link to their offers via the offers property. The expected range of the offers property is an Offer entity.

Declare each holding in the catalog record as a separate Offer.

Check your markup
<!DOCTYPE html>
<body vocab="http://schema.org/" typeof="Book" resource="#book">
...
  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td>Centennial Hills YPL</td>
    <td><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td>ON HOLDSHELF</td>
  </tr>

  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td>Centennial Hills YPL</td>
    <td><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td>ON HOLDSHELF</td>
  </tr>
...

Identify the seller, offer terms, and price of the Offer

While sellers of bibliographic items have a fairly clear mapping to Offer, the applicability for libraries is a bit less obvious. To express individual items that are available to purchase or borrow, begin by mapping your holding properties to Offer properties as follows:

Check your markup
<!DOCTYPE html>
<body vocab="http://schema.org/" typeof="Book" resource="#book">
...
  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td property="seller">Centennial Hills YPL</td>
    <td><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td>ON HOLDSHELF
      <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
      <meta property="price" content="0.00">
    </td>
  </tr>

  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td property="seller">Centennial Hills YPL</td>
    <td><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td>ON HOLDSHELF
      <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
      <meta property="price" content="0.00">
    </td>
  </tr>

  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td property="seller">Centennial Hills YPL</td>
    <td><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td>DUE 06-10-14
      <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
      <meta property="price" content="0.00">
    </td>
  </tr>

  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td property="seller">Enterprise YPL</td>
    <td><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td>AVAILABLE
      <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
      <meta property="price" content="0.00">
    </td>
  </tr>
...

Show the availability

The item holding status best maps to the availability property, which can take one of the following values:

If you have chosen to publicly display due dates in your system, you can also use the availabilityStarts property to provide an estimate of when the item will be available. The property expects a date/time value in ISO8601 format following the pattern YYYY-MM-DDThh:mm:ss (with an optional positive or negative time appended to indicate a time zone offset). Reminder: use the content attribute to supply the machine-readable counterparts for human-readable values.

Reflect the availability of each holding in the markup of the page.

Check your markup
<!DOCTYPE html>
<body vocab="http://schema.org/" typeof="Book" resource="#book">
...
  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td property="seller">Centennial Hills YPL</td>
    <td><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td>ON HOLDSHELF
      <link property="availability" href="http://schema.org/OutOfStock">
      <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
      <meta property="price" content="0.00">
    </td>
  </tr>

  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td property="seller">Centennial Hills YPL</td>
    <td><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td>ON HOLDSHELF
      <link property="availability" href="http://schema.org/OutOfStock">
      <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
      <meta property="price" content="0.00">
    </td>
  </tr>

  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td property="seller">Centennial Hills YPL</td>
    <td><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td property="availabilityStarts" content="2014-06-10T00:00:00">DUE 06-10-14
      <link property="availability" href="http://schema.org/OutOfStock">
      <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
      <meta property="price" content="0.00">
    </td>
  </tr>

  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td property="seller">Enterprise YPL</td>
    <td><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td>AVAILABLE
      <link property="availability" href="http://schema.org/InStock">
      <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
      <meta property="price" content="0.00">
    </td>
  </tr>
...

Provide finer grained location information

Earlier, you identified with which library each item is associated; however, that may not be enough to identify the location of a given copy of a resource. There are a number of other Offer properties that are suitable for providing more granular location information for items:

Add markup for the call number in the holdings page.

Check your markup
<!DOCTYPE html>
<body vocab="http://schema.org/" typeof="Book" resource="#book">
...
  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td property="seller">Centennial Hills YPL</td>
    <td property="sku"><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td>ON HOLDSHELF
      <link property="availability" href="http://schema.org/OutOfStock">
      <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
      <meta property="price" content="0.00">
    </td>
  </tr>

  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td property="seller">Centennial Hills YPL</td>
    <td property="sku"><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td>ON HOLDSHELF
      <link property="availability" href="http://schema.org/OutOfStock">
      <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
      <meta property="price" content="0.00">
    </td>
  </tr>

  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td property="seller">Centennial Hills YPL</td>
    <td property="sku"><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td property="availabilityStarts" content="2014-06-10T00:00:00">DUE 06-10-14
      <link property="availability" href="http://schema.org/OutOfStock">
      <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
      <meta property="price" content="0.00">
    </td>
  </tr>

  <tr class="bibItemsEntry" property="offers" typeof="Offer">
    <td property="seller">Enterprise YPL</td>
    <td property="sku"><a href="/search~S12?/hJ/hj/-3,-1,,B/browse">J</a></td>
    <td>AVAILABLE
      <link property="availability" href="http://schema.org/InStock">
      <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
      <meta property="price" content="0.00">
    </td>
  </tr>
...

Checkpoint: Your original HTML page should now look like step1/check_a.html.

Lessons learned

In this exercise, you learned how to express library holdings using the GoodRelations agent-promise-object model, adapting commercial properties to non-commercial usage where necessary.

About the author

Dan Scott is a systems librarian at Laurentian University.

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.