Docx4j - 如何用值替换占位符 [英] Docx4j - How to replace placeholder with value

查看:963
本文介绍了Docx4j - 如何用值替换占位符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在努力解决这些例子 FieldMailMerge VariableReplace 但似乎无法运行本地测试用例。我基本上试图从一个docx模板文档开始,让它从一个模板中创建x docx文档并替换变量。

I've been trying to work through the examples FieldMailMerge and VariableReplace but can't seem to get a local test case running. I'm basically trying to start with one docx template document and have it create x docx documents from that one template with the variables replaced.

在下面的代码中 docx4jReplaceSimpleTest()尝试替换单个变量但未能这样做。模板文件中的$ {}值将作为处理的一部分被删除,因此我相信它正在找到它们但不会因某些原因替换它们。据我所知,这可能是由于示例代码的注释中所解释的格式化,但是为了让事情正常进行故障排除,我仍在尝试它。

In the code below docx4jReplaceSimpleTest() tries to replace a single variable but fails to do so. The ${} values in the template files are removed as part of the processing therefore I believe it's finding them but not replacing them for some reason. I understand it could be due to formatting as explained in the comments of the sample code but for troubleshooting just to get something working I'm trying it anyways.

在下面的代码 docx4jReplaceTwoPeopleTest()中,我想要工作的那个,我正在尝试做我认为这是正确的方式,但这不是找到或取代任何东西。它甚至没有从docx文件中删除$ {}。

In the code below docx4jReplaceTwoPeopleTest(), the one I want to get working, I'm trying to do it in what I believe is the proper way, but that's not finding or replacing anything. It's not even removing the ${} from the docx file.

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文件包含以下文本(未进行格式化):

The docx file consists of the following text (no formatting is done):

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替换为至少两次。由于姓氏甚至没有被替换,我不认为这与它有任何关系,但我是为了以防万一而添加它。

Notice that I'm also trying to replace Person.Firstname at least twice as well. As the lastname is not even replaced I don't think this has anything to do with it but I'm adding it just in case.

推荐答案

我有同样的问题,当然我不能强迫用户在编写word文档时做一些额外的事情,所以我决定只编写一个算法来扫描整个文档中的表达式追加运行后插入,插入替换值并在第二次运行中删除表达式。如果其他人可能需要它,我就是这么做的。我从某个地方上课,所以它可能很熟悉。我刚刚添加了方法searchAndReplace()

I had the same issue and of course I could not force user to do some extra stuff when composing their word document so I decided to just write an algo to scan the whole document for expressions appending run after run, inserting replacement value and remove expressions in the second run. In case other people may need it below is what I did. I got the class from somewhere so it may be familiar. I just added the method 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);
    }
}

这篇关于Docx4j - 如何用值替换占位符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