2013-12-10 87 views
3

我一直试图通过示例FieldMailMergeVariableReplace工作,但似乎无法运行本地测试用例。我基本上试图从一个docx模板文档开始,并让它从替换变量的那个模板创建x个docx文档。Docx4j - 如何用值替换占位符

在下面的代码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

您在此处与VariableReplace在正确的轨道上,但对于FieldMailMerge,您的文档需要包含适当的OpenXML MERGEFIELD结构。请考虑每种方法的单独问题,因为它们完全不同。有关VariableReplace的进一步建议,请解压缩您的输入docx,并在您的问题中包含/word/document.xml部分的内容。如果您从头开始,请考虑内容控制XML数据绑定;其更强大,更强大! – JasonPlutext

+0

我相信我只是想出了为什么。我不是一个Word强大的用户,并没有意识到这是一个你必须创建的领域。我认为这只是一个简单的文本替换切换。我并没有完全意识到差异,所以我为什么要尝试这两种。我将添加一个答案,希望让其他人能够像我一样思考,这是一个必须使用的Word功能,它不仅仅是您键入的文本中的一个简单值,而且还可以提取它。现在我正确理解和欣赏你在代码中的意思! –

回答

3

问题是我试图创建占位符,只是在docx文件内的纯文本。我应该一直在做的是使用Word中的MergeField功能,这是我没有完全理解和欣赏的,因此造成了混淆。基本上我不知道这是文档中的含义,因为我从来没有使用它,我只是假定它仍然是某种xml文本替换。

这就是说,它仍然很难找到这个Word功能的一个很好的解释。看了几十个解释之后,我仍然无法找到这个Word功能的一个很好的干净解释。我能找到的最佳解释是found here。基本上你想要做的第3步。

这就是说,一旦我在Word中创建MergeFields并运行代码,它的工作完美。使用的方法是docx4jReplaceTwoPeopleTest问题不在代码中,而在于我理解Word如何工作。

+1

要找到占位符,您必须使用zip文件管理器打开.docx,您将看到文件夹和文件的结构,文本写入'word \ document.xml'。以防万一你改变主意。 – Math

6

我有同样的问题,当然我不能强制用户在编写Word文档时要做一些额外的工作,所以我决定写一个算法来扫描整个文档,值并在第二次运行中删除表达式。如果其他人可能需要它下面是我所做的。我从某个地方得到了课程,所以它可能很熟悉。我刚刚添加的方法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); 
    } 
} 
+0

3年过去了,但我不能拒绝说谢谢!我认为这是该任务的最佳实现,因为不仅合并字段需要用户的一些努力,而且它们(如文本)由于某种原因有时会分裂成若干指令,doc4j与NPE一起落实。不想提及数据绑定内容控件,因为它们绝对不适合普通用户。用这种方法,一切都像魅力一样运作! – mykola