使用OpenXml API更新Word Customxml部件,但无法更新主文档中的“ document.xml”

问题描述 投票:-1回答:1

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

如何实现在Windows中更改自定义xml部件以及在Word Folder中实现Document.xml?

ms-word openxml-sdk docx4j
1个回答
0
投票

使用另一个问题的增强示例,假设您有一个MainDocumentPart/word/document.xml)和一个数据绑定的w:sdt(请注意,这是一个块级结构化文档标签(SDT) 在此示例中为w:p;也可以是w:p中的内联级别SDT contained。]

<?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>

在这种情况下,“增强”是顶部w:tag中的附加MainDocumentPart元素。此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类。

© www.soinside.com 2019 - 2024. All rights reserved.