How to implement person and organisation schema in JSON-LD in WordPress

First, a brief history of structured data for SEO

Microformats

For a time, we had Microformats. Those times were simple, and good.

Implementing Microformats would lead to interesting things, like review ratings in Google’s search results. You could have your events listed, recipes indexed, and all sorts of contact details marked up as critical data points for a crawler to parse.

I really liked compound Microformats. They were simple to implement sets of attributes; added to the HTML containers surrounding useful data, like class="postal-code". Initially, web developers weren’t keen to add additional attributes to their markup, but over time, we’d get small wins, like having class="hreview-aggregate" become integrated with the stylesheet, tackling presentation and information in one go.

Then came RDFa

I never really bonded with RDFa. It was complicated, it was XML, and all we used it for was an extremely over engineered solution to gain rich snippets in Google’s search results. RDFa, an XML based extension to HTML5 was a much more powerful, but complexly structured solution compared to Microformats.

For most digital marketers, RDFa was too complex a thing to just work with. Front end web developers were especially reluctant to implement RDFa, although at the time, those that were made to just got on with it. The crippling blow to RDFa from the marketing community was that support from search engines was inconsistent and therefore the extra complexity was difficult to justify.

Schema.org ended all of this with microdata

Today I doubt you’ll meet any front end focused SEOs today that would recommend Microformats (unless they ask for a rel="nofollow" attribute adding to a link) or RDFa based markup. That’s because of Schema.org.

Schema.org: a common vocabulary for structured data markup on web pages

In June 2011, Google, Yahoo and Bing “got together” to announce Schema.org. Despite offering support for legacy RDFa and Microformats implementations, our community were immediately advised to implement Schema. It made sense, owing to wider support for other data types “including movies, music, organizations, TV shows, products, places and more”.

Schema is based on Microdata types and properties (for example: itemtype="https://schema.org/Movie") and extends quite well, with further extensibility and new types in Schema 2.0.

Dan wrote a very complete guide to all aspects of Schema Markup currently supported by Google. Wisely, he included the equivalent JSON-LD schemas, too.

This is already a lot of structured data, and you haven’t even got to the point of your post!

Here’s my point. Structured data is important, really important.

I think you should know a little structured data history too. I also think you should stay on top of what’s coming next, because it’s interesting and it will impact the future of search.

I think all of the future of structured data is JSON-LD, and I think that’s what we should all be implementing in our technical releases from now.

Staying on top of the topic of structured data

A really, really good way to stay on top of ideas relevant to digital marketers and SEOs in the structured data community is to follow Aaron Bradley‘s SEOSkeptic.com.

It was a post by Aaron that really helped the penny drop on the real importance of JSON-LD. Just the opening paragraph of “Zero Blue Links: Search After Traffic” is a real eye opener and one you should consume very carefully.

I reflected on the use of Schema, or more generally, structured markup (or JSON-LD for the future) is absolutely critical to apps like Google Now and its ability to pull a large majority of card results into the wrapper.

I predicted that Google’s mobile search results may soon look very much like Google Now, just in the browser, and that to maximise the opportunity a search marketer should make the best possible use of structured data and the knowledge graph, now.

I’ve since refined that view somewhat. I’m beginning to conclude that “search” for us may soon be about something more technically diverse. We’ll be staying on top of new search interfaces that require our content to be re-purposed in an alternative presentation layer to enable access.

Think about your site’s data layer and your site’s presentation layer as separate things. Think about your website as an API endpoint that can serve a JSON data payload to search engines. Think about how a search engine may choose (even, prefer for some applications) to consume your content on that API endpoint.

I can’t help but feel that eventually we’ll be doing exactly this to make sure we have visibility across a broad range of devices; organic and social channels, like answer boxes, instant articles and cards.

So to that end, I’m pretty sure (hopeful) Aaron would agree with me.

My take on using schema and why I’m writing this post

My take on using Schema Markup (Microdata) is this: I don’t want to implement it.

It messes with my presentation layer. Getting it implemented is slow, really really slow. You have to “get buy in” to win the resource via the front end development team who don’t want to mess up their HTML to accommodate the SEO team and this crappy code. Once you have it in, *then* you have to monitor it, checking every time a release is put live. Hoping that it hasn’t broken something. Which it often has. Which means more change requests, more communicating and more hassle.

All is not lost, however. JSON-LD’s keys and values match the attribute names set out in Schema.org – so you’re actually changing very little. Just compare the Microdata markup in Organization to its JSON-LD version in Dan’s Schema guide.

