我認爲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