我一直在努力完成示例FieldMailMerge和VariableReplace,但似乎无法运行本地测试用例。我基本上试图从一个docx模板文档开始,让它从那个模板中创建x docx文档并替换变量。
在下面的代码中docx4jReplaceSimpleTest()
尝试替换单个变量但未能这样做。模板文件中的$ {}值将作为处理的一部分被删除,因此我相信它正在找到它们但不会因某些原因替换它们。我理解这可能是由于格式化,如示例代码的注释中所解释的那样,但是为了解决问题只是为了使某些工作正在进行中我正在尝试它。
在下面的代码docx4jReplaceTwoPeopleTest()
中,我想要工作的那个,我正在尝试用我认为正确的方式来做,但那不是找到或替换任何东西。它甚至没有从docx文件中删除$ {}。
public static void main(String[] args) throws Exception
{
docx4jReplaceTwoPeopleTest();
docx4jReplaceSimpleTest();
}
private static void docx4jReplaceTwoPeopleTest() throws Exception
{
String docxFile = "C:/temp/template.docx";
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new java.io.File(docxFile));
List<Map<DataFieldName, String>> data = new ArrayList<Map<DataFieldName, String>>();
Map<DataFieldName, String> map1 = new HashMap<DataFieldName, String>();
map1.put(new DataFieldName("Person.Firstname"), "myFirstname");
map1.put(new DataFieldName("Person.Lastname"), "myLastname");
data.add(map1);
Map<DataFieldName, String> map2 = new HashMap<DataFieldName, String>();
map2.put(new DataFieldName("Person.Firstname"), "myFriendsFirstname");
map2.put(new DataFieldName("Person.Lastname"), "myFriendsLastname");
data.add(map2);
org.docx4j.model.fields.merge.MailMerger.setMERGEFIELDInOutput(OutputField.KEEP_MERGEFIELD);
int x=0;
for(Map<DataFieldName, String> docMapping: data)
{
org.docx4j.model.fields.merge.MailMerger.performMerge(wordMLPackage, docMapping, true);
wordMLPackage.save(new java.io.File("C:/temp/OUT__MAIL_MERGE_" + x++ + ".docx") );
}
}
private static void docx4jReplaceSimpleTest() throws Exception
{
String docxFile = "C:/temp/template.docx";
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new java.io.File(docxFile));
HashMap<String, String> mappings = new HashMap<String, String>();
mappings.put("Person.Firstname", "myFirstname");
mappings.put("Person.Lastname", "myLastname");
MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
documentPart.variableReplace(mappings);
wordMLPackage.save(new java.io.File("C:/temp/OUT_SIMPLE.docx") );
}
docx文件包含以下文本(未完成格式化):
This is a letter to someone
Hi ${Person.Firstname} ${Person.Lastname},
How are you?
Thank you again. I wish to see you soon ${Person.Firstname}
Regards,
Someone
请注意,我也尝试将Person.Firstname替换为至少两次。由于姓氏甚至没有被替换,我不认为这与它有任何关系,但我是为了以防万一而添加它。
答案 0 :(得分:7)
我遇到了同样的问题,当然我不能强迫用户在编写单词文档时做一些额外的事情,所以我决定只编写一个算法来扫描整个文档中的表达式,附加运行后运行,插入替换值和在第二次运行中删除表达式。如果其他人可能需要它,我就是这么做的。我从某个地方上课,所以它可能很熟悉。我刚刚添加了方法searchAndReplace()
package com.my.docx4j;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.Text;
public class Docx4j {
public static void main(String[] args) throws Docx4JException, IOException, JAXBException {
String filePath = "C:\\Users\\markamm\\Documents\\tmp\\";
String file = "Hello.docx";
Docx4j docx4j = new Docx4j();
WordprocessingMLPackage template = docx4j.getTemplate(filePath+file);
// MainDocumentPart documentPart = template.getMainDocumentPart();
List<Object> texts = getAllElementFromObject(
template.getMainDocumentPart(), Text.class);
searchAndReplace(texts, new HashMap<String, String>(){
{
this.put("${abcd_efg.soanother_hello_broken_shit}", "Company Name here...");
this.put("${I_dont_know}", "Hmmm lemme see");
this.put("${${damn.right_lol}", "Gotcha!!!");
this.put("${one_here_and}", "Firstname");
this.put("${one}", "ChildA");
this.put("${two}", "ChildB");
this.put("${three}", "ChildC");
}
@Override
public String get(Object key) {
// TODO Auto-generated method stub
return super.get(key);
}
});
docx4j.writeDocxToStream(template, filePath+"Hello2.docx");
}
public static void searchAndReplace(List<Object> texts, Map<String, String> values){
// -- scan all expressions
// Will later contain all the expressions used though not used at the moment
List<String> els = new ArrayList<String>();
StringBuilder sb = new StringBuilder();
int PASS = 0;
int PREPARE = 1;
int READ = 2;
int mode = PASS;
// to nullify
List<int[]> toNullify = new ArrayList<int[]>();
int[] currentNullifyProps = new int[4];
// Do scan of els and immediately insert value
for(int i = 0; i<texts.size(); i++){
Object text = texts.get(i);
Text textElement = (Text) text;
String newVal = "";
String v = textElement.getValue();
// System.out.println("text: "+v);
StringBuilder textSofar = new StringBuilder();
int extra = 0;
char[] vchars = v.toCharArray();
for(int col = 0; col<vchars.length; col++){
char c = vchars[col];
textSofar.append(c);
switch(c){
case '$': {
mode=PREPARE;
sb.append(c);
// extra = 0;
} break;
case '{': {
if(mode==PREPARE){
sb.append(c);
mode=READ;
currentNullifyProps[0]=i;
currentNullifyProps[1]=col+extra-1;
System.out.println("extra-- "+extra);
} else {
if(mode==READ){
// consecutive opening curl found. just read it
// but supposedly throw error
sb = new StringBuilder();
mode=PASS;
}
}
} break;
case '}': {
if(mode==READ){
mode=PASS;
sb.append(c);
els.add(sb.toString());
newVal +=textSofar.toString()
+(null==values.get(sb.toString())?sb.toString():values.get(sb.toString()));
textSofar = new StringBuilder();
currentNullifyProps[2]=i;
currentNullifyProps[3]=col+extra;
toNullify.add(currentNullifyProps);
currentNullifyProps = new int[4];
extra += sb.toString().length();
sb = new StringBuilder();
} else if(mode==PREPARE){
mode = PASS;
sb = new StringBuilder();
}
}
default: {
if(mode==READ) sb.append(c);
else if(mode==PREPARE){
mode=PASS;
sb = new StringBuilder();
}
}
}
}
newVal +=textSofar.toString();
textElement.setValue(newVal);
}
// remove original expressions
if(toNullify.size()>0)
for(int i = 0; i<texts.size(); i++){
if(toNullify.size()==0) break;
currentNullifyProps = toNullify.get(0);
Object text = texts.get(i);
Text textElement = (Text) text;
String v = textElement.getValue();
StringBuilder nvalSB = new StringBuilder();
char[] textChars = v.toCharArray();
for(int j = 0; j<textChars.length; j++){
char c = textChars[j];
if(null==currentNullifyProps) {
nvalSB.append(c);
continue;
}
// I know 100000 is too much!!! And so what???
int floor = currentNullifyProps[0]*100000+currentNullifyProps[1];
int ceil = currentNullifyProps[2]*100000+currentNullifyProps[3];
int head = i*100000+j;
if(!(head>=floor && head<=ceil)){
nvalSB.append(c);
}
if(j>currentNullifyProps[3] && i>=currentNullifyProps[2]){
toNullify.remove(0);
if(toNullify.size()==0) {
currentNullifyProps = null;
continue;
}
currentNullifyProps = toNullify.get(0);
}
}
textElement.setValue(nvalSB.toString());
}
}
private WordprocessingMLPackage getTemplate(String name)
throws Docx4JException, FileNotFoundException {
WordprocessingMLPackage template = WordprocessingMLPackage
.load(new FileInputStream(new File(name)));
return template;
}
private static List<Object> getAllElementFromObject(Object obj,
Class<?> toSearch) {
List<Object> result = new ArrayList<Object>();
if (obj instanceof JAXBElement)
obj = ((JAXBElement<?>) obj).getValue();
if (obj.getClass().equals(toSearch))
result.add(obj);
else if (obj instanceof ContentAccessor) {
List<?> children = ((ContentAccessor) obj).getContent();
for (Object child : children) {
result.addAll(getAllElementFromObject(child, toSearch));
}
}
return result;
}
private void replacePlaceholder(WordprocessingMLPackage template,
String name, String placeholder) {
List<Object> texts = getAllElementFromObject(
template.getMainDocumentPart(), Text.class);
for (Object text : texts) {
Text textElement = (Text) text;
if (textElement.getValue().equals(placeholder)) {
textElement.setValue(name);
}
}
}
private void writeDocxToStream(WordprocessingMLPackage template,
String target) throws IOException, Docx4JException {
File f = new File(target);
template.save(f);
}
}
答案 1 :(得分:3)
问题是我试图在docx文件中创建占位符只是纯文本。我应该做的是使用Word中的MergeField功能,我并不完全理解和欣赏,因此存在混淆。基本上我不知道这是文档中的意思,因为我从来没有使用它,我只是假设它仍然是某种xml文本替换。
话虽如此,找到这个Word功能的一个很好的解释仍然相当困难。看了几十个解释后,我仍然找不到这个Word功能的清晰解释。我能找到的最佳解释可以是found here。基本上你想做第3步。
话虽这么说,一旦我在Word中创建MergeField并运行代码,它就能完美运行。使用的方法是docx4jReplaceTwoPeopleTest
。 问题不在于代码,而在于我对Word中的工作原理的理解。