我认为v2.0更好...他们有一些不错的“如何:...”examples但书签看起来并不像表格那么明显...书签由两个定义XML元素BookmarkStart & BookmarkEnd。我们有一些带有文本的模板作为书签,我们只是想用其他一些文本替换书签...没有奇怪的格式正在发生,但是如何选择/替换书签文本?使用Open XML SDK替换Word文件中的书签文本
回答
这里是我的方法使用你们的灵感后:
IDictionary<String, BookmarkStart> bookmarkMap =
new Dictionary<String, BookmarkStart>();
foreach (BookmarkStart bookmarkStart in file.MainDocumentPart.RootElement.Descendants<BookmarkStart>())
{
bookmarkMap[bookmarkStart.Name] = bookmarkStart;
}
foreach (BookmarkStart bookmarkStart in bookmarkMap.Values)
{
Run bookmarkText = bookmarkStart.NextSibling<Run>();
if (bookmarkText != null)
{
bookmarkText.GetFirstChild<Text>().Text = "blah";
}
}
这里是我如何做到这一点在VB.NET:
For Each curBookMark In contractBookMarkStarts
''# Get the "Run" immediately following the bookmark and then
''# get the Run's "Text" field
runAfterBookmark = curBookMark.NextSibling(Of Wordprocessing.Run)()
textInRun = runAfterBookmark.LastChild
''# Decode the bookmark to a contract attribute
lines = DecodeContractDataToContractDocFields(curBookMark.Name, curContract).Split(vbCrLf)
''# If there are multiple lines returned then some work needs to be done to create
''# the necessary Run/Text fields to hold lines 2 thru n. If just one line then set the
''# Text field to the attribute from the contract
For ptr = 0 To lines.Count - 1
line = lines(ptr)
If ptr = 0 Then
textInRun.Text = line.Trim()
Else
''# Add a <br> run/text component then add next line
newRunForLf = New Run(runAfterBookmark.OuterXml)
newRunForLf.LastChild.Remove()
newBreak = New Break()
newRunForLf.Append(newBreak)
newRunForText = New Run(runAfterBookmark.OuterXml)
DirectCast(newRunForText.LastChild, Text).Text = line.Trim
curBookMark.Parent.Append(newRunForLf)
curBookMark.Parent.Append(newRunForText)
End If
Next
Next
我想通了这一点11分钟前所以请原谅的代码的hackish性质。
首先,我写了一个辅助递归辅助函数来找到所有的书签:
private static Dictionary<string, BookmarkEnd> FindBookmarks(OpenXmlElement documentPart, Dictionary<string, BookmarkEnd> results = null, Dictionary<string, string> unmatched = null)
{
results = results ?? new Dictionary<string, BookmarkEnd>();
unmatched = unmatched ?? new Dictionary<string,string>();
foreach (var child in documentPart.Elements())
{
if (child is BookmarkStart)
{
var bStart = child as BookmarkStart;
unmatched.Add(bStart.Id, bStart.Name);
}
if (child is BookmarkEnd)
{
var bEnd = child as BookmarkEnd;
foreach (var orphanName in unmatched)
{
if (bEnd.Id == orphanName.Key)
results.Add(orphanName.Value, bEnd);
}
}
FindBookmarks(child, results, unmatched);
}
return results;
}
返回我一个解释,我可以使用通过我的更换名单部分和书签后添加文字:
var bookMarks = FindBookmarks(doc.MainDocumentPart.Document);
foreach(var end in bookMarks)
{
var textElement = new Text("asdfasdf");
var runElement = new Run(textElement);
end.Value.InsertAfterSelf(runElement);
}
从我可以告诉插入和更换书签看起来更难。当我使用InsertAt而不是InsertIntoSelf时,我得到:“非复合元素没有子元素。”因人而异
我想我想要做的是使用的开始/结束书签标记,让我选择文本的一部分(运行?),并对其进行修改。它似乎很随机,虽然书签存储虽然,我的都在'doc.MainDocumentPart.Document.Body.Descendants' – 2010-07-23 08:06:20
@John他们是在他们被添加的文档中的地方在树中。根本没有任何关于它的随机。一切都将在Body.Descendants。 Body.Elements只能获得一级孩子。等等,也许我应该只是在寻找后裔... – jfar 2010-07-23 14:35:58
这里是我如何做到这一点和VB添加/替换bookmarkStart和BookmarkEnd之间的文本。
<w:bookmarkStart w:name="forbund_kort" w:id="0" />
- <w:r>
<w:t>forbund_kort</w:t>
</w:r>
<w:bookmarkEnd w:id="0" />
Imports DocumentFormat.OpenXml.Packaging
Imports DocumentFormat.OpenXml.Wordprocessing
Public Class PPWordDocx
Public Sub ChangeBookmarks(ByVal path As String)
Try
Dim doc As WordprocessingDocument = WordprocessingDocument.Open(path, True)
'Read the entire document contents using the GetStream method:
Dim bookmarkMap As IDictionary(Of String, BookmarkStart) = New Dictionary(Of String, BookmarkStart)()
Dim bs As BookmarkStart
For Each bs In doc.MainDocumentPart.RootElement.Descendants(Of BookmarkStart)()
bookmarkMap(bs.Name) = bs
Next
For Each bs In bookmarkMap.Values
Dim bsText As DocumentFormat.OpenXml.OpenXmlElement = bs.NextSibling
If Not bsText Is Nothing Then
If TypeOf bsText Is BookmarkEnd Then
'Add Text element after start bookmark
bs.Parent.InsertAfter(New Run(New Text(bs.Name)), bs)
Else
'Change Bookmark Text
If TypeOf bsText Is Run Then
If bsText.GetFirstChild(Of Text)() Is Nothing Then
bsText.InsertAt(New Text(bs.Name), 0)
End If
bsText.GetFirstChild(Of Text)().Text = bs.Name
End If
End If
End If
Next
doc.MainDocumentPart.RootElement.Save()
doc.Close()
Catch ex As Exception
Throw ex
End Try
End Sub
End Class
将书签替换为单个内容(可能为多个文本块)。
public static void InsertIntoBookmark(BookmarkStart bookmarkStart, string text)
{
OpenXmlElement elem = bookmarkStart.NextSibling();
while (elem != null && !(elem is BookmarkEnd))
{
OpenXmlElement nextElem = elem.NextSibling();
elem.Remove();
elem = nextElem;
}
bookmarkStart.Parent.InsertAfter<Run>(new Run(new Text(text)), bookmarkStart);
}
首先,删除开始和结束之间的现有内容。然后在开始后直接添加新的运行(结束之前)。
但是,如果没有把握书签会当它被打开了另一部分或在不同的表格单元格,等..
关闭对我来说足够了。
请注意,我翻译了这个答案(来自Google的帮助)。请检查它的准确性。将来,请以英文发布。 – 2012-01-12 12:03:03
这是为我工作的一个,只需确保添加以下行以将更改保存回文档file.MainDocumentPart.Document.Save(); file.Close();文件是您使用WordprocessingDocument.Open打开的文件(“path”,true) – 2017-07-27 15:41:52
接受的答案和其他一些人对书签在文档结构中的位置做出了假设。这是我的C#代码,它可以处理替换跨越多个段落的书签和正确替换不以段落边界开始和结束的书签。仍然不完美,但更接近...希望它是有用的。如果你找到更多的方法来改善它,请编辑!
private static void ReplaceBookmarkParagraphs(MainDocumentPart doc, string bookmark, IEnumerable<OpenXmlElement> paras) {
var start = doc.Document.Descendants<BookmarkStart>().Where(x => x.Name == bookmark).First();
var end = doc.Document.Descendants<BookmarkEnd>().Where(x => x.Id.Value == start.Id.Value).First();
OpenXmlElement current = start;
var done = false;
while (!done && current != null) {
OpenXmlElement next;
next = current.NextSibling();
if (next == null) {
var parentNext = current.Parent.NextSibling();
while (!parentNext.HasChildren) {
var toRemove = parentNext;
parentNext = parentNext.NextSibling();
toRemove.Remove();
}
next = current.Parent.NextSibling().FirstChild;
current.Parent.Remove();
}
if (next is BookmarkEnd) {
BookmarkEnd maybeEnd = (BookmarkEnd)next;
if (maybeEnd.Id.Value == start.Id.Value) {
done = true;
}
}
if (current != start) {
current.Remove();
}
current = next;
}
foreach (var p in paras) {
end.Parent.InsertBeforeSelf(p);
}
}
这里是我结束了 - 不是100%完美,但可以用于简单的书签和简单的文本中插入:
private void FillBookmarksUsingOpenXml(string sourceDoc, string destDoc, Dictionary<string, string> bookmarkData)
{
string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
// Make a copy of the template file.
File.Copy(sourceDoc, destDoc, true);
//Open the document as an Open XML package and extract the main document part.
using (WordprocessingDocument wordPackage = WordprocessingDocument.Open(destDoc, true))
{
MainDocumentPart part = wordPackage.MainDocumentPart;
//Setup the namespace manager so you can perform XPath queries
//to search for bookmarks in the part.
NameTable nt = new NameTable();
XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
nsManager.AddNamespace("w", wordmlNamespace);
//Load the part's XML into an XmlDocument instance.
XmlDocument xmlDoc = new XmlDocument(nt);
xmlDoc.Load(part.GetStream());
//Iterate through the bookmarks.
foreach (KeyValuePair<string, string> bookmarkDataVal in bookmarkData)
{
var bookmarks = from bm in part.Document.Body.Descendants<BookmarkStart>()
select bm;
foreach (var bookmark in bookmarks)
{
if (bookmark.Name == bookmarkDataVal.Key)
{
Run bookmarkText = bookmark.NextSibling<Run>();
if (bookmarkText != null) // if the bookmark has text replace it
{
bookmarkText.GetFirstChild<Text>().Text = bookmarkDataVal.Value;
}
else // otherwise append new text immediately after it
{
var parent = bookmark.Parent; // bookmark's parent element
Text text = new Text(bookmarkDataVal.Value);
Run run = new Run(new RunProperties());
run.Append(text);
// insert after bookmark parent
parent.Append(run);
}
//bk.Remove(); // we don't want the bookmark anymore
}
}
}
//Write the changes back to the document part.
xmlDoc.Save(wordPackage.MainDocumentPart.GetStream(FileMode.Create));
}
}
这里大部分的解决方案假设开始之前和之后结束的常规书签模式运行,这并非总是如此,例如如果书签在para或table中开始,并在另一个para的某个地方结束(就像其他人注意到的那样)。如何使用文档顺序来处理书签未放入常规结构的情况 - 文档顺序仍然会找到所有相关的文本节点,然后可以替换它们。只要执行root.DescendantNodes()。其中(xtext或bookmarkstart或书签结束)将按文档顺序遍历,然后可以替换在看到书签开始节点之后但在看到结束节点之前出现的文本节点。
我把代码答案,有几个问题与它的例外情形:
- 您可能要忽略隐藏的书签。如果名称以_(下划线)开头,则隐藏书签
- 如果书签是多一个TableCell的书签,您可以在行的第一个Cell中的BookmarkStart中找到它,并使用属性ColumnFirst引用基于0的书签开始处的单元格的列索引。 ColumnLast引用书签结束的单元格,因为我的特殊情况始终是ColumnFirst == ColumnLast(书签只标记一列)。在这种情况下,你也不会找到一个BookmarkEnd。
- 书签可以是空的,所以BookmarkStart直接遵循BookmarkEnd,在这种情况下,你可以叫
bookmarkStart.Parent.InsertAfter(new Run(new Text("Hello World")), bookmarkStart)
- 另外一个书签可以包含很多文本的元素,所以你可能要删除所有其他元素,否则书签的部分可能会被替换,而其他以下部分将保留。
- 我不确定我的最后一次破解是否有必要,因为我不知道OpenXML的所有限制,但是在发现前面的4之后,我也不再相信会有一个Run的兄弟,与文本的孩子。因此,我只是看看我所有的兄弟姐妹(直到BookmarEnd与BookmarkStart具有相同的ID),然后检查所有孩子,直到找到任何文本。 - 也许有更多OpenXML经验的人可以回答,如果有必要的话?
您可以查看我的具体实施here)
希望这有助于你们中的一些谁经历了同样的问题。
请注意,您应该在此处发布答案的有用点数,或者您的帖子风险被删除为[“未答复”] (http://meta.stackexchange.com/q/8259)。如果您愿意,您可能仍然包含链接,但仅作为“参考”。答案应该独立,不需要链接。 – 2013-02-26 07:18:18
了很多小时后,我写了这个方法:
Public static void ReplaceBookmarkParagraphs(WordprocessingDocument doc, string bookmark, string text)
{
//Find all Paragraph with 'BookmarkStart'
var t = (from el in doc.MainDocumentPart.RootElement.Descendants<BookmarkStart>()
where (el.Name == bookmark) &&
(el.NextSibling<Run>() != null)
select el).First();
//Take ID value
var val = t.Id.Value;
//Find the next sibling 'text'
OpenXmlElement next = t.NextSibling<Run>();
//Set text value
next.GetFirstChild<Text>().Text = text;
//Delete all bookmarkEnd node, until the same ID
deleteElement(next.GetFirstChild<Text>().Parent, next.GetFirstChild<Text>().NextSibling(), val, true);
}
在那之后,我打电话:
Public static bool deleteElement(OpenXmlElement parentElement, OpenXmlElement elem, string id, bool seekParent)
{
bool found = false;
//Loop until I find BookmarkEnd or null element
while (!found && elem != null && (!(elem is BookmarkEnd) || (((BookmarkEnd)elem).Id.Value != id)))
{
if (elem.ChildElements != null && elem.ChildElements.Count > 0)
{
found = deleteElement(elem, elem.FirstChild, id, false);
}
if (!found)
{
OpenXmlElement nextElem = elem.NextSibling();
elem.Remove();
elem = nextElem;
}
}
if (!found)
{
if (elem == null)
{
if (!(parentElement is Body) && seekParent)
{
//Try to find bookmarkEnd in Sibling nodes
found = deleteElement(parentElement.Parent, parentElement.NextSibling(), id, true);
}
}
else
{
if (elem is BookmarkEnd && ((BookmarkEnd)elem).Id.Value == id)
{
found = true;
}
}
}
return found;
}
此代码工作好如果u有没有空书签。 我希望它可以帮助某人。
那是唯一一个为我工作的人。 – 2015-08-11 20:56:03
我需要用表格替换书签的文本(书签名称是“表格”)。这是我的方法:
public void ReplaceBookmark(DatasetToTable(ds))
{
MainDocumentPart mainPart = myDoc.MainDocumentPart;
Body body = mainPart.Document.GetFirstChild<Body>();
var bookmark = body.Descendants<BookmarkStart>()
.Where(o => o.Name == "Table")
.FirstOrDefault();
var parent = bookmark.Parent; //bookmark's parent element
if (ds!=null)
{
parent.InsertAfterSelf(DatasetToTable(ds));
parent.Remove();
}
mainPart.Document.Save();
}
public Table DatasetToTable(DataSet ds)
{
Table table = new Table();
//creating table;
return table;
}
希望这有助于
- 1. 在Word中插入书签 - Open XML SDK
- 2. 使用Open Xml替换Word文档中的文本
- 3. 如何使用Open XML SDK将图像替换为Word文档(docx)中的书签?
- 4. 如何替换Word文档中的书签中的文本
- 5. 使用OpenXml SDK和C++/CLI替换docx文件中的书签
- 6. 用另一个word文档的内容替换Word文档中的书签
- 7. 使用office.js不能替换Word文档中的书签
- 8. Office Open XML SDK字替换
- 9. 是否有替代open-xml sdk来生成word文档
- 10. 用格式化(HTML)文本替换Word文档中的书签范围
- 11. 如何使用C#和Open XML SDK通过特定文本分割Word文档?
- 12. 使用Open XML SDK从Word文档获取特定页面
- 13. 使用Open XML和C读取Word书签#
- 14. 使用OpenXML SDK简单替换Word文档中的标记
- 15. gradle - 替换XML文件中的文本
- 16. java替换word文档书签并转换为pdf
- 17. 使用Word Interop打开文件Word并替换SharePoint 2010中的一些文本
- 18. 使用Sharepoint Word Automation做文本替换
- 19. 使用Novacode替换Word文档中的文本标签与图像
- 20. 的Open XML SDK:如何获得有效的Word文档WordprocessingDocument.Open
- 21. 使用Open Xml SDK和LINQ在Excel中插入文本
- 22. 用Open XML创建Word文档
- 23. 使用Open XML SDK修复Word中的单元格宽度
- 24. 使用PHP替换XML文件中的文本?
- 25. 打开现有的Word文档,编辑其书签并使用Open Xml方法保存为新文档
- 26. 使用OpenXML替换Word 2010文本框中的内容
- 27. 替换XML文本
- 28. 如何使用open xml获取word文档的所有合并域sdk
- 29. 使用Open XML在c#中设置word文档的边距
- 30. 使用OpenXML将文本替换为MS Word中的公式
你在这里遵循一个非常简单的模式,在任何情况下都不起作用。在许多情况下,书签替换会变得复杂得多,而这种算法无法使用。 – Arvand 2013-03-10 13:35:44
这对我不起作用,它不会给我任何错误,我确认它读取书签,但不会将其替换为文本。 – 2017-07-27 15:14:14