Supongo que v2.0 es mejor ... tienen algunos buenos "cómo: ..." examples pero los marcadores no parecen actuar tan obviamente como una tabla ... un marcador está definido por dos elementos XML BookmarkStart & BookmarkEnd. Tenemos algunas plantillas con texto como marcadores y simplemente queremos reemplazar los marcadores con algún otro texto ... no está ocurriendo ningún formato extraño, pero ¿cómo selecciono/sustituyo el texto del marcador?Reemplazar el marcador de texto en el archivo de Word utilizando Open XML SDK
Respuesta
Aquí está mi enfoque después de usar ustedes como fuente de inspiración:
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";
}
}
Aquí es cómo lo hago en 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
acabo cuenta de esto hace 10 minutos por lo que valga la naturaleza hacker del código.
En primer lugar me escribió una función auxiliar recursiva de ayuda para encontrar todos los marcadores:
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;
}
Esto me devuelve un diccionario que puedo usar a parte a través de mi lista de reemplazo y añadir el texto después del marcador:
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);
}
Por lo que puedo decir, la inserción y el reemplazo de los marcadores se ven con más fuerza. Cuando utilicé InsertAt en lugar de InsertIntoSelf, obtuve: "Los elementos no compuestos no tienen elementos secundarios". YMMV
supongo que lo que yo quiero hacer es marcar las etiquetas utilización de inicio/fin de dejar a seleccionar una parte del texto (una carrera?) Y modificarlo. Parece bastante aleatorio donde están almacenados los marcadores, los míos están todos en 'doc.MainDocumentPart.Document.Body.Descendants' –
@John Están dentro del árbol en el lugar del documento donde fueron agregados. Nada al azar al respecto. Todo va a estar en Body.Descendants. Cuerpo.Los elementos solo reciben niños de primer nivel. Espera, tal vez debería estar buscando Descendientes ... – jfar
Aquí es cómo lo hago y VB para añadir/reemplazar texto entre bookmarkstart y 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
Reemplazar marcadores con un solo contenido (posiblemente varios bloques de texto).
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);
}
En primer lugar, se elimina el contenido existente entre el inicio y el final. Luego se agrega una nueva ejecución directamente detrás del inicio (antes del final).
Sin embargo, no estoy seguro si el marcador se cerró en otra sección cuando fue abierto o en diferentes celdas de la tabla, etc ..
para mí es suficiente por ahora.
Nota, he traducido esta respuesta (con un _lot_ de ayuda de Google). Por favor verifíquelo para exactitud. En el futuro, publique en inglés. –
Este es el que funcionó para mí, solo asegúrese de agregar las siguientes líneas para guardar los cambios en su documento, archivo.MainDocumentPart.Document.Save(); file.Close(); archivo es el archivo que abrió con WordprocessingDocument.Open ("ruta", verdadero) –
La respuesta aceptada y algunas de las otras hacen suposiciones sobre dónde están los marcadores en la estructura del documento. Aquí está mi código C#, que puede ocuparse de reemplazar marcadores que se extienden a través de múltiples párrafos y, reemplaza correctamente los marcadores que no comienzan y terminan en los límites de los párrafos. Todavía no es perfecto, pero está más cerca ... espero que sea útil. ¡Edita si encuentras más formas de mejorarlo!
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);
}
}
Esto es lo que terminó con - no es 100% perfecto, pero funciona para marcadores simples y texto simple para insertar:
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));
}
}
mayoría de las soluciones siguientes asumen que un patrón de marcadores regular de comenzar antes y termina después de se ejecuta, lo que no siempre es cierto, por ejemplo si el marcador comienza en un párrafo o una tabla y termina en algún lugar de otro párrafo (como otros han notado).¿Qué hay de usar el orden de los documentos para lidiar con el caso en el que los marcadores no se colocan en una estructura regular? El orden del documento aún encontrará todos los nodos de texto relevantes entre los cuales se puede reemplazar. Solo haga root.DescendantNodes(). Where (xtext o bookmarkstart o bookmark end) que recorrerá en orden de documento, entonces uno puede reemplazar los nodos de texto que aparecen después de ver un nodo de inicio de marcador pero antes de ver un nodo final.
Tomé el código de la respuesta, y tuvo varios problemas con él para casos excepcionales:
- Es posible que desee hacer caso omiso de marcadores ocultos. Los marcadores están ocultos si el nombre comienza con un _ (guión bajo)
- Si el marcador es para uno más más TableCell, lo encontrará en el BookmarkStart en la primera celda de la fila con la propiedad ColumnFirst referida al 0 índice de columna de la celda donde comienza el marcador. ColumnLast hace referencia a la celda donde termina el marcador, para mi caso especial siempre fue ColumnFirst == ColumnLast (los marcadores marcaron solo una columna). En este caso, tampoco encontrará un BookmarkEnd.
- Los marcadores pueden estar vacía, por lo que un bookmarkstart se retoma la bookmarkend, en este caso sólo se puede llamar
bookmarkStart.Parent.InsertAfter(new Run(new Text("Hello World")), bookmarkStart)
- también un marcador puede contener muchos de texto-elementos, por lo que es posible que desee quitar todos los demás elementos, de lo contrario partes del marcador podrían ser reemplazadas, mientras que otras partes siguientes permanecerán.
- Y no estoy seguro si mi último truco es necesario, ya que no conozco todas las limitaciones de OpenXML, pero después de descubrir las 4 anteriores, tampoco confiaba más en que haya un hermano de Run , con un hijo de Texto. Así que, en cambio, simplemente miro a todos mis hermanos (hasta BookmarEnd, que tiene la misma identificación que BookmarkStart) y reviso a todos los niños hasta que encuentre cualquier texto. - ¿Tal vez alguien con más experiencia con OpenXML puede responder si es necesario?
Usted puede ver mi aplicación específica here)
Espero que esto ayude a algunos de ustedes que han experimentado los mismos problemas.
Tenga en cuenta que debe publicar los puntos útiles de una respuesta aquí, en este sitio, o su publicación corre el riesgo de ser eliminada como ["No es respuesta"] (http://meta.stackexchange.com/q/8259). Puede incluir el enlace si lo desea, pero solo como una "referencia". La respuesta debería ser independiente sin necesidad del enlace. –
Después de muchas horas, he escrito este método:
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);
}
Después de eso, llamo:
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;
}
Este código está trabajando bueno si no tienen Marcadores vacías. Espero que pueda ayudar a alguien.
Ese fue el único que funcionó para mí. –
Necesitaba reemplazar el texto de un marcador (el nombre de los marcadores es "Tabla") con una tabla. Este es mi enfoque:
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;
}
Esperanza esto ayuda
- 1. Diff 2 Open XML Word Documents
- 2. ¿Cómo puedo recuperar imágenes de un archivo .pptx utilizando MS Open XML SDK?
- 3. Word/OpenXML - ¿Cómo creo un marcador oculto?
- 4. ¿Cómo reemplazar el contenido en documento docx de plantilla y Open XML SDK 2.0 (09 de agosto)?
- 5. Word 2007, Open XML: incrustación de una imagen
- 6. iPhone - "Open In" en SDK?
- 7. ¿Cómo reemplazar el texto en el archivo de texto usando el script del archivo bat?
- 8. Reemplazar el bloque delimitado de texto en el archivo con el contenido de otro archivo
- 9. Reemplazar el texto interno de un nodo/elemento Xml
- 10. Reemplazar propiedad en un archivo XML utilizando ANT
- 11. ¿Códigos de ejemplo para principiantes para Open XML SDK?
- 12. Incrustar contenidos de un archivo RTF en un archivo DOCX utilizando OpenXML SDK
- 13. ¿cómo puedo reemplazar el archivo XML en APK?
- 14. leer el archivo XML utilizando LINQ
- 15. Open XML SDK - Guardar un archivo de plantilla (.xltx a .xlsx)
- 16. Reemplazar el texto seleccionado en el área de texto
- 17. Reemplazar cadena en un archivo de texto en node.js
- 18. Comprobación de texto de marcador de posición utilizando Selenium Webdriver
- 19. PHP open gzipped XML
- 20. Cómo realizar un búfer y una corriente de forma eficaz en Open XML SDK
- 21. Open Source/Free Social SDK SDK
- 22. leyendo docx (Office Open XML) en PHP
- 23. Abrir XML SDK para editar el documento activo
- 24. documento Duplicación Word utilizando OpenXml y C#
- 25. Open XML SDK 2.0: ¿cómo actualizar una celda en una hoja de cálculo?
- 26. Cómo reemplazar el texto predeterminado en MATLAB
- 27. Formatee programáticamente una fecha en una hoja de Excel usando Office Open Xml SDK
- 28. cómo leer el archivo XML de la URL utilizando php
- 29. Reemplazar cadena en el archivo
- 30. Open XML Paragraph Spacing
estás siguiendo un patrón muy simple aquí que no funcionará en todos los casos. En muchos casos, el reemplazo de marcadores se vuelve mucho más complicado, lo que no funcionará con este algoritmo. – Arvand
Esto no funciona para mí, no me da ningún error y confirmo que está leyendo los marcadores pero no reemplazándolos con el texto. –