如何在使用打开 XML 时修改文件流中的内容?



在下面的代码中,我将一些文件合并在一起并将它们保存在test.docx文件中。但是,在合并每个文件之前,我想首先替换一些用作占位符的内容控件的文本。有人可以告诉我如何做到这一点吗?

假设我在模板 2 中有一个内容控件,它称为占位符 1。如何在使用文件流时向此占位符添加文本?

string fileName = Path.Combine(@"Docstemplates", "test.docx");
for (int i = 1; i < 3; i++)
{
string filePath = Path.Combine(@"Docstemplates", "report-Part" + i + ".docx");
//using (MemoryStream ms = new MemoryStream())
//{
//ms.Write(templateFile, 0, templateFile.Length);
using (WordprocessingDocument myDoc = WordprocessingDocument.Open(fileName, true))
{
MainDocumentPart mainPart = myDoc.MainDocumentPart;
string altChunkId = "AltChunkId" + Guid.NewGuid();
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML, altChunkId);
using (FileStream fileStream = File.Open(filePath, FileMode.Open))
{
chunk.FeedData(fileStream);
}
//chunk.FeedData(ms);
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
Paragraph paragraph2 = new Paragraph() { RsidParagraphAddition = "00BE27E7", RsidRunAdditionDefault = "00BE27E7" };
Run run2 = new Run();
Break break1 = new Break() { Type = BreakValues.Page };
run2.Append(break1);
paragraph2.Append(run2);
mainPart.Document.Body.Append(paragraph2);
var lastParagraph = mainPart.Document.Body.Elements<Paragraph>().Last();
mainPart.Document.Body.InsertAfter(altChunk, lastParagraph);
mainPart.Document.Save();
myDoc.Close();
}
//ms.Position = 0;
////ms.ToArray();
//output = new byte[ms.ToArray().Length];
//ms.Read(output, 0, output.Length);
//}

