schema.org/Products with multipule values

Using rich snippets schema for Products that have differing prices based on size, color etc with a common URL

HTML, Schema.org, Semantic Web, Products

It appears that the ontology for Product doesn't allow for a single URL/Endpoint to contain ranging prices based on differing sizes, how are we going to deal with them.

It's really easy to add schema data to a product page. All you have to do is reference the product ontology, add the appropriate attributes itemscope, itemtype & itemprop to the relavant elements.

But the schema assumes a single endpoint / url, with multipule offers for a given product but offer doesn't have vocabulary for a range of size / colours that might affect the offer.

Eh? what does that mean?

Many products are simple. That's to say: they are the same price regardless of colour/size. So a single page can be used for the different colour or size options with a single offer.

But, what if a given product pirce changes based on the colour/size? Let's first think about what products might be avaliable in different size and how their prices are affected.

Product  
Shirt Might be available in S, M, L & XL each with different prices
Cable Might be available in 1m, 2m & 3m each with different prices
Book Might be hardback / softback, again, each with different prices

The Schema for Product & the respective Offer is structured like this :

  • Product
    • Name
    • Description
    • URL
    • Offers
      • Offer
        • Price
        • Currency
        • Avaliblity
        • Condition

The easy solution is to duplicate the page for all the different sizes. I'm sure most, so called, SEO Experts are would advocate this solution.

I'm sure we've all heard something along the line of :

More content, more URLs better search results. A so-called SEO Expert (AKA Web Spammer)

I call Bollex and suggest you run in opposite direction from that web spammer before you get penalised and pay through the nose for the pleasure.

Vaild, structured semantic content is far more benificial than spamming the web with unnessasary duplications. SEO Experts are the bane of my life!

Me

For me the visitor is King, surly most people would prefer to see all size / colour options on a single page; having to navigate to a new URL to view a different size is just silly; the product details shouldn't change, it's the same 'thing', just option associated with it.

Books Have the Vocab!

Now there's already some schema.org ontology for the case of Hard/Soft back Books using the BookFormatType via the values:

There's a post on the use of BookFormatType vocabs on the W3C maillist back in Feb 2012.

The name and all the attributes associated with the Book such as Author, Edition etc are defined on the book schema.

Here's the markup, slight modified to help make it more legiable, semantic & removed props that no longer exist in the schema definition :

<!-- Start Book !-->
<div itemscope itemtype="http://schema.org/Book" id="Catcher_In_The_Rye">

  <!-- Book Title / Name !-->
  <h1 itemprop="name">The Catcher in the Rye</h1>

  <!-- Hardcover Offer !-->
  <div itemprop="offers" itemscope itemtype="http://schema.org/Offer" id="HardBackOffer">

    <dl itemprop="itemOffered" itemscope itemtype="http://schema.org/Book">       
      <dt itemprop="bookFormat" href="http://schema.org/Hardcover"> 
        Hardcover
      </dt>
      <dd>
        With <span itemprop="numberOfPages">234</span> pages
      </dd>
    </dl>

    <p>
      Price :
      <span itemprop="priceCurrency" content="USD">$</span>
      <span itemprop="price">4.99</span>        
    </p>

  </div>

  <!-- Paperback Offer !-->
  <div itemprop="offers" itemscope itemtype="http://schema.org/Offer" id="PaperbackOffer">

    <dl itemprop="itemOffered" itemscope itemtype="http://schema.org/Book">
      <dt itemprop="bookFormat" href="http://schema.org/Paperback"> 
        Paperback
      </dt>
      <dd>
        With <span itemprop="numberOfPages">321</span> pages
      </dd>

    </dl>
    <p>   
      Price
      <span itemprop="priceCurrency" content="USD">$</span>
      <span itemprop="price">2.99</span>
    </p>

  </div>
  <a href="BookOntology.html" itemprop="url">Buy Now</a>
  <!-- Author !-->
  <div>by <span itemprop="author">J.D. Salinger</span></div>

  <!-- Edition !-->
  <div itemprop="bookEdition">2nd Edition</div>

</div>

Check out the results of the Rich Snippets Testing tool for this book. It's clear to see the offers assoicated with the different Book formats and their respective prices.

The key point to draw from here is the use of itemOffered property that defines the itemtype as Book.

What About Other Product Types?

So, lets try to see if we can get the rich snippets tool to understand size:price differences via the Product Schema using the a similar approach as the author of the Book example did.

Here's a working snippet of html (included below) that includes 3 different sizes Small Medium & Large for a T-Shirt.

