使用OpenXml WmlComparer对比含不同法律样式编号文档的异常问题
问题:OpenXml WmlComparer对比Word文档丢失法律样式编号及删除线格式
使用C#的OpenXml库,通过WmlComparer.Compare方法对比两个Word文档时,多数场景功能正常,但遇到法律样式编号时出现以下问题:
- 当其中一个文档包含法律样式编号,另一个文档不含时,生成的对比文档会完全丢失法律编号
- 未为不匹配的编号添加删除线格式
- 已配置基础的
WmlComparerSettings参数,但无法解决上述问题
相关代码
WmlComparerSettings settings = new WmlComparerSettings { AuthorForRevisions = author, DetailThreshold = 0 }; var originalDoc = new WmlDocument(originalPath.ToString()); var revisedDoc = new WmlDocument(revisedPath.ToString()); WmlDocument comparedDoc = WmlComparer.Compare(originalDoc, revisedDoc, settings);
解决方案
1. 手动合并编号样式定义
WmlComparer默认不会完整保留两个文档的编号库差异,需要手动复制缺失的编号样式到对比文档中:
- 分别提取原始文档和修订文档的
NumberingDefinitionsPart - 将其中一方存在但对比文档缺失的编号实例复制进去,确保编号ID对应正确
// 获取原始与修订文档的编号部分 var originalNumbering = originalDoc.Document.MainDocumentPart.NumberingDefinitionsPart; var revisedNumbering = revisedDoc.Document.MainDocumentPart.NumberingDefinitionsPart; // 确保对比文档有编号部分 var comparedNumbering = comparedDoc.Document.MainDocumentPart.NumberingDefinitionsPart; if (comparedNumbering == null) { comparedNumbering = comparedDoc.Document.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>(); } // 复制原始文档中缺失的编号实例 foreach (var numInstance in originalNumbering.Numbering.Elements<NumberingInstance>()) { if (!comparedNumbering.Numbering.Elements<NumberingInstance>().Any(n => n.NumberID == numInstance.NumberID)) { comparedNumbering.Numbering.Append(numInstance.CloneNode(true)); } } // 复制修订文档中缺失的编号实例 foreach (var numInstance in revisedNumbering.Numbering.Elements<NumberingInstance>()) { if (!comparedNumbering.Numbering.Elements<NumberingInstance>().Any(n => n.NumberID == numInstance.NumberID)) { comparedNumbering.Numbering.Append(numInstance.CloneNode(true)); } }
2. 手动添加编号的修订标记
针对编号存在/缺失的差异,手动遍历段落并添加删除线或插入标记:
- 对比原始与修订文档段落的编号ID
- 对原始有编号、修订无编号的段落,在对比文档中为编号添加删除线修订
- 对原始无编号、修订有编号的段落,添加插入标记
var originalParagraphs = originalDoc.Document.MainDocumentPart.Document.Body.Elements<Paragraph>().ToList(); var revisedParagraphs = revisedDoc.Document.MainDocumentPart.Document.Body.Elements<Paragraph>().ToList(); var comparedBody = comparedDoc.Document.MainDocumentPart.Document.Body; for (int i = 0; i < Math.Min(originalParagraphs.Count, revisedParagraphs.Count); i++) { var origPara = originalParagraphs[i]; var revPara = revisedParagraphs[i]; var comparedPara = comparedBody.Elements<Paragraph>().ElementAt(i); var origNumId = origPara.ParagraphProperties?.NumberingProperties?.NumberingId?.Val; var revNumId = revPara.ParagraphProperties?.NumberingProperties?.NumberingId?.Val; // 原始有编号,修订无编号:添加删除线标记 if (origNumId != null && revNumId == null) { if (origPara.ParagraphProperties?.NumberingProperties != null) { var deleteMark = new Delete(); deleteMark.Append(origPara.ParagraphProperties.NumberingProperties.CloneNode(true)); deleteMark.Author = settings.AuthorForRevisions; deleteMark.Date = DateTime.Now; comparedPara.ParagraphProperties?.InsertBefore(deleteMark, comparedPara.ParagraphProperties.NumberingProperties); comparedPara.ParagraphProperties?.RemoveChild(comparedPara.ParagraphProperties.NumberingProperties); } } // 原始无编号,修订有编号:添加插入标记 else if (origNumId == null && revNumId != null) { if (revPara.ParagraphProperties?.NumberingProperties != null) { var insertMark = new Insert(); insertMark.Append(revPara.ParagraphProperties.NumberingProperties.CloneNode(true)); insertMark.Author = settings.AuthorForRevisions; insertMark.Date = DateTime.Now; comparedPara.ParagraphProperties?.Append(insertMark); } } }
3. 优化对比器细节阈值
调整DetailThreshold参数,提高对比器对格式差异的识别精度:
WmlComparerSettings settings = new WmlComparerSettings { AuthorForRevisions = author, DetailThreshold = 1 // 提升细节阈值,增强格式差异识别 };
内容的提问来源于stack exchange,提问作者yatin tripathi
相关产品推荐
相关产品推荐

