通过 OpenXml 编辑 MailMerge
Edit MailMerge via OpenXml
我有一个 .docx
,其中包含一个 MailMerge,我需要通过代码 (C#) 更改它的源代码。作为图书馆,我正在使用 OpenXml
。通过将 docx 作为 zip 文件打开并查找路径,我发现数据源的 link 存储在两个不同的位置:word/settings.xml
和 word/_rels/settings.xml.rels
。
要替换第一个:
var template = new MemoryStream(myDocxInByteArray);
using (var doc = DocumentFormat.OpenXml.Packaging.WordprocessingDocument.Open(template, true))
{
var s = doc.MainDocumentPart.DocumentSettingsPart.Settings;
foreach (var els in s.ChildElements)
{
if (els is DocumentFormat.OpenXml.Wordprocessing.MailMerge)
{
var mm = (DocumentFormat.OpenXml.Wordprocessing.MailMerge)els;
mm.Query.Val = "SELECT * FROM " + myNewPath;
break;
}
}
}
这工作正常,但是当文件打开时,Word 要求从新路径和旧路径加载文件,因为它是在 word/_rels/settings.xml.rels
中定义的。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/mailMergeSource" Target="oldPath" TargetMode="External"/>
</Relationships>
如何在 WordprocessingDocument
中找到这个对象 (Relation
)?找不到...
或者有其他方法可以改变它吗?
编辑:
我用不同的来源创建了两个不同的空文件,然后用 OpenXml Productivity Tool 打开以比较它们。正如我已经看到的,区别在于 word/settings.xml
和 word/_rels/settings.xml.rels
.
我相信你可以这样做,如果你已经在设置部分的相关部分XML中有合适的rID(关系ID)。我给出的方法依赖于Linq。
但要注意Word在Settings.xml中可以存储两个个数据源文件名,在settings.xml.rels中有2个对应关系。更重要的是,它们都具有相同的 Relationship 类型字符串。 AIUI Word 在MailMerge 设置的ODSO ("Office Data Source Object") 部分实际上并没有使用数据文件名,但你应该检查一下。
获得旧关系的一种方法如下(这需要 Linq,就目前而言):
// Get the Settings part
var settingsPart = wordDocument.MainDocumentPart.DocumentSettingsPart;
// Create a MailMerge object and populate it with the existing data
// (You do not have to do this - you could get the individual
// elements/attributes from the part's XML)
var mailMerge =
new MailMerge(settingsPart.Settings.ChildElements.First<MailMerge>().OuterXml);
// Get the relationship
ReferenceRelationship oldRel
= settingsPart.ExternalRelationships.Where(
Rel => Rel.Id == mailMerge.DataSourceReference.Id
).First();
/*
// to get the ODSO relationship, use the following, and to replace it, follow the pattern I give below for oldRel and newRel
ReferenceRelationship oldOdsoRel =
settingsPart.ExternalRelationships.Where(
Rel => Rel.Id == mailMerge.DataSourceObject.SourceReference.Id
).First();
*/
// Delete the old Relationship (but notice that the oldRel object remains)
settingsPart.DeleteExternalRelationship(oldRel.Id);
var newRel =
settingsPart.AddExternalRelationship(
oldRel.RelationshipType,
new Uri("your file path/name",UriKind.RelativeOrAbsolute),
oldRel.Id);
最后我想到了以下解决方案:
using (WordprocessingDocument doc = WordprocessingDocument.Open(docInByteArray, isEditable: true))
{
Settings settings = doc.MainDocumentPart.DocumentSettingsPart.Settings;
OpenXmlElement openXmlElement = null;
foreach (OpenXmlElement element in settings.ChildElements)
{
if (element is MailMerge mailMerge)
{
mailMerge.Query.Val = "SELECT * FROM " + csvPath;
mailMerge.DataSourceObject.Remove();
}
else if (element is AttachedTemplate)
{
openXmlElement = element;
}
}
if (openXmlElement != null)
{
openXmlElement.Remove();
}
doc.Save();
}
我有一个 .docx
,其中包含一个 MailMerge,我需要通过代码 (C#) 更改它的源代码。作为图书馆,我正在使用 OpenXml
。通过将 docx 作为 zip 文件打开并查找路径,我发现数据源的 link 存储在两个不同的位置:word/settings.xml
和 word/_rels/settings.xml.rels
。
要替换第一个:
var template = new MemoryStream(myDocxInByteArray);
using (var doc = DocumentFormat.OpenXml.Packaging.WordprocessingDocument.Open(template, true))
{
var s = doc.MainDocumentPart.DocumentSettingsPart.Settings;
foreach (var els in s.ChildElements)
{
if (els is DocumentFormat.OpenXml.Wordprocessing.MailMerge)
{
var mm = (DocumentFormat.OpenXml.Wordprocessing.MailMerge)els;
mm.Query.Val = "SELECT * FROM " + myNewPath;
break;
}
}
}
这工作正常,但是当文件打开时,Word 要求从新路径和旧路径加载文件,因为它是在 word/_rels/settings.xml.rels
中定义的。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/mailMergeSource" Target="oldPath" TargetMode="External"/>
</Relationships>
如何在 WordprocessingDocument
中找到这个对象 (Relation
)?找不到...
或者有其他方法可以改变它吗?
编辑:
我用不同的来源创建了两个不同的空文件,然后用 OpenXml Productivity Tool 打开以比较它们。正如我已经看到的,区别在于 word/settings.xml
和 word/_rels/settings.xml.rels
.
我相信你可以这样做,如果你已经在设置部分的相关部分XML中有合适的rID(关系ID)。我给出的方法依赖于Linq。
但要注意Word在Settings.xml中可以存储两个个数据源文件名,在settings.xml.rels中有2个对应关系。更重要的是,它们都具有相同的 Relationship 类型字符串。 AIUI Word 在MailMerge 设置的ODSO ("Office Data Source Object") 部分实际上并没有使用数据文件名,但你应该检查一下。
获得旧关系的一种方法如下(这需要 Linq,就目前而言):
// Get the Settings part
var settingsPart = wordDocument.MainDocumentPart.DocumentSettingsPart;
// Create a MailMerge object and populate it with the existing data
// (You do not have to do this - you could get the individual
// elements/attributes from the part's XML)
var mailMerge =
new MailMerge(settingsPart.Settings.ChildElements.First<MailMerge>().OuterXml);
// Get the relationship
ReferenceRelationship oldRel
= settingsPart.ExternalRelationships.Where(
Rel => Rel.Id == mailMerge.DataSourceReference.Id
).First();
/*
// to get the ODSO relationship, use the following, and to replace it, follow the pattern I give below for oldRel and newRel
ReferenceRelationship oldOdsoRel =
settingsPart.ExternalRelationships.Where(
Rel => Rel.Id == mailMerge.DataSourceObject.SourceReference.Id
).First();
*/
// Delete the old Relationship (but notice that the oldRel object remains)
settingsPart.DeleteExternalRelationship(oldRel.Id);
var newRel =
settingsPart.AddExternalRelationship(
oldRel.RelationshipType,
new Uri("your file path/name",UriKind.RelativeOrAbsolute),
oldRel.Id);
最后我想到了以下解决方案:
using (WordprocessingDocument doc = WordprocessingDocument.Open(docInByteArray, isEditable: true))
{
Settings settings = doc.MainDocumentPart.DocumentSettingsPart.Settings;
OpenXmlElement openXmlElement = null;
foreach (OpenXmlElement element in settings.ChildElements)
{
if (element is MailMerge mailMerge)
{
mailMerge.Query.Val = "SELECT * FROM " + csvPath;
mailMerge.DataSourceObject.Remove();
}
else if (element is AttachedTemplate)
{
openXmlElement = element;
}
}
if (openXmlElement != null)
{
openXmlElement.Remove();
}
doc.Save();
}