java替换word模板中占位符Docx4j和Poi实现

有时候,不是因为你没有能力,也不是因为你缺少勇气,只是因为你付出的努力还太少,所以,成功便不会走向你。而你所需要做的,就是坚定你的梦想,你的目标,你的未来,然后以不达目的誓不罢休的那股劲,去付出你的努力,成功就会慢慢向你靠近。

导读:本篇文章讲解 java替换word模板中占位符Docx4j和Poi实现,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

word模板动态替换占位符(eg:${placeholder})然后生成新的word

网上搜索的不管是docx4j还是poi都只是实现了占位符在同一个文本中(读取word行数据后,行数据会分为多个文本)的替换,针对占位符没有在同一个文本或者换行了都没有实现,总结docx4j和poi两种方式终极实现占位符替换生成新word,两种方式源码如下

1、Docx4J实现代码


import cn.hutool.core.util.ObjectUtil;
import com.google.common.collect.Lists;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.Text;
import org.junit.Test;

import javax.xml.bind.JAXBElement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author liuchao
 * @date 2020/6/8
 */
public class Test01 {

    /**
     * 设置最大Text类型节点个数 如果超过此值,在删除占位符时可能会重复计算导致错误
     */
    private static int MAX_TEXT_SIZE = 1000000;

    @Test
    public void test() throws Exception {
        String docxFile = "/Users/liuchao/java/temp/1.docx";
        WordprocessingMLPackage template = WordprocessingMLPackage.load(new java.io.File(docxFile));
        List<Object> texts = getAllElementFromObject(
                template.getMainDocumentPart(), Text.class);
        Map<String, String> map = new HashMap<>();
        map.put("${company}", "Company Name here...");
        map.put("${address}", "xxxfffadfasdf");
        map.put("${secondParty}", "xxxfffadfasdf");
        map.put("${year}", "xxxfffadfasdf");
        map.put("${month}", "xxxfffadfasdf");
        map.put("${day}", "xxxfffadfasdf");
        map.put("${money}", "1000");
        searchAndReplace(texts, map);
        template.save(new java.io.File("/Users/liuchao/java/temp/NEW.docx"));
    }


    /**
     * 递归获取所有的节点
     *
     * @param obj      当前文档
     * @param toSearch 要查询的节点类型
     * @return java.util.List<java.lang.Object>
     * @author liuchao
     * @date 2020/6/9
     */
    private static List<Object> getAllElementFromObject(Object obj,
                                                        Class<?> toSearch) {
        List<Object> result = Lists.newArrayListWithCapacity(60);
        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;
    }

    /**
     * 查找并且替换占位符
     *
     * @param texts  当前文档所有的Text类型节点
     * @param values 要替换的占位符key\value
     * @return void
     * @author liuchao
     * @date 2020/6/9
     */
    public static void searchAndReplace(List<Object> texts, Map<String, String> values) {
        // 存储占位符 位置信息集合
        List<int[]> placeholderList = getPlaceholderList(texts, values);
        if (ObjectUtil.isEmpty(placeholderList)) {
            return;
        }
        int[] currentPlaceholder;
        // 删除元素占位符
        for (int i = 0; i < texts.size(); i++) {
            if (ObjectUtil.isEmpty(placeholderList)) {
                break;
            }
            currentPlaceholder = placeholderList.get(0);
            Text textElement = (Text) texts.get(i);
            String v = textElement.getValue();
            StringBuilder nval = new StringBuilder();
            char[] textChars = v.toCharArray();
            for (int j = 0; j < textChars.length; j++) {
                char c = textChars[j];
                if (null == currentPlaceholder) {
                    nval.append(c);
                    continue;
                }
                // 计算是否需要排除当前节点
                int start = currentPlaceholder[0] * MAX_TEXT_SIZE + currentPlaceholder[1];
                int end = currentPlaceholder[2] * MAX_TEXT_SIZE + currentPlaceholder[3];
                int cur = i * MAX_TEXT_SIZE + j;
                // 排除'$'和'}'两个字符之间的字符
                if (!(cur >= start && cur <= end)) {
                    nval.append(c);
                }

                if (j > currentPlaceholder[3] && i >= currentPlaceholder[2]) {
                    placeholderList.remove(0);
                    if (ObjectUtil.isEmpty(placeholderList)) {
                        currentPlaceholder = null;
                        continue;
                    }
                    currentPlaceholder = placeholderList.get(0);
                }
            }
            textElement.setValue(nval.toString());
        }
    }