<div itemscope itemtype="http://schema.org/Product" itemid="NPL127">

  <meta itemprop="url" content="ProductOntology.html?NPL127">

  <h1 itemprop="name">No place like 127.0.0.1 T-Shirt</h1>

  <p itemprop="description">
    White T-Shirt with "No place like 127.0.0.1" logo, avaliable in 
    Small, Medium &amp; Large.
  </p>

  <p>
    Product Reference : <span itemprop="productid">NPL127</span>
  </p>

  <p>
    Made in the UK By : <span itemprop="brand">Cool As *Ts!</span>
  </p>

  <img src="http://placehold.it/400x200&text=No%20place%20Like%20127.0.0.1" itemprop="image">

  <h2>
    Avaliable In the 3 Size
  </h2>

  <!-- Small Offer !-->
  <div itemprop="offers" itemscope itemtype="http://schema.org/Offer" itemid="NPL127_S_OFFER">

    <h3 itemprop="name">
      No place like 127.0.0.1 T-Shirt in Small
    </h3>

    <p>
      3 
      <span itemprop="availability" content="InStock">
        In Stock
      </span>

      Avaliable for
      <span itemprop="deliveryLeadTime" content="Next Day">
        Next Day Delivery
      </span>
    </p>

    <p>
      Price : 
      <span itemprop="priceCurrency" content="GBP">£</span>
      <span itemprop="price">10.99</span>   
      <a href="buy.html?id=NPL127-S">Buy Now</a>      
    </p>

    <div itemprop="itemOffered" itemscope itemtype="http://schema.org/Product" itemid="NPL127_S">
      <meta itemprop="url" content="ProductOntology.html?NPL127">
      <meta itemprop="productid" content="NPL127/S">
      <meta itemprop="weight" content="Small">
    </div>


  </div>

  <!-- Medium Offer !-->
  <div itemprop="offers" itemscope itemtype="http://schema.org/Offer" itemid="NPL127_M_OFFER">

    <h3 itemprop="name">
      No place like 127.0.0.1 T-Shirt in Medium
    </h3>

    <p>
      9
      <span itemprop="availability" content="InStock">
        In Stock
      </span>

      Avaliable for
      <span itemprop="deliveryLeadTime" content="Next Day">
        Next Day Delivery
      </span>
    </p>

    <p>
      Price : 
      <span itemprop="priceCurrency" content="GBP">£</span>
      <span itemprop="price">11.99</span>   
      <a href="buy.html?id=NPL127-N">Buy Now</a>      
    </p>

    <div itemprop="itemOffered" itemscope itemtype="http://schema.org/Product" itemid="NPL127_M">
      <meta itemprop="url" content="ProductOntology.html?NPL127">
      <meta itemprop="productid" content="NPL127/M">
      <meta itemprop="weight" content="Medium">
    </div>


  </div>

  <!-- Large Offer !-->
  <div itemprop="offers" itemscope itemtype="http://schema.org/Offer" itemid="NPL127_L_OFFER">

    <h3 itemprop="name">
      No place like 127.0.0.1 T-Shirt in Large
    </h3>

    <p>
      23 
      <span itemprop="availability" content="InStock">
        In Stock
      </span>

      Avaliable for
      <span itemprop="deliveryLeadTime" content="Next Day">
        Next Day Delivery
      </span>
    </p>

    <p>
      Price : 
      <span itemprop="priceCurrency" content="GBP">£</span>
      <span itemprop="price">12.99</span>   
      <a href="buy.html?id=NPL127-L">Buy Now</a>      
    </p>

    <div itemprop="itemOffered" itemscope itemtype="http://schema.org/Product" itemid="NPL127_L">
      <meta itemprop="url" content="ProductOntology.html?NPL127">
      <meta itemprop="productid" content="NPL127/L">
      <meta itemprop="weight" content="Large">
    </div>


  </div>

</div>

You might notice I've opted to use weight to hold the sizing information, there's no generic size itemprop defined; using size causes Google's Rich snippets to display an error with the data.

I know, there's a whole bunch of markup there, so to help explain here's the conceptual structure :

  • Product

    • URL
    • Name
    • Description
    • Product Id
    • Offers
    • Brand
    • Image
    • Offers

      • Offer (Small)

        • Name
        • Avaliblity
        • Delivery Time
        • Price
        • Currency
        • ItemOffered (Reference to Product again)
          • Product
            • URL (Same as top Level)
            • Product Id (Size specific Id)
            • Weight (Size information)
      • Offer (Medium)

        • Name
        • Avaliblity
        • Delivery Time
        • Price
        • Currency
        • ItemOffered (Reference to Product again)
          • Product
            • URL (Same as top Level)
            • Product Id (Size specific Id)
            • Weight (Size information)
      • Offer (Large)

        • Name
        • Avaliblity
        • Delivery Time
        • Price
        • Currency
        • ItemOffered (Reference to Product again)
          • Product
            • URL (Same as top Level)
            • Product Id (Size specific Id)
            • Weight (Size information)

