schema.org restaurant menu - describing an individual dish - schema.org
Is there a way to describe the properties of each dish in a restaurantmenu?
These fields in particular: name, description, price
Or does one have to pray that google interprets the data correct when you have indicated the url of the menu in the restaurant properties?
The vocabulary Schema.org does not (yet) offer a type for representing restaurant menus or single menu items.
Their menu property expects text or URL as value. If there should ever be a type for menus, it would become another expected value.
The issue Extension Proposal: FoodProduct and Restaurant Menu with FoodProducts. asked for this. It got closed, referencing the broader issue Create a new Food type (help further with foodWarning and recipeIngredient). It’s still under discussion.
If you need something now, you could use the hasOfferCatalog property that references an OfferCatalog type (representing the menu, or grouped parts of the menu) and the Offer type (representing a single menu item, with a price).
For example:
<div vocab="http://schema.org/" typeof="Restaurant">
<section property="hasOfferCatalog" typeof="OfferCatalog">
<h1 property="name">Menu</h1>
<ul>
<li property="itemListElement" typeof="Offer">
<b property="name">Bread</b> –
<span property="price">1.50</span>
<meta property="priceCurrency" content="EUR" />
</li>
<li property="itemListElement" typeof="Offer">
<b property="name">Water</b> –
<span property="price">1.00</span>
<meta property="priceCurrency" content="EUR" />
</li>
</ul>
</section>
</div>
If you want to use the menu property, too, you could add a div that encloses the OfferCatalog. The value for menu would then be the textual content.
<div vocab="http://schema.org/" typeof="Restaurant">
<div property="menu">
<section property="hasOfferCatalog" typeof="OfferCatalog">
<!-- … -->
</section>
</div>
</div>
There's a couple of options, depending on (somewhat) personal preference:
Leave it and see if Google understands
menu defines the value as either Text or URL, so providing either of these should be sufficient. Doing anything else may confuse Google or another parser as the specification is not followed.
Here's an article which shows a URL providing a menu to Google without any schema, so the system does work.
Provide a schema at the destination of the URL
ItemList is the most generic kind of list and allows you to add itemListElements to the list. This doesn't specify that the list is a menu, but the menu property that the URL has been provided for should be sufficient.
Alternatively, Offer allows you to specify name, description and price which satisfies your requirements. Product doesn't allow you to specify price, so Offer is preferable here. As itemListElement can be a Thing, you can combine Offers in an ItemList.
Schema.org has release a new Restaurant Menu Markup !
That is really good news! So, here I am to share something I've been waiting for a looong time myself.
If you've been dreaming of a proper method which allows the markup of multiple Restaurant Menus with along Menu Sections or even Offers differentiation, you'll be amazed with the refreshed schema.org/restaurant structuring markup possibilities.
The first thing you'll notice when you visit the schema.org/Restaurant page is that the menu property has been replaced with the hasMenu property, which now supersedes the still valid menu one.
So, straight to the point:
How to mark up restaurant menus with Schema.org
On every page of the website, it is recommended to point search engines in the right direction as to where the menu can be found. Here's a nice article that covers much of what I've put together here.
Google’s guidelines states that we should only mark up content that’s visible on the page, we can’t exactly include the entire menu in our home page markup unless the entire menu is published there. Instead, we’ll simply use the hasMenu property on the home page to point to the menu page, like this:
<script type="application/ld+json">
{
"#context": "http://schema.org",
"#type": "WebSite",
"name": "Your Restaurant's Name",
"url": "http://your-restaurant.com/",
"publisher": {
"#type": "Restaurant",
"name": "Your Restaurant's Name",
"hasMenu": "http://your-restaurant.com/menu/",
"logo": "http://.....
In fact, on any page of your website that includes some schema markup, you could use the hasMenu property to point to the URL of the menu page.
When you have more than one menu:
“hasMenu”: [
{
“#type”: “Menu”,
“name”: “Breakfast”,
“url”: “http://your-restaurant.com/breakfast-menu/”
},
{
“#type”: “Menu”,
“name”: “Lunch”,
“url”: “http://your-restaurant.com/lunch-menu/”
},
{
“#type”: “Menu”,
“name”: “Dinner”,
“url”: “http://your-restaurant.com/dinner-menu/”
}
],
...
Starting the menu page markup:
Switching our attention to the actual menu page, let’s say that the menu was only served between 5:00pm and 11:00pm. So, on the menu page, our markup would begin like this:
<script type=”application/ld+json”>
{
“#context”: “http://schema.org”,
“#type”: “Menu”,
“name”: “Our Menu”,
“mainEntityOfPage”: “http://your-restaurant.com/menu/”,
“inLanguage”: “English”,
“offers”: {
“#type”: “Offer”,
“availabilityStarts”: “T17:00”,
“availabilityEnds”: “T23:00”
},
Marking up sections of the menu:
Next, we can begin marking up the various sections of the menu and the individual menu items. First, we’ll start with the appetizers. For the first appetizer, we’ll include in our markup the name, a brief description, and the price, which should be the minimum for any menu item. In our second appetizer markup example, we’ll also include an image, the nutritional information, and the fact that it’s gluten-free:
“hasMenuSection”: [
{
“#type”: “MenuSection”,
“name”: “Appetizers”,
“hasMenuItem”: [
{
“#type”: “MenuItem”,
“name”: “Fried Eggplant”,
“description”: “Served with Italian red gravy.”,
“offers”: {
“#type”: “Offer”,
“price”: “7.95”,
“priceCurrency”: “USD”
}
},
{
“#type”: “MenuItem”,
“name”: “Fried Calamari”,
“description”: “Served with Italian red gravy or honey mustard.”,
“image”: “http://your-restaurant.com/images/fried-calamari.jpg”,
“suitableForDiet”: “http://schema.org/GlutenFreeDiet”,
“nutrition”: {
“#type”: “NutritionInformation”,
“calories”: “573 calories”,
“fatContent”: “25 grams”,
“carbohydrateContent”: “26 grams”,
“proteinContent”: “61 grams”
},
“offers”: {
“#type”: “Offer”,
“price”: “7.95”,
“priceCurrency”: “USD”
}
}
]
},
Marking up the menu items:
Let’s say we’ve marked up all of the appetizers and we’re ready to begin marking up the next menu section, which in our case are the soups. Sometimes menu items such as soups are available in two or more sizes. We can mark up the available options by using a separate offer markup for each along with the eligibleQuantity property, like this:
{
“#type”: “MenuSection”,
“name”: “Soups”,
“hasMenuItem”: [
{
“#type”: “MenuItem”,
“name”: “Lobster Bisque”,
“offers”: [
{
“#type”: “Offer”,
“price”: “6.75”,
“priceCurrency”: “USD”,
“eligibleQuantity”: {
“#type”: “QuantitativeValue”,
“name”: “Cup”
}
},
{
“#type”: “Offer”,
“price”: “9.95”,
“priceCurrency”: “USD”,
“eligibleQuantity” : {
“#type”: “QuantitativeValue”,
“name”: “Bowl”
}
}
]
},
{
“#type”: “MenuItem”,
“name”: “Creole Seafood Gumbo”,
“offers”: [
{
“#type”: “Offer”,
“price”: “6.75”,
“priceCurrency”: “USD”,
“eligibleQuantity”: {
“#type”: “QuantitativeValue”,
“name”: “Cup”
}
},
{
“#type”: “Offer”,
“name”: “Bowl”,
“price”: “9.95”,
“priceCurrency”: “USD”,
“eligibleQuantity” : {
“#type”: “QuantitativeValue”,
“name”: “Bowl”
}
}
]
}
]
},
Putting it all together:
After we’ve marked up all of the soup items, we can move on to marking up the other menu sections and items using the same format. And that’s it. Putting it all together, our JSON-LD menu markup would look something like this:
<script type=”application/ld+json”>
{
“#context”:”http://schema.org”,
“#type”:”Menu”,
“name”: “Our Menu”,
“url”: “http://your-restaurant.com/menu/”,
“mainEntityOfPage”: “http://your-restaurant.com/menu/”,
“inLanguage”:”English”,
“offers”: {
“#type”: “Offer”,
“availabilityStarts”: “T17:00”,
“availabilityEnds”: “T23:00”
},
“hasMenuSection”: [
{
“#type”: “MenuSection”,
“name”: “Appetizers”,
“hasMenuItem”: [
{
“#type”: “MenuItem”,
“name”: “Fried Eggplant”,
“description”: “Served with Italian red gravy.”,
“offers”: {
“#type”: “Offer”,
“price”: “7.95”,
“priceCurrency”: “USD”
}
},
{
“#type”: “MenuItem”,
“name”: “Fried Calamari”,
“description”: “Served with Italian red gravy or honey mustard.”,
“image”: “http://your-restaurant.com/images/fried-calamari.jpg”,
“suitableForDiet”: “http://schema.org/GlutenFreeDiet”,
“nutrition”: {
“#type”: “NutritionInformation”,
“calories”: “573 calories”,
“fatContent”: “25 grams”,
“carbohydrateContent”: “26 grams”,
“proteinContent”: “61 grams”
},
“offers”: {
“#type”: “Offer”,
“price”: “7.95”,
“priceCurrency”: “USD”
}
}
]
},
{
“#type”: “MenuSection”,
“name”: “Soups”,
“hasMenuItem”: [
{
“#type”: “MenuItem”,
“name”: “Lobster Bisque”,
“offers”: [
{
“#type”: “Offer”,
“price”: “6.75”,
“priceCurrency”: “USD”,
“eligibleQuantity”: {
“#type”: “QuantitativeValue”,
“name”: “Cup”
}
},
{
“#type”: “Offer”,
“price”: “9.95”,
“priceCurrency”: “USD”,
“eligibleQuantity” : {
“#type”: “QuantitativeValue”,
“name”: “Bowl”
}
}
]
},
{
“#type”: “MenuItem”,
“name”: “Creole Seafood Gumbo”,
“offers”: [
{
“#type”: “Offer”,
“price”: “6.75”,
“priceCurrency”: “USD”,
“eligibleQuantity”: {
“#type”: “QuantitativeValue”,
“name”: “Cup”
}
},
{
“#type”: “Offer”,
“name”: “Bowl”,
“price”: “9.95”,
“priceCurrency”: “USD”,
“eligibleQuantity” : {
“#type”: “QuantitativeValue”,
“name”: “Bowl”
}
}
]
}
]
},
{
“#type”: “MenuSection”,
“name”: “Pastas”,
“description”: “Entrées served with dinner salad or a cup of soup of the day.”,
“hasMenuItem”: [
{
“#type”: “MenuItem”,
“name”: “Veal Parmigiana”,
“description”: “Tender cuts of paneed veal crowned with golden fried eggplant, Italian red gravy, mozzarella, and parmesan; served with spaghetti.”,
“offers”: {
“#type”: “Offer”,
“price”: “17.95”,
“priceCurrency”: “USD”
}
},
{
“#type”: “MenuItem”,
“name”: “Eggplant Parmigiana”,
“description”: “Pan fried eggplant layered and topped with Italian red gravy, mozzarella, and parmesan baked until bubbly; served with spaghetti.”,
“offers”: {
“#type”: “Offer”,
“price”: “14.95”,
“priceCurrency”: “USD”
}
}
]
}
]
}
</script>
The future of the Restaurant Menu markup
I realize that not every restaurant menu scenario is perfectly covered with these new menu properties and types, but hopefully you now at least have plenty to get started with. Keep in mind, too, that the Schema.org vocabulary continues to evolve and so will this particular area of schema. You're welcome to participate in the evolution of Schema.org through the discussions on Github. If you think that you have a great suggestion, please feel free to join the conversation.
Related
Why does Google says that an item needs an url property in schema.org?
When I put an example from the ItemList page in the Structured Data Testing Tool by Google, it says that an item needs an url property. The example: <script type="application/ld+json"> { "#context": "http://schema.org", "#type": "ItemList", "name": "Top 5 covers of Bob Dylan Songs", "itemListOrder": "http://schema.org/ItemListOrderAscending", "numberOfItems": 5, "itemListElement": [ { "#type": "ListItem", "position": 5, "item": { "#type": "MusicRecording", "name": "If Not For You", "byArtist": { "#type": "MusicGroup", "name": "George Harrison" } } } ] } </script> I simplified it to have a minimal example, but the error is the same with the exact example from the page. I tested this code and there is no error here: { "#type": "ListItem", "position": 5, "item": { "#type": "MusicRecording", "name": "If Not For You", "byArtist": { "#type": "MusicGroup", "name": "George Harrison" } } } Can anybody explain this to me?
A ItemList in general is a list collection of sites like a breadcrumb, overview of bandsites, etc. If you only want a list without links to deatils sites you can use only ListItem with Items. This is the different of your samples ItemList and ListItem.
Switch case is not working in Reactjs
I have a json and I'm trying to display a form using the json data. I tried to display the indexes using the Switch case, so based on the html control type the index will be displayed. Below is my code var React = require('react'); var ReactDOM = require('react-dom'); var DATA = { "indexList": [{ "Label": "Name", "Type": "text", "Regex": "", "Default_Val": "", "Values": { "Key": "", "Value": "" }, "Validtion Msg": "", "Script": "", "Mandatory": "required", "maxLength":"16", "minLength":"7", "format":"Alphanumeric", "cssClassName": "form-control", "Placeholder": "" }, { "Label": "Select Language", "Type": "dropdown", "Regex": "", "Default_Val": "English", "Values": [{ "Key": "option1", "Value": "English" },{ "Key": "option2", "Value": "Spanish" }], "Validtion Msg": "", "Script": "", "Mandatory": "Y", "maxLength":"", "minLength":"", "format":"", "cssClassName": "form-control", "Placeholder": "" }, { "Label": "Type", "Field_Type": "radio", "Regex": "", "Default_Val": "", "Values": [{ "Key": "option1", "Value": "Form1" }, { "Key": "option2", "Value": "Form2" }, { "Key": "option3", "Value": "Form3" },{ "Key": "option4", "Value": "Form4" },{ "Key": "option5", "Value": "Form5" }], "Validtion Msg": "", "Script": "", "Mandatory": "Y", "maxLength":"", "minLength":"", "format":"", "cssClassName": "form-control", "Placeholder": "" } ] }; var Menu = React.createClass({ renderForm: function () { var data = DATA.indexList; console.log(data); return data.map(group =>{ return <div> <label for={group.Label}>{group.Label}</label> <div> switch(group.Type) { case 'text': return <input className={group.cssClassName} id={group.Label} placeholder={group.Placeholder} {group.Mandatory}/> case 'dropdown': return <select className={group.cssClassName}> <option value="">{group.Placeholder}</option> <option for="let values of group.Values.value">{values}</option> </select> case 'radio': return <div className={group.Type}> <div for="let value of group.Values"> <label><input name="radios"/>{value}</label> </div> </div> case 'chekbox' return <div className={group.Type}> <div for="let value of group.Values"> <label><input name="checkbox"/>{value}</label> </div> </div> } </div> </div> }); }, render: function() { return ( <div className="container"> <br/> <div className="panel panel-primary"> <div className="panel-heading">Form</div> <div className="panel-body"> <form> <div className="form-group"> <div className="col-xs-5"> {this.renderForm()} <button type="button" className="btn btn-primary">Submit</button> </div> </div> </form> </div> </div> </div> )} }); module.exports = Menu With the above code Im getting an error "Unexpexcted token" and the error is pointing towards the "case". Can anyone help to resolve the issue, Im new to react and Im not able to resolve this issue. Any syntax error in the code?
Because you forgot to put {}, use this: <div> { } To use any javascript code inside HTML element we need to use {}. Note: We can't directly use if-else/switch statement inside JSX, use either ternary operator or call a function from JSX and use if-else/switch inside that. Reference: http://reactjs.cn/react/tips/if-else-in-JSX.html Check the working example: var DATA = { "indexList": [{ "Label": "Name", "Type": "text", "Regex": "", "Default_Val": "", "Values": { "Key": "", "Value": "" }, "Validtion Msg": "", "Script": "", "Mandatory": "Y", "maxLength":"16", "minLength":"7", "format":"Alphanumeric", "cssClassName": "form-control", "Placeholder": "" }, { "Label": "Select Language", "Type": "dropdown", "Regex": "", "Default_Val": "English", "Values": [{ "Key": "option1", "Value": "English" },{ "Key": "option2", "Value": "Spanish" }], "Validtion Msg": "", "Script": "", "Mandatory": "Y", "maxLength":"", "minLength":"", "format":"", "cssClassName": "form-control", "Placeholder": "" }, { "Label": "Type", "Type": "radio", "Regex": "", "Default_Val": "", "Values": [{ "Key": "option1", "Value": "Form1" }, { "Key": "option2", "Value": "Form2" }, { "Key": "option3", "Value": "Form3" },{ "Key": "option4", "Value": "Form4" },{ "Key": "option5", "Value": "Form5" }], "Validtion Msg": "", "Script": "", "Mandatory": "Y", "maxLength":"", "minLength":"", "format":"", "cssClassName": "form-control", "Placeholder": "" } ] }; var Menu = React.createClass({ _renderElement: function(group){ switch(group.Type){ case 'text': return <input className={group.cssClassName} id={group.Label} placeholder={group.Placeholder} required={group.Mandatory == 'Y'? true: false}/> case 'dropdown': return <select className={group.cssClassName}> <option value="">{group.Placeholder}</option> {group.Values.map(el => <option key={el.Key} for="let values of group.Values.value">{el.Value}</option>)} </select> case 'radio': return <div className={group.Type}> <div for="let value of group.Values"> {group.Values.map(el=> <label key={el.Value}><input name="radios"/>{el.Value}</label>)} </div> </div> case 'checkbox': return <div className={group.Type}> <div for="let value of group.Values"> <label><input name="checkbox"/>{value}</label> </div> </div> } }, renderForm: function () { var data = DATA.indexList; return data.map(group =>{ return <div> <label for={group.Label}>{group.Label}</label> <div> { this._renderElement(group) } </div> </div> }); }, render: function() { return ( <div className="container"> <br/> <div className="panel panel-primary"> <div className="panel-heading">Form</div> <div className="panel-body"> <form> <div className="form-group"> <div className="col-xs-5"> {this.renderForm()} <button type="button" className="btn btn-primary">Submit</button> </div> </div> </form> </div> </div> </div> )} }); ReactDOM.render(<Menu/>, document.getElementById('app')) <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id='app'/>
swithc-case should be within braces renderForm: function() { var data = DATA.indexList; console.log(data); return data.map(group => { return <div > < label for = { group.Label } > { group.Label } < /label> < div >{ switch (group.Type) { case 'text': return <input className = { group.cssClassName } id = { group.Label } placeholder = { group.Placeholder } /> case 'dropdown': return; }} < /div> < /div> }); },
Country dimension in Analytics Reporting API v4
I want to get a report from Google Analytics v4 for pageviews by country. Since my website is targeting a specific region, I'm filtering by countries I'm most interested in using ga:countryIsoCode dimension so I don't have to type complete country name in the filter. Now I'd be also interested in getting pageviews for all other countries combined into 'other' entry. What would be the easiest way to achieve that? I've tried dynamic segments so far adding ga:segment to dimensions and a NOT filter in the segment with the same rule I used for filtering counties (except ga:countryIsoCode isn't allowed in segments, so I used ga:country). This produced 3 rows of data with each of three countries appearing along with 'other' every time. I'd appreciate an advice on this as it seems like a common use case to me. Thanks in advance!
I was able to achieve that by using segments. This approach doesn't allow to batch requests though unless all of them have the same segment definition. Also, there're limitations on the number of segments to include in the report request. Very surprised this functionality is so hard to achieve. For interested, there's an example request: { "reportRequests": [{ "viewId": "ga:XXXXXXXXX", "dateRanges": [{ "startDate": "YYYY-MM-DD", "endDate": "YYYY-MM-DD" }], "metrics": [{ "expression": "ga:pageviews" }, { "expression": "ga:sessions" }], "dimensions": [{ "name": "ga:segment" }], "segments": [{ "dynamicSegment": { "name": "Other", "sessionSegment": { "segmentFilters": [{ "not": "True", "simpleSegment": { "orFiltersForSegment": [{ "segmentFilterClauses": [{ "dimensionFilter": { "dimensionName": "ga:country", "operator": "IN_LIST", "expressions": ["United States", "United Kingdom"] } }] }] } }] } } }, { "dynamicSegment": { "name": "United States", "sessionSegment": { "segmentFilters": [{ "simpleSegment": { "orFiltersForSegment": [{ "segmentFilterClauses": [{ "dimensionFilter": { "dimensionName": "ga:country", "operator": "EXACT", "expressions": "United States" } }] }] } }] } } }, { "dynamicSegment": { "name": "United Kingdom", "sessionSegment": { "segmentFilters": [{ "simpleSegment": { "orFiltersForSegment": [{ "segmentFilterClauses": [{ "dimensionFilter": { "dimensionName": "ga:country", "operator": "EXACT", "expressions": "United Kingdom" } }] }] } }] } } }], 'hideTotals': 'true', 'hideValueRanges': 'true' }] }
Google Custom Search Engine Schema.org structured data and returned results through API
Here is my markup with rich snippets <div vocab="http://schema.org" typeof="GovernmentOrganization"> <p> <span property="logo"><img src="http://www.place.com/image.png" class="logo"/></span> <span class="h2" property="name">Department Of Stuff And Things</span><br /> <span class="h4" property="department">State Agency</span><br /> http://www.place.com </p> <strong>Locations:</strong><br /><br /> <div property="location" typeof="GovernmentOffice"> <p property="location" typeof="PostalAddress"> Main Office<br /> <span property="streetAddress">555 Something Street Apt 2</span><br /> <span property="addressLocality">Jacksonville</span> <span property="addressRegion">FL</span> <span property="postalCode">11111</span><br /> <span property="addressCountry">US</span> </p> </div> <strong>Services:</strong><br /><br /> <div property="hasOfferCatalog" typeof="OfferCatalog"> <div property="itemListElement" typeof="GovernmentService"> <p> <strong><span property="name">Service 1</span></strong><br /> <span property="category">Web Based</span><br /> <span property="description">Get Some stuff and things</span><br /> https://www.place.com/Service1<br /> </p> </div> <div property="itemListElement" typeof="GovernmentService"> <p> <strong><span property="name">Apply For Benefits</span></strong><br /> <span property="category">Phone Based</span><br /> <span property="description">This service helps you apply for the benefits you deserve</span><br /> https://www.place.com/Service1<br /> </p> </div> </div> The structured data testing tool seems to organize and validate everything appropriately, Including my small collection of services (OfferCatalog). When doing a request to the Custom Search API and tacking on the :more:pagemap:GovernmentOrganization things seem to be OK and I get results I expect. But the JSON object for pagemap only includes the first level of my organization: "pagemap": { "GovernmentOrganization": [ { "name": "Department Of Stuff And Things", "department": "State Agency", "url": "http://www.place.com" }, Any ideas on why my related objects (GovernmentOffice / locations / OfferCatalog / GovernmentServices) are not being including? Is there a better way to organize and structure this for Google?
Consider this approach. Include the following JSON-LD script in the document. It can go anywhere but consider placing it before the RDFa DIV: <script type="application/ld+json" id=""> { "#context": { "#vocab": "http://schema.org/", "#base": "http://www.place.com/" }, "#graph": [ { "#id": "_:ub220bL18C41", "#type": "PostalAddress", "addressCountry": "US", "addressLocality": "Jacksonville", "addressRegion": "FL", "postalCode": "11111", "streetAddress": "555 Something Street Apt 2" }, { "#id": "_:ub220bL4C1", "#type": "GovernmentOrganization", "department": "State Agency", "hasOfferCatalog": { "#id": "_:ub220bL6C40" }, "location": { "#id": "_:ub220bL17C33" }, "logo": "", "name": "Department Of Stuff And Things", "url": "http://www.place.com" }, { "#id": "_:ub220bL17C33", "#type": "GovernmentOffice", "location": { "#id": "_:ub220bL18C41" } }, { "#id": "_:ub220bL6C40", "#type": "OfferCatalog", "itemListElement": [ { "#id": "_:ub220bL12C17" }, { "#id": "_:ub220bL7C48" } ] }, { "#id": "_:ub220bL12C17", "#type": "GovernmentService", "category": "Web Based", "description": "Get Some stuff and things", "name": "Service 1", "url": "https://www.place.com/Service1" }, { "#id": "_:ub220bL7C48", "#type": "GovernmentService", "category": "Phone Based", "description": "This service helps you apply for the benefits you deserve", "name": "Apply For Benefits", "url": "https://www.place.com/Service2" } ] } </script> You'll want to change #base to your site and decide how to organize your directory structure to manage your identifiers. Then change each blank node to the appropriate identifier. Then minimize the JSON-LD. The JSON-LD is semantically identical to the RDFa. As such, you will be able to analyze the Google pagemap and decide which strategy works better for you. I don't expect Google to penalize the page if the JSON-LD is semantically identical to the HTML/RDFa markup.
As per the Google: I believe you are seeing only one value of a attribute in JSON API. I would like to update you that as per the design in JSON API we show only one attribute value, while in XML we show all values. We already have a feature request #16696973 to display all the attribute values in JSON API. Currently I don't have an ETA when it will be implemented. As a workaround you have to use XML API.
JSTree Types plugin not working with Rel attribute
Here is how i configure the jsTree Plugin: $(function () { $("#FoldersTreeContainer").jstree({ "core": { "animation": 150 }, "themes": { "rtl": true, "theme": "classic", "dots": false, "icons": true }, "types": { "types": { "Normal": { "icon": { "image": "\Content\css\jsTree\default\Folder.png" }, }, "Legend": { "icon": { "image": "\Content\css\jsTree\default\Legend.png" }, } } }, "plugins": ["html_data", "themes", "types"] }); }); now here is the relevant HTML: <div id="FoldersTreeContainer"> <ul id="FoldersTree"> <li rel="Normal"><a href="#" >other</a></li> <li rel="Normal"><a href="#" >item1</a></li> <li rel="Normal"><a href="" >item2</a></li> <li rel="Legend"><a href="#" >item3-legend</a></li> </ul> </div> I use the "rel" attribute of the <li> tag for the type but i still get the default folder icons.. what am i dooing wrong ?
You might be using the 3.x version of jstree which does not take the rel attribute into account. If you are using the 3.x version, more information can be found at this link : https://github.com/vakata/jstree/issues/473