    /**
     * 获取占位符信息,并且在占位符后面填充值
     *
     * @param texts  Text类型节点集合
     * @param values 要替换的占位符key\value
     * @return java.util.List<int [ ]>
     * @author liuchao
     * @date 2020/6/9
     */
    public static List<int[]> getPlaceholderList(List<Object> texts, Map<String, String> values) {
        // 标识忽略
        int ignoreTg = 0;
        // 标识已读取到'$'字符
        int startTg = 1;
        // 标识已读取到'{'字符
        int readTg = 2;
        // 当前标识
        int modeTg = ignoreTg;

        // 存储占位符 位置信息集合
        List<int[]> placeholderList = new ArrayList<>();
        // 当前占位符 0:'$'字符Text在texts中下标
        //          1:'$'字符在Text.getValue().toCharArray()数组下标
        //          2: '}'字符Text在texts中下标
        //          3:'}'字符在Text.getValue().toCharArray()数组下标
        int[] currentPlaceholder = new int[4];
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < texts.size(); i++) {
            Text textElement = (Text) texts.get(i);
            String newVal = "";
            String text = textElement.getValue();
            StringBuilder textSofar = new StringBuilder();
            char[] textChars = text.toCharArray();
            for (int col = 0; col < textChars.length; col++) {
                char c = textChars[col];
                textSofar.append(c);
                switch (c) {
                    case '$': {
                        modeTg = startTg;
                        sb.append(c);
                    }
                    break;
                    case '{': {
                        if (modeTg == startTg) {
                            sb.append(c);
                            modeTg = readTg;
                            currentPlaceholder[0] = i;
                            currentPlaceholder[1] = col - 1;
                        } else {
                            if (modeTg == readTg) {
                                sb = new StringBuilder();
                                modeTg = ignoreTg;
                            }
                        }
                    }
                    break;
                    case '}': {
                        if (modeTg == readTg) {
                            modeTg = ignoreTg;
                            sb.append(c);
                            newVal += textSofar.toString()
                                    + (null == values.get(sb.toString()) ? sb.toString() : values.get(sb.toString()));
                            textSofar = new StringBuilder();
                            currentPlaceholder[2] = i;
                            currentPlaceholder[3] = col;
                            placeholderList.add(currentPlaceholder);
                            currentPlaceholder = new int[4];
                            sb = new StringBuilder();
                        } else if (modeTg == startTg) {
                            modeTg = ignoreTg;
                            sb = new StringBuilder();
                        }
                    }
                    default: {
                        if (modeTg == readTg) {
                            sb.append(c);
                        } else if (modeTg == startTg) {
                            modeTg = ignoreTg;
                            sb = new StringBuilder();
                        }
                    }
                }
            }
            newVal += textSofar.toString();
            textElement.setValue(newVal);
        }
        return placeholderList;
    }

依赖jar

        <dependency>
            <groupId>org.docx4j</groupId>
            <artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
            <version>8.1.7</version>
        </dependency>
        <!--hutool工具类-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.6.0</version>
        </dependency>
        <!--json格式化-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.60</version>
        </dependency>  

2、Poi实现代码


import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.junit.Test;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author liuchao
 * @date 2020/6/9
 */
public class Test03 {

    // 标识忽略
    static final int ignoreTg = 0;
    // 标识已读取到'$'字符
    static final int startTg = 1;
    // 标识已读取到'{'字符
    static final int readTg = 2;

