繁体   English   中英

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

[英]update word Customxml part using of OpenXml API, but can't update "document.xml" in Main Document

我们使用 C# 代码更新自定义 xml 部分。 我们在寡妇中成功更新了文档。 但是在Linux环境下能不能打开这个文件,不能改值。

我们如何实现windows中自定义xml部分以及word文件夹中Document.xml的更改?

使用另一个问题的稍微增强的示例,让我们假设您有一个MainDocumentPart ( /word/document.xml ) 与数据绑定w:sdt (注意这是一个包含w:p的块级结构化文档标签 (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类。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM