怎么操作docx4j查找和替换
问题描述
我有一些占位符的docx文档。现在,我应该用其他内容替换它们并保存新的docx文档。我从docx4j开始,发现了这种方法:
public static List
但是这种方法很少起作用,因为通常占位符会分割成多个文本运行。
我尝试过UnmarshallFromTemplate,但它也很少起作用。
如何解决此问题?
思路一:
您可以使用VariableReplace
实现这一点,而在其他答案时可能还不存在。此操作本身并不执行查找/替换,但可用于占位符,例如$(myField)
java.util.HashMap mappings = new java.util.HashMap();
VariablePrepare.prepare(wordMLPackage);//see notes
mappings.put("myField", "foo");
wordMLPackage.getMainDocumentPart().variableReplace(mappings);
请注意,您不要将$(myField)
作为字段名传递;而不是传递未转义的字段名称myField
-这相当不灵活,因为按目前的样子,您的占位符必须为$(xyz)
格式,而如果您可以传递任何内容,则可以将其用于任何查找/替换。 docx4j.NET中的C#人员也可以使用此功能
请参阅here以获取有关VariableReplace
的更多信息或here以获取VariablePrepare
的更多信息
思路二:
[美好的一天,我举了一个例子,如何快速将文本替换为您需要的内容通过正则表达式。我找到$ {param.sumname}并将其替换为文档。注意,您必须将文本插入为“仅文本”!玩得开心!
WordprocessingMLPackage mlp = WordprocessingMLPackage.load(new File("filepath"));
replaceText(mlp.getMainDocumentPart());
static void replaceText(ContentAccessor c)
throws Exception
{
for (Object p: c.getContent())
{
if (p instanceof ContentAccessor)
replaceText((ContentAccessor) p);
else if (p instanceof JAXBElement)
{
Object v = ((JAXBElement) p).getValue();
if (v instanceof ContentAccessor)
replaceText((ContentAccessor) v);
else if (v instanceof org.docx4j.wml.Text)
{
org.docx4j.wml.Text t = (org.docx4j.wml.Text) v;
String text = t.getValue();
if (text != null)
{
t.setSpace("preserve"); // needed?
t.setValue(replaceParams(text));
}
}
}
}
}
static Pattern paramPatern = Pattern.compile("(?i)(\\$\\{([\\w\\.]+)\\})");
static String replaceParams(String text)
{
Matcher m = paramPatern.matcher(text);
if (!m.find())
return text;
StringBuffer sb = new StringBuffer();
String param, replacement;
do
{
param = m.group(2);
if (param != null)
{
replacement = getParamValue(param);
m.appendReplacement(sb, replacement);
}
else
m.appendReplacement(sb, "");
}
while (m.find());
m.appendTail(sb);
return sb.toString();
}
static String getParamValue(String name)
{
// replace from map or something else
return name;
}
思路三:
这可能是个问题。我在此答案中介绍了如何缓解拆分文本的问题:https://stackoverflow.com/a/17066582/125750
...,但是您可能想考虑使用内容控件。 docx4j源站点在此处具有各种内容控制示例:
https://github.com/plutext/docx4j/tree/master/src/samples/docx4j/org/docx4j/samples
思路四:
我解决了这样的问题:
第一步:
// (this method was part of your question)
List getAllElementFromObject(docxDocument.getMainDocumentPart(), Text.class);
结果问题:搜索文本/占位符可以根据多个文本实例进行拆分(因为之间可能看不见样式标记),例如${FOOBAR}
,${
+ FOOBAR}
或$
+{FOOB
+ AR}
第二步:
将所有文本对象连接为完整字符串/“完整字符串”
Optional completeStringOpt = texts.stream().map(Text::getValue).reduce(String::concat);
第三步:
创建一个类TextMetaItem
。每个TextMetaItem都知道它的文本对象的内容在哪里开始,并在完整字符串中结束。例如。如果“ foo”和“ bar”的文本对象产生完整的字符串“ foobar”,则索引0-2
属于"foo"-Text-object
,3-5
至"bar"-Text-object
。建立List
static List buildMetaItemList(List texts) {
final int[] index = {0};
final int[] iteration = {0};
List list = new ArrayList();
texts.forEach(text -> {
int length = text.getValue().length();
list.add(new TextMetaItem(index[0], index[0] + length - 1, text, iteration[0]));
index[0] += length;
iteration[0]++;
});
return list;
}
第四步:
建立Map
,其中键是完整字符串中的索引/字符。这意味着地图的长度等于completeString.length()
static Map buildStringIndicesToTextMetaItemMap(List texts) {
List metaItemList = buildMetaItemList(texts);
Map map = new TreeMap();
int currentStringIndicesToTextIndex = 0;
// + 1 important here!
int max = metaItemList.get(metaItemList.size() - 1).getEnd() + 1;
for (int i = 0; i = currentTextMetaItem.getEnd()) {
currentStringIndicesToTextIndex++;
}
}
return map;
}
中期结果:
现在您有足够的元数据可将要对完整字符串执行的每个操作委派给相应的Text对象! (要更改文本对象的内容,您只需要调用(#setValue())这就是Docx4J编辑文本所需的全部。所有样式信息等都将保留!
最后一步:搜索和替换
- 构建一种方法,查找所有可能的占位符。您应该创建一个类似于
FoundMetaItem(int start, ind end)
的类,该类在完整的字符串中存储找到的值(占位符)的开始和结束索引 - 您必须从最后一项到第一项对该列表进行排序(按字符串中的位置排序)
-
现在您可以编写一个全部替换算法,因为您知道需要对哪个Text对象执行什么操作。我们这样做了(2),以便替换操作不会使其他
FoundMetaItems
的索引无效。3.1。)查找需要更改的文本对象3.2。)在它们上调用getValue()3.3。)将字符串编辑为新值3.4。)在文本对象上调用setValue()
以上内容就是爱站技术频道小编为大家分享的怎么操作docx4j查找和替换,看完以上分享之后,大家应该都知道怎么操作docx4j查找和替换了吧。