Skip to content

Commit 9a0131f

Browse files
knilecrackclaude
andcommitted
Fix XDocument mutation and duplicate singleton relationships in WmlComparer
This commit addresses two issues: 1. XDocument Mutation (Issue OpenXmlDev#3): - WmlDocument.MainDocumentPart and PtMainDocumentPart.WordprocessingCommentsPart were removing nodes from cached XDocuments, causing subsequent GetXDocument() calls to return empty documents within the same package lifetime - Fixed by cloning the root element before extracting and removing child nodes - Location: WmlDocument.cs lines 67-81 and 20-37 2. Duplicate Singleton Relationships: - WmlComparer.MoveRelatedPartsToDestination was creating duplicate relationships for singleton parts (fontTable, styles, numbering, settings, webSettings, theme) - This caused validation errors: "can only have one instance of relationship that targets part fontTable" - Fixed by checking if a singleton relationship already exists and reusing it instead of creating a duplicate - Location: WmlComparer.cs MoveRelatedPartsToDestination method Test Results: - Fixed 2 WmlComparer tests (RC-0020, WCB-1740) that were failing with fontTable errors - Improved from 22 failing tests to 20 failing tests (956 -> 958 passing) - Remaining failures are unrelated to these issues Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 1408924 commit 9a0131f

File tree

2 files changed

+28
-2
lines changed

2 files changed

+28
-2
lines changed

OpenXmlPowerTools/WmlComparer.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4771,6 +4771,30 @@ private static XElement MoveRelatedPartsToDestination(PackagePart partOfDeletedC
47714771

47724772
if (targetUri != null)
47734773
{
4774+
// Check if this is a singleton relationship type that should be reused if it already exists
4775+
var isSingletonRelationship =
4776+
relationshipForDeletedPart.RelationshipType == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" ||
4777+
relationshipForDeletedPart.RelationshipType == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" ||
4778+
relationshipForDeletedPart.RelationshipType == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/stylesWithEffects" ||
4779+
relationshipForDeletedPart.RelationshipType == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" ||
4780+
relationshipForDeletedPart.RelationshipType == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" ||
4781+
relationshipForDeletedPart.RelationshipType == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" ||
4782+
relationshipForDeletedPart.RelationshipType == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
4783+
4784+
// For singleton relationships, check if one already exists and reuse it
4785+
if (isSingletonRelationship)
4786+
{
4787+
var existingRelationship = partInNewDocument
4788+
.GetRelationships()
4789+
.FirstOrDefault(r => r.RelationshipType == relationshipForDeletedPart.RelationshipType);
4790+
4791+
if (existingRelationship != null)
4792+
{
4793+
// Reuse the existing relationship ID
4794+
att.Value = existingRelationship.Id;
4795+
continue;
4796+
}
4797+
}
47744798

47754799
var relatedPackagePart = partOfDeletedContent.Package.GetPart(targetUri);
47764800
var uriSplit = relatedPackagePart.Uri.ToString().Split('/');

OpenXmlPowerTools/WmlDocument.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ public PtWordprocessingCommentsPart WordprocessingCommentsPart
2727
WordprocessingCommentsPart commentsPart = wDoc.MainDocumentPart.WordprocessingCommentsPart;
2828
if (commentsPart == null)
2929
return null;
30-
XElement partElement = commentsPart.GetXDocument().Root;
30+
// Clone the root element to avoid mutating the cached XDocument
31+
XElement partElement = new XElement(commentsPart.GetXDocument().Root);
3132
var childNodes = partElement.Nodes().ToList();
3233
foreach (var item in childNodes)
3334
item.Remove();
@@ -71,7 +72,8 @@ public PtMainDocumentPart MainDocumentPart
7172
using (MemoryStream ms = new MemoryStream(this.DocumentByteArray))
7273
using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, false))
7374
{
74-
XElement partElement = wDoc.MainDocumentPart.GetXDocument().Root;
75+
// Clone the root element to avoid mutating the cached XDocument
76+
XElement partElement = new XElement(wDoc.MainDocumentPart.GetXDocument().Root);
7577
var childNodes = partElement.Nodes().ToList();
7678
foreach (var item in childNodes)
7779
item.Remove();

0 commit comments

Comments
 (0)