    @Test
    public void test() throws Exception {
        Map<String, String> map = new HashMap<>();
        map.put("${company}", "Company Name here...");
        map.put("${address}", "xxxfffadfasdf");
        map.put("${secondParty}", "xxxfffadfasdf");
        map.put("${year}", "xxxfffadfasdf");
        map.put("${month}", "xxxfffadfasdf");
        map.put("${day}", "xxxfffadfasdf");
        map.put("${money}", "1000");

        XWPFDocument doc = new XWPFDocument(new FileInputStream(new File("/Users/liuchao/java/temp/1.docx")));
        List<XWPFParagraph> paragraphList = Lists.newArrayList(doc.getParagraphs());
        // 获取表格中的占位符信息
        getTableParagraphs(doc, paragraphList, placeholderValues);
        //获取占位符,并且将占位符需要替换的值写入
        List<int[]> placeholderList = getPlaceholderList(paragraphList, map);
        //清除占位符信息
        clearPlaceholder(placeholderList, paragraphList);
        BufferedOutputStream bos = FileUtil.getOutputStream(new File("/Users/liuchao/java/temp/NEW.docx"));
        doc.write(bos);
        bos.flush();
        bos.close();
        doc.close();
    }


    /**
     * 获取表格中占位符信息
     *
     * @param doc               当前文档
     * @param list              存储占位符信息
     * @param placeholderValues 需要替换的值
     * @return void
     * @author liuchao
     * @date 2020/7/6
     */
    private void getTableParagraphs(XWPFDocument doc, List<XWPFParagraph> list, Map<String, String> placeholderValues) {
        List<XWPFTable> tables = doc.getTables();
        if (ObjectUtil.isEmpty(tables)) {
            return;
        }
        tables.forEach(table -> table.getRows().forEach(row -> row.getTableCells().forEach(cell -> {
            String text = cell.getText();
            if (ObjectUtil.isEmpty(text)) {
                return;
            }
            Iterator<String> it = placeholderValues.keySet().iterator();
            while (it.hasNext()) {
                String key = it.next();
                if (text.indexOf(key) != -1) {
                    list.addAll(cell.getParagraphs());
                }
            }
        })));
    }



    /**
     * 清除占位符信息
     *
     * @param placeholderList 占位符位置信息
     * @param paragraphList   行数据
     * @return void
     * @author liuchao
     * @date 2020/6/10
     */
    public static void clearPlaceholder(List<int[]> placeholderList, List<XWPFParagraph> paragraphList) {
        if (ObjectUtil.isEmpty(placeholderList)) {
            return;
        }
        int[] currentPlaceholder = placeholderList.get(0);
        StringBuilder tempSb = new StringBuilder();
        for (int i = 0; i < paragraphList.size(); i++) {
            XWPFParagraph p = paragraphList.get(i);
            List<XWPFRun> runs = p.getRuns();
            for (int j = 0; j < runs.size(); j++) {
                XWPFRun run = runs.get(j);
                String text = run.getText(run.getTextPosition());
                StringBuilder nval = new StringBuilder();
                char[] textChars = text.toCharArray();
                for (int m = 0; m < textChars.length; m++) {
                    char c = textChars[m];
                    if (null == currentPlaceholder) {
                        nval.append(c);
                        continue;
                    }
                    // 排除'$'和'}'两个字符之间的字符
                    int start = currentPlaceholder[0] * 1000000 + currentPlaceholder[1] * 500 + currentPlaceholder[2];
                    int end = currentPlaceholder[3] * 1000000 + currentPlaceholder[4] * 500 + currentPlaceholder[5];
                    int cur = i * 1000000 + j * 500 + m;
                    if (!(cur >= start && cur <= end)) {
                        nval.append(c);
                    } else {
                        tempSb.append(c);
                    }
                    //判断是否是占位符结尾,如果是那获取新的占位符
                    if (tempSb.toString().endsWith("}")) {
                        placeholderList.remove(0);
                        if (ObjectUtil.isEmpty(placeholderList)) {
                            currentPlaceholder = null;
                            continue;
                        }
                        currentPlaceholder = placeholderList.get(0);
                        tempSb = new StringBuilder();
                    }
                }
                run.setText(nval.toString(), run.getTextPosition());

            }
        }
    }