JSON-LD is where it’s at

JSON-LD is where it’s at, friends. Here’s why I think this:

I’ve had a few objections to this, namely; it feels “weird” to duplicate data on the webpage and support is limited (breadcrumbs won’t work).

I’ve reflected on those points for hours and I think the advantages in faster release outweigh any concerns. Sure – duplication. Specifically, the ArticleBody value for in-depth articles, for example. I still don’t think that’s a problem.

Implementing JSON-LD in WordPress

So, on that note let’s get to the point of the post. I had a lot of fun implementing JSON-LD across our homepage, author pages and posts.

Take a look in the head of my author page.


<script type="application/ld+json">
// <![CDATA[
{ 
  "@context":"http:\/\/schema.org\/", 
  "name":"Richard Baxter", 
  "@type":"Person",
  "email":"richard@builtvisible.com",
  "sameAs":[ 
    "https:\/\/twitter.com\/richardbaxter",
    "http:\/\/builtvisible.com\/",
    "https:\/\/www.facebook.com\/richardbaxterseo",
    "https:\/\/plus.google.com\/+RichardBaxterSEO\/",
    "http:\/\/uk.linkedin.com\/in\/richardbaxterseo",
    "http:\/\/www.slideshare.net\/richardbaxterseo" 
  ]
}
//]]>
</script>

Which validates in the rich snippets testing tool as follows:

person-json-ld

Our homepage uses Organization as follows:

<script type="application/ld+json">// <![CDATA[
{
  "@context": "http:\/\/schema.org\/",
  "name": "Builtvisible",
  "@type": "Organization",
  "logo": "https:\/\/builtvisible.com\/wp-content\/uploads\/2018\/09\/builtvisible-logo.png",
  "url": "http:\/\/builtvisible.com\/",
  "sameAs": [
    "https:\/\/twitter.com\/builtvisible", 
    "https:\/\/www.facebook.com\/builtvisible",
    "https:\/\/www.linkedin.com\/company\/builtvisible",
    "https:\/\/plus.google.com\/+SEOgadget\/posts"
  ],
  "contactPoint": [{
    "@type": "ContactPoint",
    "telephone": "+44 20 7148 0453",
    "email": "hello@builtvisible.com",
    "contactType": "sales"
  }]
}
// ]]>
</script>

Again, validating nicely:

organization-json-ld

And finally, an article:


<script type="application/ld+json">
// <![CDATA[ 
{ 
  "@context":"http:\/\/schema.org\/", 
  "name":"Implementing JSON-LD in WordPress", 
  "@type":"Article",
  "author":"Richard Baxter", 
  "ArticleSection":"Technical", 
  "datePublished":"2015-05-18 18:37:17", 
  "Publisher":"Builtvisible", 
  "url":"http:\/\/builtvisible.com\/implementing-json-ld-wordpress/" 
} 
// ]]>
</script>

Which validates nicely, here:

article-json-ld

Implementation

Save the following code as json-ld.php in your theme directory:

<?php 
// JSON-LD for WordPress Home Articles and Author Pages 
 // Stuff for any page 
 function get_post_data() { 
    global $post; 
    return $post; 
  } 
  // This has all the data of the post/page etc 
  $payload["@context"] = "https://schema.org/"; 
  // Stuff for any page, if it exists 
  $post_data = get_post_data(); 
  // Stuff for specific pages 
  $category = get_the_category(); 
  // This gets the data for the user who wrote that particular item 
  if (is_single()) { 
    $author_data = get_userdata($post_data->post_author); 
    $post_url = get_permalink(); 
    $post_thumb = wp_get_attachment_url(get_post_thumbnail_id($post->ID)); 
    $payload["@type"] = "Article"; 
    $payload["url"] = $post_url; 
    $payload["author"] = array( "@type" => "Person", "name" => 
    $author_data->display_name, ); 
    $payload["headline"] = $post_data->post_title; 
    $payload["datePublished"] = $post_data->post_date; 
    $payload["image"] = $post_thumb; 
    $payload["ArticleSection"] = $category[0]->cat_name; 
    $payload["Publisher"] = "Builtvisible"; 
  } 

  // We do all this separately so we keep the right things for organization together 
  if (is_front_page()) { 
    $payload["@type"] = "Organization"; 
    $payload["name"] = "Builtvisible"; 
    $payload["logo"] = "https:\/\/builtvisible.com\/wp-content\/uploads\/2018\/09\/builtvisible-logo.png"; 
    $payload["url"] = "http:\/\/builtvisible.com\/"; 
    $payload["sameAs"] = [
      "https:\/\/twitter.com\/builtvisible", 
      "https:\/\/www.facebook.com\/builtvisible", 
      "https:\/\/www.linkedin.com\/company\/builtvisible", 
      "https:\/\/plus.google.com\/+SEOgadget\/"
    ]; 
    $payload["contactPoint"] = [
      ["@type" => "ContactPoint", 
        "telephone" => "+44 20 7148 0453",
        "email" => "hello@builtvisible.com",
        "contactType" => "sales"
      ]
    ]; 
  }  
  // This gets the data for the user who wrote that particular item 
  if (is_author()) { 
  // Some of you may not have all of these data points in your user profiles - delete as appropriate 
  // fetch twitter from author meta and concatenate with full twitter URL 
    $author_data = get_userdata($post_data->post_author);
    $twitter_url = " https:\/\/twitter.com/"; 
    $twitterHandle = get_the_author_meta('twitter'); 
    $twitterHandleURL = $twitter_url . $twitterHandle; 
    $websiteHandle = get_the_author_meta('url'); 
    $facebookHandle = get_the_author_meta('facebook'); 
    $gplusHandle = get_the_author_meta('googleplus'); 
    $linkedinHandle = get_the_author_meta('linkedin'); 
    $slideshareHandle = get_the_author_meta('slideshare'); 
    $payload["@type"] = "Person"; 
    $payload["name"] = $author_data->display_name; 
    $payload["email"] = $author_data->user_email; 
    $payload["sameAs"] = array($twitterHandleURL, $websiteHandle, $facebookHandle, $gplusHandle, $linkedinHandle, $slideshareHandle); 
  } 
?>

Obviously you’ll have to edit our details out and yours in.

Also the usual disclaimers apply, use our work, but we can’t be held responsible for anything that breaks. Do check what data you have in your author_meta – we’ve added some extra user profile fields.

Then, add the following two lines of code in header.php as follows:


<?php include('json-ld.php'); ?>
<script type="application/ld+json">
// <![CDATA[
<?php echo json_encode($payload); ?>
// ]]
</script> 

So wonderful. My data is kept well away from my presentation layer. I’m a very happy man.


With all the new search result formats and devices, “search” for us may soon be about staying on top of new interfaces that require our content to be re-purposed in an alternative presentation layer. Think about that: Apple Watch doesn’t have a browser.

Our own websites may become end points that need an accessible data payload to be useful to search engines. I can’t help but feel that eventually we’ll be supplying our entire site content as JSON to make sure we still have access to a broad range of devices and organic and social channels, like answer boxes, instant articles and cards.

At Builtvisible, we’ve got a sense that the JSON-LD spec might be extended to include popular actions outside of Gmail and that publishers like ourselves will be expected to make the consumption of our content entirely possible without ever visiting the site. With actions like “buy” coming to the paid shopping model, it’s only a matter of time before the same options are presented to organic search users, and HTML begins to become an option, not a prerequisite to performing well in search.