下面的示例代码(作为 xUnit 单元测试编写(演示如何实现要执行的操作。我添加了代码注释来解释做了什么以及为什么。

public class AltChunkAssemblyTests
{
// Sample template file names for unit testing purposes.
private readonly string[] _templateFileNames =
{
"report-Part1.docx",
"report-Part2.docx",
"report-Part3.docx"
};
// Sample content maps for unit testing purposes.
// Each Dictionary<string, string> represents data used to replace the
// content of block-level w:sdt elements identified by w:tag values of
// "firstTag" and "secondTag".
private readonly List<Dictionary<string, string>> _contentMaps = new List<Dictionary<string, string>>
{
new Dictionary<string, string>
{
{ "firstTag", "report-Part1: First value" },
{ "secondTag", "report-Part1: Second value" }
},
new Dictionary<string, string>
{
{ "firstTag", "report-Part2: First value" },
{ "secondTag", "report-Part2: Second value" }
},
new Dictionary<string, string>
{
{ "firstTag", "report-Part3: First value" },
{ "secondTag", "report-Part3: Second value" }
}
};
[Fact]
public void CanAssembleDocumentUsingAltChunks()
{
// Create some sample "templates" (technically documents) for unit
// testing purposes.
CreateSampleTemplates();
// Create an empty result document.
using WordprocessingDocument wordDocument = WordprocessingDocument.Create(
"AltChunk.docx", WordprocessingDocumentType.Document);
MainDocumentPart mainPart = wordDocument.AddMainDocumentPart();
var body = new Body();
mainPart.Document = new Document(body);
// Add one w:altChunk element for each sample template, using the
// sample content maps for mapping sample data to the content
// controls contained in the templates.
for (var index = 0; index < 3; index++)
{
if (index > 0) body.AppendChild(new Paragraph(new Run(new Break { Type = BreakValues.Page })));
body.AppendChild(CreateAltChunk(_templateFileNames[index], _contentMaps[index], wordDocument));
}
}
private void CreateSampleTemplates()
{
// Create a sample template for each sample template file names.
foreach (string templateFileName in _templateFileNames)
{
CreateSampleTemplate(templateFileName);
}
}
private static void CreateSampleTemplate(string templateFileName)
{
// Create a new Word document with paragraphs marking the start and
// end of the template (for testing purposes) and two block-level
// structured document tags identified by w:tag elements with values
// "firstTag" and "secondTag" and values that are going to be
// replaced by the ContentControlWriter during document assembly.
using WordprocessingDocument wordDocument = WordprocessingDocument.Create(
templateFileName, WordprocessingDocumentType.Document);
MainDocumentPart mainPart = wordDocument.AddMainDocumentPart();
mainPart.Document =
new Document(
new Body(
new Paragraph(
new Run(
new Text($"Start of template '{templateFileName}'"))),
new SdtBlock(
new SdtProperties(
new Tag { Val = "firstTag" }),
new SdtContentBlock(
new Paragraph(
new Run(
new Text("First template value"))))),
new SdtBlock(
new SdtProperties(
new Tag { Val = "secondTag" }),
new SdtContentBlock(
new Paragraph(
new Run(
new Text("Second template value"))))),
new Paragraph(
new Run(
new Text($"End of template '{templateFileName}'")))));
}
private static AltChunk CreateAltChunk(
string templateFileName,
Dictionary<string, string> contentMap,
WordprocessingDocument wordDocument)
{
// Copy the template file contents to a MemoryStream to be able to
// update the content controls without altering the template file.
using FileStream fileStream = File.Open(templateFileName, FileMode.Open);
using var memoryStream = new MemoryStream();
fileStream.CopyTo(memoryStream);
// Open the copy of the template on the MemoryStream, update the
// content controls, save the updated template back to the
// MemoryStream, and reset the position within the MemoryStream.
using (WordprocessingDocument chunkDocument = WordprocessingDocument.Open(memoryStream, true))
{
var contentControlWriter = new ContentControlWriter(contentMap);
contentControlWriter.WriteContentControls(chunkDocument);
}
memoryStream.Seek(0, SeekOrigin.Begin);
// Create an AlternativeFormatImportPart from the MemoryStream.
string altChunkId = "AltChunkId" + Guid.NewGuid();
AlternativeFormatImportPart chunk = wordDocument.MainDocumentPart.AddAlternativeFormatImportPart(
AlternativeFormatImportPartType.WordprocessingML, altChunkId);
chunk.FeedData(memoryStream);
// Return the w:altChunk element to be added to the w:body element.
return new AltChunk { Id = altChunkId };
}
}

我已经测试了代码,使用我创建的ContentControlWriter类来回答您的另一个问题,即如何使用documentformat.openxml从具有多个页面的word模板创建新文档。它工作得很好。完整的代码可以在我的 CodeSnippets GitHub 存储库中找到。查找 AltChunkAssemblyTests 和 ContentControlWriter。

CreateSampleTemplates()方法创建三个示例文档。例如,report-Part1.docx的主文档部分具有以下内容:

<?xml version="1.0" encoding="utf-8"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p>
<w:r>
<w:t>Start of template 'report-Part1.docx'</w:t>
</w:r>
</w:p>
<w:sdt>
<w:sdtPr>
<w:tag w:val="firstTag" />
</w:sdtPr>
<w:sdtContent>
<w:p>
<w:r>
<w:t>First template value</w:t>
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>
<w:sdt>
<w:sdtPr>
<w:tag w:val="secondTag" />
</w:sdtPr>
<w:sdtContent>
<w:p>
<w:r>
<w:t>Second template value</w:t>
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>
<w:p>
<w:r>
<w:t>End of template 'report-Part1.docx'</w:t>
</w:r>
</w:p>
</w:body>
</w:document>

组装后,如果没有 Word 再次保存文档,AltChunk.docx的主文档部分如下所示:

<?xml version="1.0" encoding="utf-8"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:altChunk r:id="AltChunkId81885280-e38d-4ffb-b8a3-38d96992c2eb" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" />
<w:p>
<w:r>
<w:br w:type="page" />
</w:r>
</w:p>
<w:altChunk r:id="AltChunkId6d862de7-c477-42bc-baa4-c42441e5b03b" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" />
<w:p>
<w:r>
<w:br w:type="page" />
</w:r>
</w:p>
<w:altChunk r:id="AltChunkIdbfd7ea64-4cd0-4acf-9d6f-f3d405c021ca" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" />
</w:body>
</w:document>

我不确定您为什么要使用这些w:altChunk元素和相关部分来组合多个 Word 文档。这需要 Word Microsoft 来完成"繁重的工作",尽管在您的情况下,直接生成正确的标记可能非常容易。例如,一旦您将文档保存在 Word Microsoft中,主文档部分将如下所示(带有其他 XML 命名空间,为了清楚起见,我将其删除(:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
mc:Ignorable="w14">
<w:body>
<w:p w14:paraId="76D6BC46" w14:textId="77777777" w:rsidR="00EA51EB" w:rsidRDefault="00B25FEF">
<w:r>
<w:t>Start of template 'report-Part1.docx'</w:t>
</w:r>
</w:p>
<w:sdt>
<w:sdtPr>
<w:tag w:val="firstTag"/>
<w:id w:val="-1950995891"/>
</w:sdtPr>
<w:sdtEndPr/>
<w:sdtContent>
<w:p w14:paraId="2701CE6E" w14:textId="77777777" w:rsidR="00EA51EB" w:rsidRDefault="00B25FEF">
<w:r>
<w:t>report-Part1: First value</w:t>
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>
<w:sdt>
<w:sdtPr>
<w:tag w:val="secondTag"/>
<w:id w:val="551584029"/>
</w:sdtPr>
<w:sdtEndPr/>
<w:sdtContent>
<w:p w14:paraId="0B591553" w14:textId="77777777" w:rsidR="00EA51EB" w:rsidRDefault="00B25FEF">
<w:r>
<w:t>report-Part1: Second value</w:t>
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>
<w:p w14:paraId="7393CFF0" w14:textId="77777777" w:rsidR="00E60EE9" w:rsidRDefault="00B25FEF">
<w:r>
<w:t>End of template 'report-Part1.docx'</w:t>
</w:r>
</w:p>
<w:p w14:paraId="089D32A3" w14:textId="77777777" w:rsidR="00E60EE9" w:rsidRDefault="00B25FEF">
<w:r>
<w:br w:type="page"/>
</w:r>
</w:p>
<w:p w14:paraId="11AC41DA" w14:textId="77777777" w:rsidR="00716CCA" w:rsidRDefault="00B25FEF">
<w:r>
<w:lastRenderedPageBreak/>
<w:t>Start of template 'report-Part2.docx'</w:t>
</w:r>
</w:p>
<w:sdt>
<w:sdtPr>
<w:tag w:val="firstTag"/>
<w:id w:val="-1559003811"/>
</w:sdtPr>
<w:sdtEndPr/>
<w:sdtContent>
<w:p w14:paraId="1867093C" w14:textId="77777777" w:rsidR="00716CCA" w:rsidRDefault="00B25FEF">
<w:r>
<w:t>report-Part2: First value</w:t>
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>
<w:sdt>
<w:sdtPr>
<w:tag w:val="secondTag"/>
<w:id w:val="-1480071868"/>
</w:sdtPr>
<w:sdtEndPr/>
<w:sdtContent>
<w:p w14:paraId="43DA0FC0" w14:textId="77777777" w:rsidR="00716CCA" w:rsidRDefault="00B25FEF">
<w:r>
<w:t>report-Part2: Second value</w:t>
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>
<w:p w14:paraId="1F9B0122" w14:textId="77777777" w:rsidR="00E60EE9" w:rsidRDefault="00B25FEF">
<w:r>
<w:t>End of template 'report-Part2.docx'</w:t>
</w:r>
</w:p>
<w:p w14:paraId="18873AAA" w14:textId="77777777" w:rsidR="00E60EE9" w:rsidRDefault="00B25FEF">
<w:r>
<w:br w:type="page"/>
</w:r>
</w:p>
<w:p w14:paraId="16E23FE9" w14:textId="77777777" w:rsidR="003C3D2D" w:rsidRDefault="00B25FEF">
<w:r>
<w:lastRenderedPageBreak/>
<w:t>Start of template 'report-Part3.docx'</w:t>
</w:r>
</w:p>
<w:sdt>
<w:sdtPr>
<w:tag w:val="firstTag"/>
<w:id w:val="780077040"/>
</w:sdtPr>
<w:sdtEndPr/>
<w:sdtContent>
<w:p w14:paraId="00BA914F" w14:textId="77777777" w:rsidR="003C3D2D" w:rsidRDefault="00B25FEF">
<w:r>
<w:t>report-Part3: First value</w:t>
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>
<w:sdt>
<w:sdtPr>
<w:tag w:val="secondTag"/>
<w:id w:val="-823814304"/>
</w:sdtPr>
<w:sdtEndPr/>
<w:sdtContent>
<w:p w14:paraId="10653801" w14:textId="77777777" w:rsidR="003C3D2D" w:rsidRDefault="00B25FEF">
<w:r>
<w:t>report-Part3: Second value</w:t>
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>
<w:p w14:paraId="1622299A" w14:textId="77777777" w:rsidR="00E60EE9" w:rsidRDefault="00B25FEF">
<w:r>
<w:t>End of template 'report-Part3.docx'</w:t>
</w:r>
</w:p>
<w:sectPr w:rsidR="00E60EE9">
<w:pgSz w:w="12240" w:h="15840"/>
<w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="708" w:footer="708" w:gutter="0"/>
<w:cols w:space="708"/>
<w:docGrid w:linePitch="360"/>
</w:sectPr>
</w:body>
</w:document>

Word 添加w:sectPr元素,您不必添加该元素(除非您需要特定的页面布局(。它还添加了不需要的w:lastRenderedPageBreaks。此外,添加到w:p(Paragraph(元素的属性和添加到w:sdt元素的元素(例如,w:idw:sdtEndPr(是可选的。

相关内容

  • 没有找到相关文章

最新更新