Table of contents
🛠Introduction to Sitecore Content Serialization (SCS) and Common Challenges
Sitecore Content Serialization (SCS) is a great tool for managing and syncing content in Sitecore projects, especially when dealing with multiple sites and lots of content items. But problems like duplicate item errors can mess up the serialization process, which can be frustrating for developers. This article looks at a real-life situation where duplicate items caused errors during SCS setup and how an automated PowerShell script using Sitecore PowerShell Extension (SPE) quickly fixed the problem.
📋What is Sitecore Content Serialization?
Sitecore Content Serialization (SCS) lets developers turn content items, templates, and settings into JSON files (.yml) for version control and deployment. It makes content management easier by helping teams track changes, work together well, and deploy content consistently across different environments.
- Benefits of SCS:
- Version Control: Keep content in Git or other version control systems for easy tracking.
- Team Collaboration: Allow multiple developers to work on the same project
- Automation: Automate content deployment and syncing
- Consistency: Keep content the same across development, staging, and production environments
- Version Control: Keep content in Git or other version control systems for easy tracking.
-
Best Practices:
To prevent serialization problems, follow these SCS best practices::
-
Use unique item names in the content tree.
-
Set up correct ignore and scope rules in .module.json.
-
Test serialization in a development environment before deploying.
-
Use Sitecore PowerShell scripts for bulk content tasks.
-
Use unique item names in the content tree.
While setting up Sitecore Content Serialization (SCS) for a project with multiple sites and running the command dotnet sitecore ser pull, I encountered a serialization error: Non-unique paths cannot be serialized. Please choose a different item name.:
This happened even though I set the module.json to ignore certain scopes like /ContosoTenant.
attempted to be cached, but the cache already contained [...] with different ID. Non-unique paths cannot be serialized.
This happened even though I set the module.json to ignore certain scopes like /ContosoTenant.
{ "namespace": "Contoso.Tenants", "references": ["Contoso.InIt"], "items": { "includes": [ { "name": "tenant.Root", "path": "/sitecore/content", "scope": "descendantsOnly", "allowedPushOperations": "createUpdateAndDelete", "rules": [ { "path": "/ContosoTenantFolder", "scope": "SingleItem" }, { "path": "/ContosoTenantFolder/ContosoTenant", "scope": "SingleItem" }, { "path": "/ContosoTenant", "scope": "ignored" } ] } ] } }
Manually deleting hundreds of duplicate items and updating references would have been a huge hassle. So, I made a custom Sitecore PowerShell Extension (SPE) script that:
-
Moves through a specific part of the content tree
-
Finds items with the same name
-
Keeps the oldest item and removes the others
-
Updates all links to point to the item that was kept
-
💻 Sample SPE Script for Duplicate Cleanup
<# This script goes through a chosen node, finds items with a certain name and template, and deletes all but the oldest one. It also updates all links from the deleted items to point to the oldest item. #> $dialogProps = @{ Title = "Duplicate Item Cleanup" Description = "Select the root node and item name to search for duplicates using a specific template." Width = 450 Height = 250 OkButtonName = "Continue" CancelButtonName = "Cancel" ShowHints = $true Parameters = @( @{ Name = "selectedPath" Title = "Root Path" Source = "/sitecore/content" Editor = "droptree" Tooltip = "Choose the root path for duplicate detection." }, @{ Name = "itemName" Title = "Item Name" Editor = "text" Tooltip = "Enter the name of the item to search for." } ) } $result = Read-Variable @dialogProps if ($result -ne "ok" -or [string]::IsNullOrEmpty($selectedPath) -or [string]::IsNullOrEmpty($itemName)) { Write-Host "Path or item name not provided. Exiting." Exit } $rootPath = $selectedPath.Paths.Path if ([string]::IsNullOrEmpty($rootPath)) { $rootPath = "/sitecore/content" } Write-Host "Processing root path: $rootPath" Write-Host "Searching for item name: $itemName" $templateId = "{xxx-xxx-xxxx-xxxx-xxxx}" $matchedItems = Get-ChildItem -Path "master:$rootPath" -Recurse | Where-Object { $_ -ne $null -and $_.TemplateID.ToString() -eq $templateId -and $_.Name -eq $itemName } if ($matchedItems.Count -le 1) { Write-Host "Only one or no matching item found. No action needed." Exit } $items = $matchedItems | Sort-Object __Created $oldest = $items[0] $duplicates = $items | Where-Object { $_.ID -ne $oldest.ID } $deletedItems = @() foreach ($dupe in $duplicates) { Write-Host "Replacing links from '$($dupe.Paths.Path)' to '$($oldest.Paths.Path)'" -ForegroundColor Cyan $referrers = [Sitecore.Globals]::LinkDatabase.GetReferrers($dupe) foreach ($link in $referrers) { $refItem = Get-Item -Path ("master:" + $link.SourceItemID) if ($refItem -ne $null -and $refItem.Fields[$link.SourceFieldID] -ne $null) { $field = $refItem.Fields[$link.SourceFieldID] $refItem.Editing.BeginEdit() $field.Value = $field.Value -replace [Regex]::Escape($dupe.ID.ToString()), $oldest.ID.ToString() $refItem.Editing.EndEdit() Write-Host " Updated link in item: $($refItem.Paths.Path)" } } Write-Host "Deleting item: $($dupe.Paths.Path)" -ForegroundColor Red $dupe | Remove-Item $deletedItems += $dupe } $props = @{ Title = "Duplicate Item Cleanup Report" InfoTitle = "Items Deleted and Links Updated" InfoDescription = "List of duplicate items named '$itemName' using template ID $templateId that were deleted." PageSize = 100 } $deletedItems | Show-ListView @props -Property @{ Label = "Item Name"; Expression = { $_.Name } }, @{ Label = "Item ID"; Expression = { $_.ID } }, @{ Label = "Item Path"; Expression = { $_.Paths.Path } }, @{ Label = "Created"; Expression = { $_.__Created } }, @{ Label = "Updated"; Expression = { $_.__Updated } } Close-Window
⚠ Always backup before bulk-deleting and test extensively in non-production environments.
⚡ This script saved hours of manual work and made sure the content structure was clean and consistent for serialization. -
✔️ Benefits of Using This Script
-
Efficient Automation: Makes content cleanup easier by cutting down on mistakes and manual work, ensuring it's done right.
-
Versatile Reusability: Can be used for different duplicate-cleaning tasks and content tree navigation, making it flexible.
-
Integrity and Safety: Keeps data intact and updates links automatically to avoid broken references, ensuring safe changes.
-
Scalable Performance: Can manage large content trees with many items, saving time and resources..
-
Efficient Automation: Makes content cleanup easier by cutting down on mistakes and manual work, ensuring it's done right.
Setting up Sitecore Content Serialization can be tough, especially with old content structures and duplicate items. But by using Sitecore PowerShell Extensions, you can automate finding and fixing duplicate item errors, which often happen in big, multi-site projects. This saves time and keeps your content organized and efficient. You can adjust the script to match your Sitecore project needs, making things more efficient and cutting down on manual mistakes.
Scan the QR code below to download the script. It will help you manage your Sitecore content more easily and fix duplicate item issues.
If you've had similar issues with SCS, share your solutions 🔠 or tips 💬 to help others in the community.
If you enjoy this content, consider subscribing 📰 for more updates and insights. Your engagement is very important to me and helps me keep providing valuable resources! 🌟
Scan the QR code below to download the script. It will help you manage your Sitecore content more easily and fix duplicate item issues.
If you've had similar issues with SCS, share your solutions 🔠 or tips 💬 to help others in the community.
Automate Sitecore XM Cloud Webhook Registration with Azure DevOps Pipeline and PowerShell | How to Implement Real-Time Updates from XM Cloud to Sitecore Search | How to Create Azure Function Webhooks for Sitecore XM Cloud and Experience Edge Integration |
How to Set Up Full-Stack XM Cloud Local Development for Multiple Code Bases | How to Configure an External Editing Host for Sitecore XM Cloud | Deploying Sitecore XM Cloud - JSS Apps to Vercel with Azure DevOps Pipeline: A Step-by-Step Guide |
Extract metadata with Sitecore Search JavaScript Document Extractor | Sitecore Official Documentation: Content Serialization | Sitecore PowerShell Extensions (SPE) GitHub - The official repository for Sitecore PowerShell Extensions, a must-have tool for automation in Sitecore. |
Handling Duplicate Content in Sitecore – Strategies to identify and resolve duplicate content problems in large-scale Sitecore implementations. | XM Cloud Serialization Best Practices – Sitecore Docs Sitecore’s recommendations for serialization in XM Cloud, including modular architecture tips. | Bulk Editing Sitecore Items with SPE - A deep dive into using PowerShell for bulk operations in Sitecore, including cleanup scripts. |
Fixing Serialization Conflicts - A community discussion on resolving ‘non-unique path’ errors in Sitecore SCS, with tested solutions. | Sitecore CLI and Serialization How to integrate Sitecore CLI with serialization for DevOps-friendly workflows. | SPE Script Repository – Sitecore Community - A curated collection of PowerShell scripts for Sitecore, including content tree maintenance. |