    /**
     * 获取占位符信息,并且在占位符后面填充值
     *
     * @param paragraphList 行数据
     * @param map           要替换的占位符key\value
     * @return java.util.List<int [ ]>
     * @author liuchao
     * @date 2020/6/10
     */
    public static List<int[]> getPlaceholderList(List<XWPFParagraph> paragraphList, Map<String, String> map) {
        // 存储占位符 位置信息集合
        List<int[]> placeholderList = new ArrayList<>();
        // 当前占位符 0:'$'字符在XWPFParagraph集合中下标
        //          1:'$'字符在XWPFRun集合中下标
        //          2:'$'字符在text.toCharArray()数组下标
        //          3: '}'字符在XWPFParagraph集合中下标
        //          4: '}'字符在XWPFRun集合中下标
        //          5:'}'字符在text.toCharArray()数组下标
        int[] currentPlaceholder = new int[6];

        // 当前标识
        int modeTg = ignoreTg;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < paragraphList.size(); i++) {
            XWPFParagraph p = paragraphList.get(i);
            List<XWPFRun> runs = p.getRuns();
            for (int j = 0; j < runs.size(); j++) {
                XWPFRun run = runs.get(j);
                String text = run.getText(run.getTextPosition());
                char[] textChars = text.toCharArray();
                String newVal = "";
                StringBuilder textSofar = new StringBuilder();
                for (int m = 0; m < textChars.length; m++) {
                    char c = textChars[m];
                    textSofar.append(c);
                    switch (c) {
                        case '$': {
                            modeTg = startTg;
                            sb.append(c);
                        }
                        break;
                        case '{': {
                            if (modeTg == startTg) {
                                sb.append(c);
                                modeTg = readTg;
                                currentPlaceholder[0] = i;
                                currentPlaceholder[1] = j;
                                currentPlaceholder[2] = m - 1;
                            } else {
                                if (modeTg == readTg) {
                                    sb = new StringBuilder();
                                    modeTg = ignoreTg;
                                }
                            }
                        }
                        break;
                        case '}': {
                            if (modeTg == readTg) {
                                modeTg = ignoreTg;
                                sb.append(c);
                                String val = map.get(sb.toString());
                                if (ObjectUtil.isNotEmpty(val)) {
                                    newVal += textSofar.toString() + val;
                                    placeholderList.add(currentPlaceholder);
                                    textSofar = new StringBuilder();
                                }
                                currentPlaceholder[3] = i;
                                currentPlaceholder[4] = j;
                                currentPlaceholder[5] = m;
                                currentPlaceholder = new int[6];
                                sb = new StringBuilder();
                            } else if (modeTg == startTg) {
                                modeTg = ignoreTg;
                                sb = new StringBuilder();
                            }
                        }
                        default: {
                            if (modeTg == readTg) {
                                sb.append(c);
                            } else if (modeTg == startTg) {
                                modeTg = ignoreTg;
                                sb = new StringBuilder();
                            }
                        }
                    }
                }
                newVal += textSofar.toString();
                run.setTextPosition(0);
                run.setText(newVal, run.getTextPosition());
            }
        }
        return placeholderList;
    }

依赖jar

            <!--hutool工具类-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>4.6.0</version>
            </dependency>
            <!--json格式化-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.60</version>
            </dependency>  
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>4.1.1</version>
            </dependency>
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>4.1.1</version>
            </dependency>
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml-schemas</artifactId>
                <version>4.1.1</version>
            </dependency>                      

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/144730.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!