java docx文档解析_带有docx4j的Java Word(.docx)文档

java docx文档解析

几个月前,我需要创建一个包含许多表和段落的动态Word文档。 过去,我曾使用POI来实现此目的,但是我发现它很难使用,并且在创建更复杂的文档时对我来说效果不佳。 因此,对于这个项目,经过一番搜索,我决定使用docx4j 。 Docx4j,根据他们的网站是:

“ docx4j是一个Java库,用于创建和处理Microsoft Open XML(Word docx,Powerpoint pptx和Excel xlsx)文件。
它类似于Microsoft的OpenXML SDK,但适用于Java。

在本文中,我将向您展示几个示例,您可以使用这些示例来生成Word文档的内容。 更具体地说,我们将看以下两个示例:

  • 加载模板Word文档以添加内容并另存为新文档
  • 将段落添加到此模板文档
  • 将表添加到此模板文档

这里的一般方法是首先创建一个Word文档,其中包含最终文档的布局和主要样式。 在本文档中,您将需要添加占位符(简单字符串),我们将使用这些占位符来搜索并替换为真实内容。

例如,一个非常基本的模板如下所示:

在本文中,我们将向您展示如何填充此内容,以便获得此信息:

加载模板Word文档以添加内容并另存为新文档

首先是第一件事。 让我们创建一个简单的Word文档,将其用作模板。 为此,只需打开Word,创建一个新文档并将其另存为template.docx。 这是我们用来向其添加内容的单词模板。 我们需要做的第一件事是用docx4j加载该文档。 您可以使用以下一段Java代码:

private WordprocessingMLPackage getTemplate(String name) throws Docx4JException, FileNotFoundException {WordprocessingMLPackage template = WordprocessingMLPackage.load(new FileInputStream(new File(name)));return template;}

这将返回一个Java对象,该对象表示完整的(此时)空文档。 现在,我们可以使用Docx4J API在此Word文档中添加,删除和修改内容。 Docx4J有许多帮助程序类,可用于遍历此文档。 我确实写了一些帮助程序,尽管它们确实使查找特定的占位符并用实际内容替换它们非常容易。 让我们看看其中之一。 此操作是几个JAXB操作的包装,使您可以搜索特定元素及其所有子元素来查找某个类。 例如,您可以使用它来获取文档中的所有表,表中的所有行等等。

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;}

没什么复杂的,但真的很有帮助。 让我们看看如何使用此操作。 在此示例中,我们仅将简单的文本占位符替换为其他值。 例如,这是您用来动态设置文档标题的内容。 不过,首先,在您创建的Word模板中添加一个自定义占位符。 我将为此使用SJ_EX1。 我们将用我们的名字替换这个值。 docx4j中的基本文本元素由org.docx4j.wml.Text类表示。 要替换此简单的占位符,我们要做的就是调用此方法:

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);}}}

这将查找文档中的所有Text元素,并将匹配的元素替换为我们指定的值。 现在,我们要做的就是将文档写回到文件中。

private void writeDocxToStream(WordprocessingMLPackage template, String target) throws IOException, Docx4JException {File f = new File(target);template.save(f);}

如您所见,并不难。

通过此设置,我们还可以将更复杂的内容添加到Word文档中。 确定如何添加特定内容的最简单方法是查看word文档的XML源代码。 这将告诉您需要哪些包装器以及Word如何编组XML。 对于下一个示例,我们将研究如何添加完整的段落。  

将段落添加到此模板文档

您可能想知道为什么我们需要添加段落? 我们已经可以添加文本了,一个段落不只是一大段文本吗? 好吧,是的,不是。 一段确实看起来像是一段很大的文字,但是您需要考虑的是换行符。 如果您像以前一样添加Text元素,并在文本中添加换行符,则它们不会显示。 如果需要换行,则需要创建一个新段落。 幸运的是,使用Docx4j也很容易做到这一点。
我们将通过以下步骤进行操作:

  1. 从模板中找到要替换的段落
  2. 将输入文本分成单独的行
  3. 对于每一行,根据模板中的段落创建一个新段落
  4. 删除原始段落

我们应该已经拥有的辅助方法不应该太难了。

