使用 OpenXml API 更新单词自定义部分,但无法更新主文档中的"document.xml"



我们使用 C# 代码更新自定义 XML 部件。 我们已成功更新寡妇中的文档。 但是我们可以在 Linux 环境中打开此文档,它无法更改值。

我们如何在窗口中实现自定义XML部分的更改以及word文件夹中的文档.xml?

使用另一个问题中略微增强的示例,假设您有一个带有数据绑定w:sdtMainDocumentPart(/word/document.xml((请注意,这是一个块级结构化文档标记(SDT(,包含此示例中的w:p;它也可以是包含在w:p中的内联级SDT(。

<?xml version="1.0" encoding="utf-8"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:sdt>
<w:sdtPr>
<w:tag w:val="Node" />
<w:dataBinding w:prefixMappings="xmlns:ex='http://example.com'" 
w:xpath="ex:Root[1]/ex:Node[1]"
w:storeItemID="{C152D0E4-7C03-4CFA-97E6-721B2DCB5C7B}" />
</w:sdtPr>
<w:sdtContent>
<w:p>
<w:r>
<w:t>VALUE1</w:t>
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>
</w:body>
</w:document>

我们的MainDocumentPart引用了以下CustomXmlPart(/customXML/item.xml(:

<?xml version="1.0" encoding="utf-8"?>
<ex:Root xmlns:ex="http://example.com">
<ex:Node>VALUE1</ex:Node>
</ex:Root>

然后,上述CustomXmlPart引用以下CustomXmlPropertiesPart(/customXML/itemProps1.xml(:

<?xml version="1.0" encoding="utf-8"?>
<ds:datastoreItem xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/customXml" 
ds:itemID="{C152D0E4-7C03-4CFA-97E6-721B2DCB5C7B}">
<ds:schemaRefs>
<ds:schemaRef ds:uri="http://example.com" />
</ds:schemaRefs>
</ds:datastoreItem>

在这种情况下,"增强"是顶部MainDocumentPart中的附加w:tag元素。此w:tag元素是在w:sdt元素与其绑定到的自定义 XML 元素(例如,ex:Node(之间创建易于使用的链接的一种方法。在此示例中,值Node恰好是ex:Node元素的本地名称。

最后,这是一个工作代码示例,展示了如何同时更新CustomXmlPartMainDocumentPart。这使用 Open-XML-PowerTools。

[Fact]
public void CanUpdateCustomXmlAndMainDocumentPart()
{
// Define the initial and updated values of our custom XML element and
// the data-bound w:sdt element.
const string initialValue = "VALUE1";
const string updatedValue = "value2";
// Create the root element of the custom XML part with the initial value.
var customXmlRoot =
new XElement(Ns + "Root",
new XAttribute(XNamespace.Xmlns + NsPrefix, NsName),
new XElement(Ns + "Node", initialValue));
// Create the w:sdtContent child element of our w:sdt with the initial value.
var sdtContent =
new XElement(W.sdtContent,
new XElement(W.p,
new XElement(W.r,
new XElement(W.t, initialValue))));
// Create a WordprocessingDocument with the initial values.
using var stream = new MemoryStream();
using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, Type))
{
InitializeWordprocessingDocument(wordDocument, customXmlRoot, sdtContent);
}
// Assert the WordprocessingDocument has the expected, initial values.
using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true))
{
AssertValuesAreAsExpected(wordDocument, initialValue);
}
// Update the WordprocessingDocument, using the updated value.
using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true))
{
MainDocumentPart mainDocumentPart = wordDocument.MainDocumentPart;
// Change custom XML element, again using the simplifying assumption
// that we only have a single custom XML part and a single ex:Node
// element.
CustomXmlPart customXmlPart = mainDocumentPart.CustomXmlParts.Single();
XElement root = customXmlPart.GetXElement();
XElement node = root.Elements(Ns + "Node").Single();
node.Value = updatedValue;
customXmlPart.PutXDocument();
// Change the w:sdt contained in the MainDocumentPart.
XElement document = mainDocumentPart.GetXElement();
XElement sdt = FindSdtWithTag("Node", document);
sdtContent = sdt.Elements(W.sdtContent).Single();
sdtContent.ReplaceAll(
new XElement(W.p,
new XElement(W.r,
new XElement(W.t, updatedValue))));
mainDocumentPart.PutXDocument();
}
// Assert the WordprocessingDocument has the expected, updated values.
using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true))
{
AssertValuesAreAsExpected(wordDocument, updatedValue);
}
}
private static void InitializeWordprocessingDocument(
WordprocessingDocument wordDocument,
XElement customXmlRoot,
XElement sdtContent)
{
MainDocumentPart mainDocumentPart = wordDocument.AddMainDocumentPart();
string storeItemId = CreateCustomXmlPart(mainDocumentPart, customXmlRoot);
mainDocumentPart.PutXDocument(new XDocument(
new XElement(W.document,
new XAttribute(XNamespace.Xmlns + "w", W.w.NamespaceName),
new XElement(W.body,
new XElement(W.sdt,
new XElement(W.sdtPr,
new XElement(W.tag, new XAttribute(W.val, "Node")),
new XElement(W.dataBinding,
new XAttribute(W.prefixMappings, $"xmlns:{NsPrefix}='{NsName}'"),
new XAttribute(W.xpath, $"{NsPrefix}:Root[1]/{NsPrefix}:Node[1]"),
new XAttribute(W.storeItemID, storeItemId))),
sdtContent)))));
}
private static void AssertValuesAreAsExpected(
WordprocessingDocument wordDocument,
string expectedValue)
{
// Retrieve inner text of w:sdt element.
MainDocumentPart mainDocumentPart = wordDocument.MainDocumentPart;
XElement sdt = FindSdtWithTag("Node", mainDocumentPart.GetXElement());
string sdtInnerText = GetInnerText(sdt);
// Retrieve inner text of custom XML element, making the simplifying
// assumption that we only have a single custom XML part. In reality,
// we would have to find the custom XML part to which our w:sdt elements
// are bound among any number of custom XML parts. Further, in our
// simplified example, we also assume there is a single ex:Node element.
CustomXmlPart customXmlPart = mainDocumentPart.CustomXmlParts.Single();
XElement root = customXmlPart.GetXElement();
XElement node = root.Elements(Ns + "Node").Single();
string nodeInnerText = node.Value;
// Assert those inner text are indeed equal.
Assert.Equal(expectedValue, sdtInnerText);
Assert.Equal(expectedValue, nodeInnerText);
}
private static XElement FindSdtWithTag(string tagValue, XElement openXmlCompositeElement)
{
return openXmlCompositeElement
.Descendants(W.sdt)
.FirstOrDefault(e => e
.Elements(W.sdtPr)
.Elements(W.tag)
.Any(tag => (string) tag.Attribute(W.val) == tagValue));
}
private static string GetInnerText(XElement openXmlElement)
{
return openXmlElement
.DescendantsAndSelf(W.r)
.Select(UnicodeMapper.RunToString)
.StringConcatenate();
}

完整的源代码包含在我的 CodeSnippets GitHub 存储库中。查找 DataBoundContentControlTests 类。

最新更新