The schema for offer contains an ItemOffered Property that, currently, expects a type of Product. Using this property and including the size specific information allows for semantic data such as price, currency, stock levels/avalibility and lead time for delivery against each individual size.

Some disscussion & tests need to be carried out regarding which properties are best the use.

For example I'm using :

  • Offer => name to hold the name of the product with it's size appended;
  • Maybe this should be on the Offer => itemOffered => Product => name instead.

Or if the Circual Reference back to the top level product via it's url property is the best practice for indicating where the extended product data is found for that size, suchas brand, image, description etc.

Maybe I shouldn't give the different sizes unique productid's, using a common value might be sufficient.

Also, there's very little documentation on the use of itemid on schema.org. It appears that Google Rich Snippets will just ignore the property, although no vaidation error. I had hoped this could be used to semanticaly define the relationship back to the Product that contains the extended details.

Other questions around the use of the meta for the individual size details; a prime example is the url, really it should link as per schema.org's getting started docs

Using Foolip's Live MicroData Tool You can see it's extracted the relevant details.

Show the JSON generated by the MicroData Tool

{
  "items": [
	{
	  "type": [
		"http://schema.org/Product"
	  ],
	  "id": "http://example.com/NPL127",
	  "properties": {
		"url": [
		  "ProductOntology.html?NPL127"
		],
		"name": [
		  "No place like 127.0.0.1 T-Shirt"
		],
		"description": [
		  "\n\tWhite T-Shirt with \"No place like 127.0.0.1\" logo, avaliable in \n\t Small, Medium, & Large.\n  "
		],
		"productid": [
		  "NPL127"
		],
		"brand": [
		  "Cool As *Ts!"
		],
		"image": [
		  "http://placehold.it/400x200&text=No%20place%20Like%20127.0.0.1"
		],
		"offers": [
		  {
			"type": [
			  "http://schema.org/Offer"
			],
			"id": "http://example.com/NPL127_S_OFFER",
			"properties": {
			  "name": [
				"\n\t  No place like 127.0.0.1 T-Shirt in Small\n\t"
			  ],
			  "availability": [
				"\n\t\tIn Stock\n\t  "
			  ],
			  "deliveryLeadTime": [
				"\n\t\tNext Day Delivery\n\t  "
			  ],
			  "priceCurrency": [
				"£"
			  ],
			  "price": [
				"10.99"
			  ],
			  "itemOffered": [
				{
				  "type": [
					"http://schema.org/Product"
				  ],
				  "id": "http://example.com/NPL127_S",
				  "properties": {
					"url": [
					  "ProductOntology.html?NPL127"
					],
					"productid": [
					  "NPL127/S"
					],
					"weight": [
					  "Small"
					]
				  }
				}
			  ]
			}
		  },
		  {
			"type": [
			  "http://schema.org/Offer"
			],
			"id": "http://example.com/NPL127_M_OFFER",
			"properties": {
			  "name": [
				"\n\t  No place like 127.0.0.1 T-Shirt in Medium\n\t"
			  ],
			  "availability": [
				"\n\t\tIn Stock\n\t  "
			  ],
			  "deliveryLeadTime": [
				"\n\t\tNext Day Delivery\n\t  "
			  ],
			  "priceCurrency": [
				"£"
			  ],
			  "price": [
				"11.99"
			  ],
			  "itemOffered": [
				{
				  "type": [
					"http://schema.org/Product"
				  ],
				  "id": "http://example.com/NPL127_M",
				  "properties": {
					"url": [
					  "ProductOntology.html?NPL127"
					],
					"productid": [
					  "NPL127/M"
					],
					"weight": [
					  "Medium"
					]
				  }
				}
			  ]
			}
		  },
		  {
			"type": [
			  "http://schema.org/Offer"
			],
			"id": "http://example.com/NPL127_L_OFFER",
			"properties": {
			  "name": [
				"\n\t  No place like 127.0.0.1 T-Shirt in Large\n\t"
			  ],
			  "availability": [
				"\n\t\tIn Stock\n\t  "
			  ],
			  "deliveryLeadTime": [
				"\n\t\tNext Day Delivery\n\t  "
			  ],
			  "priceCurrency": [
				"£"
			  ],
			  "price": [
				"12.99"
			  ],
			  "itemOffered": [
				{
				  "type": [
					"http://schema.org/Product"
				  ],
				  "id": "http://example.com/NPL127_L",
				  "properties": {
					"url": [
					  "ProductOntology.html?NPL127"
					],
					"productid": [
					  "NPL127/L"
					],
					"weight": [
					  "Large"
					]
				  }
				}
			  ]
			}
		  }
		]
	  }
	}
  ]
}