private void replaceParagraph(String placeholder, String textToAdd, WordprocessingMLPackage template, ContentAccessor addTo) {// 1. get the paragraphList<Object> paragraphs = getAllElementFromObject(template.getMainDocumentPart(), P.class);P toReplace = null;for (Object p : paragraphs) {List<Object> texts = getAllElementFromObject(p, Text.class);for (Object t : texts) {Text content = (Text) t;if (content.getValue().equals(placeholder)) {toReplace = (P) p;break;}}}// we now have the paragraph that contains our placeholder: toReplace// 2. split into seperate linesString as[] = StringUtils.splitPreserveAllTokens(textToAdd, '\n');for (int i = 0; i < as.length; i++) {String ptext = as[i];// 3. copy the found paragraph to keep styling correctP copy = (P) XmlUtils.deepCopy(toReplace);// replace the text elements from the copyList texts = getAllElementFromObject(copy, Text.class);if (texts.size() > 0) {Text textToReplace = (Text) texts.get(0);textToReplace.setValue(ptext);}// add the paragraph to the documentaddTo.getContent().add(copy);}// 4. remove the original one((ContentAccessor)toReplace.getParent()).getContent().remove(toReplace);}

在此方法中,我们用提供的文本替换段落的内容,然后将新段落替换为用addTo指定的参数。

String placeholder = "SJ_EX1";String toAdd = "jos\ndirksen";replaceParagraph(placeholder, toAdd, template, template.getMainDocumentPart());

如果您在Word模板中使用更多内容来运行此程序,则会注意到这些段落将出现在文档的底部。 原因是将段落添加回了主文档。 如果您希望将段落添加到文档中的特定位置(通常需要这样做),则可以将其包装在1×1无边界表格中。 该表被视为段落的父级,可以在此处添加新段落。

将表添加到此模板文档

我想展示的最后一个示例是如何向单词模板添加表格。 实际上,更好的描述是如何在Word模板中填充预定义的表格。 就像我们对简单的文本和段落所做的一样,我们将替换占位符。 对于此示例,向您的Word文档中添加一个简单的表格(您可以随意设置样式)。 向此表添加1个哑行,用作内容模板。 在代码中,我们将查找该行,将其复制,并将内容替换为来自Java代码的新行,如下所示:

  1. 查找包含我们的关键字之一的表
  2. 复制用作行模板的行
  3. 对于每行数据,根据行模板向表中添加一行
  4. 删除原始模板行

与我们在段落中显示的方法相同。 首先,让我们看一下如何提供替换数据。 对于此示例,我仅提供了一组哈希图,其中包含要替换的占位符的名称和要替换为其的值。 我还提供了可以在表格行中找到的替换令牌。

Map<String,String> repl1 = new HashMap<String, String>();repl1.put("SJ_FUNCTION", "function1");repl1.put("SJ_DESC", "desc1");repl1.put("SJ_PERIOD", "period1");Map<String,String> repl2 = new HashMap<String,String>();repl2.put("SJ_FUNCTION", "function2");repl2.put("SJ_DESC", "desc2");repl2.put("SJ_PERIOD", "period2");Map<String,String> repl3 = new HashMap<String,String>();repl3.put("SJ_FUNCTION", "function3");repl3.put("SJ_DESC", "desc3");repl3.put("SJ_PERIOD", "period3");replaceTable(new String[]{"SJ_FUNCTION","SJ_DESC","SJ_PERIOD"}, Arrays.asList(repl1,repl2,repl3), template);

现在,这个replaceTable方法是什么样的。

private void replaceTable(String[] placeholders, List<Map<String, String>> textToAdd,WordprocessingMLPackage template) throws Docx4JException, JAXBException {List<Object> tables = getAllElementFromObject(template.getMainDocumentPart(), Tbl.class);// 1. find the tableTbl tempTable = getTemplateTable(tables, placeholders[0]);List<Object> rows = getAllElementFromObject(tempTable, Tr.class);// first row is header, second row is contentif (rows.size() == 2) {// this is our template rowTr templateRow = (Tr) rows.get(1);for (Map<String, String> replacements : textToAdd) {// 2 and 3 are done in this methodaddRowToTable(tempTable, templateRow, replacements);}// 4. remove the template rowtempTable.getContent().remove(templateRow);}}

该方法查找表,获取第一行,并为每个提供的映射向表添加新行。 返回之前,它将删除模板行。 此方法使用两个帮助器:addRowToTable和getTemplateTable。 我们首先来看最后一个:

private Tbl getTemplateTable(List<Object> tables, String templateKey) throws Docx4JException, JAXBException {for (Iterator<Object> iterator = tables.iterator(); iterator.hasNext();) {Object tbl = iterator.next();List<?> textElements = getAllElementFromObject(tbl, Text.class);for (Object text : textElements) {Text textElement = (Text) text;if (textElement.getValue() != null && textElement.getValue().equals(templateKey))return (Tbl) tbl;}}return null;}

此函数只是查看表是否包含我们的占位符之一。 如果是这样,则返回该表。 addRowToTable操作也非常简单。

private static void addRowToTable(Tbl reviewtable, Tr templateRow, Map<String, String> replacements) {Tr workingRow = (Tr) XmlUtils.deepCopy(templateRow);List textElements = getAllElementFromObject(workingRow, Text.class);for (Object object : textElements) {Text text = (Text) object;String replacementValue = (String) replacements.get(text.getValue());if (replacementValue != null)text.setValue(replacementValue);}reviewtable.getContent().add(workingRow);}

此方法复制我们的模板,并使用提供的值替换此模板行中的占位符。 该副本将添加到表中。 就是这样。 通过这段代码,我们可以在Word文档中填写套利表,同时保留表的布局和样式。

至此为止。 使用段落和表,您可以创建许多不同类型的文档,这与最常生成的文档类型非常匹配。 但是,也可以使用这种相同的方法将其他类型的内容添加到Word文档中。

参考:来自Smart Java博客的JCG合作伙伴 Jos Dirksen 使用docx4j以编程方式创建复杂的Word(.docx)文档 。


翻译自: https://www.javacodegeeks.com/2012/07/java-word-docx-documents-with-docx4j.html

java docx文档解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/354716.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Win8.1部署 .NET Framework 3.5 安装方式

Windows 8.1中包含.NET Framework&#xff0c;操作系统安装过程中默认安装 .NET Framework 4.5.1。如果程序需要.NET Framework 3.5支持&#xff0c;将自动启用相关功能。 注意&#xff1a; 1. .NET Framework 3.5同时支持基于 .NET Framework 2.0/3.0/3.5构建应用程序。 2. 在…

@Autowired所有的东西!

最近&#xff0c;我写了Autowired注释 &#xff0c;它使我们可以编写更少的代码&#xff0c;从而使我们的生活更轻松 。 但是&#xff0c;使用它通常会使您的设计更加复杂。 尤其是当我们谈论在类的属性上使用它时。 它更容易违反 单一责任原则 。 这样可以更容易地注意到这一…

Python基础-闭包

闭包&#xff1a;闭包内部函数定义函数时的环境 def outer():x 10def inner(): # 条件一 inner就是内部函数print(x) # 条件二 外部环境的一个变量return inner # 结论 内部函数inner就是一个闭包 outer()() 转载于:https://www.cnblogs.com/fansik/p/7676231.html

jit即时编译_热点中的即时编译器(JIT)

jit即时编译即时编译器&#xff08;JIT&#xff09;的概念以及更广泛的自适应优化是除Java&#xff08;.Net&#xff0c;Lua&#xff0c;JRuby&#xff09;之外的许多语言中众所周知的概念。 为了解释什么是JIT编译器&#xff0c;我想先定义一个编译器概念。 根据维基百科&…

每天十分钟系列:JS数据操作之神奇的map()

Array.prototype.map() map()方法可以创建一个新数组&#xff0c;其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。 demo1 上面的例子&#xff0c;在控制台中打印的结果是&#xff1a; 1 2 3 4 5 demo2 javascript学习交流群&#xff1a;453833554 上面的例子…

JSON格式数据与数据组件

1.将JSON格式数据转化为wex5data数据组件中的数据&#xff0c;且数据列数不必一 一对应&#xff0c;但转化的列数据的列名与数据表字段相同。 Model.prototype.goodsDataCustomRefresh function(event){var url require.toUrl("./json/goodsData.json");$.ajaxSett…

作业十三

<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <title>GZCC</title> </head> <body> <h1>MIS问答平台</h1> <h2 id"2015">2015</h2> <div id&quo…

openshift_通过OpenShift超越云炒作

openshift您是否厌倦了为应用程序请求新的开发机器&#xff1f; 您是否为必须为应用程序设置新的测试环境感到烦恼&#xff1f; 您是否只想专注于和平开发应用程序而不会一直“沉迷于堆栈”&#xff1f; 我们听到你的声音。 我们也去过那里。 不用担心&#xff0c;OpenShift就在…

初级程序员需要接触好的架构代码

最近感触最深的就是一个项目需要好的架构设计&#xff0c;不会因为后面不断延伸业务需求造成业务代码的不断堆砌&#xff0c;破坏整个项目的层次结构&#xff0c;也让新来的团队成员更好的上手项目&#xff0c;从清晰的结构就能很快的了解整个业务的流程。一个好的架构设计&…

行动中的反思实践

在上一篇文章中 &#xff0c;我解释了最基本的敏捷实践是反思。 在本文中&#xff0c;我概述了组织&#xff0c;团队和人员如何在行动中运用反思的示例。 通过回顾反思 回顾是强大的工具&#xff0c;整个团队都可以使用它们来反思其当前的工作实践&#xff0c;以了解他们为持续…

python基本语法1.2--数的移位及与或抑或相关计算

#便于中文的显示# -*- coding: utf-8 -*- #指数表示 # a ** b <> power(a, b) print(10 ** 2) #100 print(10 ** 2.5) #316.22776601683796 #做除法 # //: 返回商的整数部分 print(23 // 5) #4 print(28.7 // 4.4) #6.0#<<向左移位做乘法#>>向右移位做除法 …

fabric canvas 清空并重置画布

fabric.js是一个强大而简约的依赖HTML5上的 canvas的javascript库&#xff0c;Fabric在canvas元素顶部提供了交互式对象模型&#xff0c;它还具有SVG到画布&#xff08;和画布到SVG&#xff09;解析器&#xff0c;官网上提供了丰富的案例&#xff0c;可照葫芦画瓢&#xff0c;足…

使用vue-axios请求geoJson数据报错的问题

最近的项目用到了echarts一个带有散点地图的图表,按照正常jquery写法应该使用ajax请求geojson的数据动态去切换地图,就像下面这样 $.get(Js/map/ cityData.name .json, function(geoJson) {map(gr-map, cityData, geoJson, geoCoordMap);//调用地图方法}); 页面显示(成功) bu…

前端工业物联网开发(Electron + Typescript + Vue)

从SpaceX公司的带来的Javascript上天新闻开始&#xff0c;以及近几年js语言在整个互联网的广泛应用&#xff0c;注定未来js语言有着不平凡的使命&#xff0c;跨端&#xff08;一套代码多端运行&#xff09;、易UI&#xff08;前端开发UI的便利&#xff09;、丰富的生态&#xf…

JAVA记录-Servlet介绍

1.什么是Servlet Servlet是sun公司提供的一门用于开发动态web资源的技术。Sun公司在其API中提供了一个servlet接口&#xff0c;用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据)&#xff0c;需要完成以下2个步骤&#xff1a;1、编写一个Java类&#xff0c;实现…

前端jenkins打包编译发布项目流程

如今的前端项目已不像之前直接写完代码&#xff0c;扔到服务器直接运行了。免不了安装依赖&#xff0c;执行build过程&#xff0c;然后再将打包后的代码上传到服务器&#xff0c;可能还需要更改配置文件啥的。如此多的步骤严重影响开发的日常划水时间。所以jenkins自动化部署少…

OpenMap教程4 –图层

1.简介 在第一个教程中&#xff0c;我们创建了一个基本的OpenMap GIS应用程序&#xff0c;该应用程序在JFrame中显示一个从文件系统加载的具有一个形状图层的地图。 该教程基于com.bbn.openmap.app.example.SimpleMap 。 在第二篇教程中&#xff0c;我们扩展了基本应用程序以使…

Vue.js实战之Vuex的入门教程

在 Vue.js 的项目中&#xff0c;如果项目结构简单&#xff0c; 父子组件之间的数据传递可以使用 props 或者 $emit 等方式。 但是如果是大型项目&#xff0c;很多时候都需要在子组件之间传递数据&#xff0c;使用之前的方式就不太方便。Vue 的状态管理工具 Vuex 完美的解决了这…

为什么写公众号,为什么改名字

这是一篇水果文&#xff0c;啥叫水果文&#xff1f;比水文稍微有点内容&#xff0c;我暂时这么解释吧。最近刚刚开始决定写公众号&#xff0c;其实这个订阅号申请的还蛮早的&#xff0c;当时是为了学习开发公众号而创建的&#xff0c;所以中间有过一段空档期&#xff08;呸&…

chrome浏览器解决ajax跨域问题

方法一 1、右键谷歌快捷方式&#xff0c;选择“属性”。 2、打开属性窗口&#xff0c;切换到“快捷方式”选项卡。 3、在目标路径的后面添加【 --disable-web-security】&#xff0c;其中chrome.exe与--disable之间有一个空格 4、点击应用&#xff0c;然后点击确定关闭窗口。 5…