2013-12-10 86 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