ItemList JSON-LD on Jobs/Shifts list & landing pages
CI/CD / CI · dotnet build (push) Successful in 2m43s
CI/CD / Deploy · hamkadr (push) Successful in 1m24s

Mark up the result list as a schema.org ItemList (ordered listing URLs) so Google
reads the landing/list pages as a curated collection. Emitted alongside the
breadcrumb JSON-LD when there are results.

Improvement 6 of the backlog (SEO).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-20 19:15:12 +03:30
parent 3edd21d2b6
commit 5e1b2ee979
3 changed files with 24 additions and 0 deletions
@@ -145,4 +145,8 @@
@section Head {
@{ var bcUrl = $"{ViewContext.HttpContext.Request.Scheme}://{ViewContext.HttpContext.Request.Host}"; }
@Html.Raw("<script type=\"application/ld+json\">" + JobsMedical.Web.Services.SeoJsonLd.Breadcrumb(Model.Breadcrumbs, bcUrl) + "</script>")
@if (Model.Results.Count > 0)
{
@Html.Raw("<script type=\"application/ld+json\">" + JobsMedical.Web.Services.SeoJsonLd.ItemList(Model.Results.Select(j => "/Jobs/Details/" + j.Id), bcUrl) + "</script>")
}
}
@@ -178,4 +178,8 @@
@section Head {
@{ var bcUrl = $"{ViewContext.HttpContext.Request.Scheme}://{ViewContext.HttpContext.Request.Host}"; }
@Html.Raw("<script type=\"application/ld+json\">" + JobsMedical.Web.Services.SeoJsonLd.Breadcrumb(Model.Breadcrumbs, bcUrl) + "</script>")
@if (Model.Results.Count > 0)
{
@Html.Raw("<script type=\"application/ld+json\">" + JobsMedical.Web.Services.SeoJsonLd.ItemList(Model.Results.Select(s => "/Shifts/Details/" + s.Id), bcUrl) + "</script>")
}
}
+16
View File
@@ -145,6 +145,22 @@ public static class SeoJsonLd
}, Opts));
}
/// <summary>ItemList JSON-LD for a results/landing page — an ordered list of the listing URLs,
/// so Google understands it as a curated collection. Relative URLs are made absolute.</summary>
public static string ItemList(IEnumerable<string> urls, string baseUrl)
{
var els = urls.Select((u, i) => (object)new Dictionary<string, object?>
{
["type"] = "ListItem", ["position"] = i + 1, ["url"] = u.StartsWith("http") ? u : baseUrl + u,
}).ToList();
return Fix(JsonSerializer.Serialize(new Dictionary<string, object?>
{
["@context"] = "https://schema.org",
["@type"] = "ItemList",
["itemListElement"] = els,
}, Opts));
}
// Nested anonymous objects use "type"/"queryyy" placeholders for @type / query-input;
// restore the @-prefixed schema.org keys here.
private static string Fix(string json) => json