Comments are closed.

  • Awesome share. Was always curious about JSON-LD markup, looking forward to seeing what else we can do with JSON-LD. Just implemented it on our site. Thanks for sharing.

  • Awesome post guys. Have you tested/played around with implementing JSON within Tag Manager to call stuff such as Product information etc/

  • George + Kevin, there’s a comment in your json-ld.php file:

    // JSON-LD for WordPress Home, Articles and Author Pages. Written by Pete Wailes and Richard Baxter.

    Remove this as it was accidentally made outside the PHP function. I’ve updated the code here so no problems moving forward :D

  • Thanks for this fine look at the future Richard (because you’re absolutely right, JSON-LD is the future – at least syntactically – of structured data markup), and for your kind words concerning my work and the Semantic Search Marketing Community.

    A quick note regarding your sample Article markup. Sometime after Google rolled out its new suite of structured data pages on Google Developers, they included (without any fanfare) an article titled “Enabling Rich Snippets for Articles“. In all but name these “rich snippets” are the evolution of Google’s “in-depth articles” feature: in-depth articles continue to populate the SERPs, but without the “in-depth article” label.

    The required and recommended properties for article rich snippets are at variance with those used in your sample article.

    Most importantly, the required headline and image fields are missing.

    headline is a bit of an odd duck. You use the fundamental and perfectly acceptable name property to declare the article’s title. Why Google should require headline either in addition to or instead of name baffled me when it came to in-depth articles, and the requirement baffles me here. Be that as it may, apparently its use is required to generate an article rich snippet.

    image is also required for article rich snippet generation. At least this makes more sense than requiring headline as there’s no other equivalent property for declaring an image, and a graphic is a feature of article rich snippets.

  • Aaron, outstanding comments – thank you for sharing. I’ll update my code and update you soon after!

  • Hi Richard!

    “My take on using Schema Markup (Microdata) is this: I don’t want to implement it.”
    When I got to this part of the article, I was like AMEN!

    I actually hated it, so I was all “hallelujah” when JSON became possible.

    I actually had an internal plugin for wordpress made.

    I will be sharing it this Friday through a blog post based on a presentation I did for a local conference.

    The plugin basically handles the basic ones, social profiles, logo, most corporate contacts, sitelinks Search Box etc
    Haven’t gotten to the article part yet though. Still more to come :)

    Wondering if you’d be interested in trying it out on a site that you aren’t actively using and maybe give me feedback to further improve it? It’s still not in the WordPress.org repository though so versions will be sent out through email for the mean time.

    The current page for it is over here. If you’d like, I can send it to you directly so you dont have to sign up.

    The only time I’ve run into problems with it is when a certain theme won’t work with JSON.

    Thanks for this post, I will be inserting it to my blog post for this friday.

    PS. I still remember reading the Zero Blue links article a while back. It also opened my eyes.
    Just joined Aaron’s G+ community and it looks cool!

  • Loved this article as now I can visualise the history of marking up a site with all the different codes out there. I’ve had a feeling JSON-LD was being preferred by Google when I’ve reviewed their official intro to structured data. For example this quote on this page https://developers.google.com/structured-data/schema-org

    So far, JSON-LD is supported for all Knowledge Graph features, sitelink search boxes, and Event Rich Snippets; Google recommends the use of JSON-LD for those features. For the remaining Rich Snippets types and breadcrumbs, Google recommends the use of microdata or RDFa

    To me this is Google saying use JSON-LD for Google Knowledge Graph as a primary data input and to add extra info use schema microdata where appropriate.

  • Thanks for the article! I use WordPress to build all my websites so this is dead useful to me. Much appreciated!

  • I would just like to say: Thank You !

    JSON-LD is a beautiful standard. I am working on making the uniprot rdf data available in JSON-LD, and it is starting to look like something that a JS developer would actually use. Its still RDF, but at the same time its practical and usable JSON.

    At the same time SPARQL construct returning results in JSON-LD is a really nice way to build quick data applications with just a quad store and some JS files!

    Thank you!

  • Hi,
    i find this post it is very helpful for me.Thanks for sharing. You did a good job.
    Keep up the good work. Have a great week ahead!

  • Would it not – if I may say so – be more of direct use if this approach were to be taken via the functions.php angle?

    Or am I missing something?

  • Hi JC,

    Yes if you wanted to permanently bake this into a theme, you could go ahead and do this inside functions.php. But for the purpose of this post, it was easier to share this way.

  • Your post has somehow kept mulling through my head on its own accord, so I’ve been experimenting a bit. Granted, with zero knowledge :P But I keep wondering about how WordPress as a platform is going to deal with the evolution of search and structured data.

    WordPress already has quite a bit of trouble with the Above The Fold drama, something which increasingly is seperating the marketplace theme agencies and what some appear to call the professional agencies – but that is a debate in its own right – either way I’m getting the idea that particularly Google wants the Head for itself. So to speak.

    Maybe I’m wrong, but I’d still favour the use of functions.php. Perhaps you could combine both approaches, a payload via seperate json-ld.php containing classes and structure, child theme functions.php to keep the parent theme untouched, and maybe down the road a simple options page.

    That said, it’s messy (I’ve got no knowledge of php or json-ld), functions.php approach does appear to work. I haven’t found much of anything elsewhere on the topic, hence why I’m back here.

    add_action(‘wp_head’,’insert_json_ld’);
    function insert_json_ld (){
    if (is_page() || is_single()) {
    if (have_posts()) : while (have_posts()) : the_post();
    $context = ‘https://schema.org’;
    $type = ‘Article’;
    $name = get_the_title();
    $authorType = ‘Person’;
    $authorName = get_the_author();
    $dataPublished = get_the_date(‘Y-n-j’);
    $image = the_post_thumbnail();
    $articleSection = get_the_excerpt();
    $articleBody = get_the_content();
    $url = get_permalink();
    $publisherType = ‘Organization’;
    $publisherName = get_bloginfo(‘name’);

    $json= “‘@context’ : ‘{$context}’,
    ‘@type’ : ‘{$type}’,
    ‘name’ : ‘{$name}’,
    ‘author’ : {
    ‘@type’ : ‘{$authorType}’,
    ‘name’ : ‘{$authorName}’
    },
    ‘datePublished’ : ‘{$dataPublished}’,
    ‘image’ : ‘{$image}’,
    ‘articleSection’ : ‘{$articleSection}’,
    ‘articleBody’ : ‘{$articleBody}’,
    ‘url’ : ‘{$url}’,
    ‘publisher’ : {
    ‘@type’ : ‘{$publisherType}’,
    ‘name’ : ‘{$publisherName}’
    }”;
    echo ‘{‘.$json.’}’;
    endwhile; endif;
    rewind_posts();
    }
    }

  • WordPress is the most popular CMS today(if i’m not mistaken) and I regularly used this in building my website. It’s nice to know that it was also implemented in wordpress through plugins. I’m still studying the syntax and I hope this could not ruin my design in wordpress.

  • That’s just about the best explanation I’ve found so far. Thanks man.

  • Hi Team

    Ive just applied this to my website. Thank-you very much for this information.

    Im just curious as to how quickly this code is picked up by https://developers.google.com/structured-data/testing-tool? Is it instantly or does it take a day or two?

    Cheers
    Paul

  • Hi again

    I tried to do this in a genesis child theme and its not working. Any suggestions please on how to implement this in a WordPress – Genesis Child Theme?

    Thanks in advance
    Paul

  • Hi,

    I first want to say thanks for providing the exact implementation that I was imagining in my newbie coding mind.

    I have implemented this on one of the sites that I manage and have had good success in the Structured Data Testing Tool.

    I was wondering if you have an implementation for wordpress is_page() and am I on the right track in thinking this would be beneficial.

    Thanks in advance!
    Andrew

  • Thank so much for your informative post. I am new to json ld and I am bit confused by the ‘implementation’ section where you create json-ld.php section. As your article is the only article I have ever seen mention this aspect of getting jason ld to work. Mostly every article I have found on the web just mentions applying the json ld markup to the webpage.

    Is this something specifically for wordpress to work?

    How does this step apply to html sites, etc..

    Thanks for your time!

  • Hello
    very knowledgeable article.
    i have a query about post author :
    for example if i have added a post author using script mentioned above then is it possible that fornt page , home page , categories archive and others auto display the author in that too. I am facing this issue on my website, default author is shown at all of fields. Is there is any solution.

  • Any update to this article?
    Implemented and getting a variety of Article type errors (image, logo, dateModified, and mainEntityOfPage).

    Thanks,

  • @Sam Google seems to have changed their requirements for certain types such as Article. Simply fill in those fields with the appropriate data and you should be good to go.

  • Thanks a lot for this great article. When I test my site with Google, it’s giving errors only for image and logo. According to their article, image should be like this:

    “image”: {
    “@type”: “ImageObject”,
    “url”: “https://google.com/thumbnail1.jpg”,
    “height”: 800,
    “width”: 800
    },

    And logo should be under Publisher section like this:

    “publisher”: {
    “@type”: “Organization”,
    “name”: “Google”,
    “logo”: {
    “@type”: “ImageObject”,
    “url”: “https://google.com/logo.jpg”,
    “width”: 600,
    “height”: 60
    }

    I’m not a coder, so I wasn’t able to make the necessary array modifications. Is it possible for someone to do please?

    Thanks

  • For the images, you could try this:

    list($width, $height) = getimagesize( $post_thumb );
    $payload[“image”] = array(
    “@type” => “ImageObject”,
    “url” => $post_thumb,
    “height” => $height,
    “width” => $width
    );

  • Thanks @Mindert Aardema for the images code. I’m getting the same errors as Gezgin.

    Where do we put this code? When I place this code into the json-ld.php document I get a 200 error while testing my schema. I’ve commented it out for now… but if you could elaborate a little bit for us non-coders that would be great! Thanks so much.

  • @gezgin

    $payload[“Publisher”] = array(
    “@type” => “Organization”,
    “name” => “Mihai Slujitoru”,
    “logo” => array(
    “@type” => “ImageObject”,
    “url” => “add logo”,
    “width” => “add width”,
    “height” => “add height”
    )
    );


Join the Inner Circle

Industry leading insights direct to your inbox every month.