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,一经查实,立即删除!

相关文章

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 上面的例子…

使用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…

前端的使命来了

最近国家工信部发布了一则关于重大App进行适老化改造的政策目的在于随着互联网的普及&#xff0c;有一部分群体在互联网应用的使用上存在着一定的障碍&#xff0c;为了保证这部分人能够顺利的使用手机中的App&#xff0c;国家出手了&#xff01;现在人们的生活越来越离不开互联…

第二章 Burp Suite代理和浏览器设置

Burp Suite代理工具是以拦截代理的方式&#xff0c;拦截所有通过代理的网络流量&#xff0c;如客户端的请求数据、服务器端的返回信息等。Burp Suite主要拦截http和https协议的流量&#xff0c;通过拦截&#xff0c;Burp Suite以中间人的方式&#xff0c;可以对客户端请求数据、…

Multiavatar头像生成,要多少有多少

还在为选一个个性十足&#xff0c;不同于别人的头像而烦恼吗&#xff1f;12,230,590,464个头像够你选择够不够&#xff1f;Multiavatar一款集多文化头像生成器&#xff0c;它可以根据不同的种族&#xff0c;不同的文化&#xff0c;不同的年龄&#xff0c;不同的世界观&#xff…

将Java 8流解析为SQL

当Java 8发行并且人们开始流式处理各种东西时&#xff0c;很快他们就开始想象如果可以以相同的方式使用数据库将有多大的潜力。 本质上&#xff0c;关系数据库由以表状结构组织的巨大数据块组成。 这些结构非常适合进行过滤和映射操作&#xff0c;如SQL语言的SELECT&#xff0c…

TCP Congestion Control

TCP Congestion Control Congestion occurs when total arrival rate from all packet flows exceeds R over a sustained(维持) period of timeBuffers(缓冲) at multiplexer will fill and packets will be lostPhases of Congestion Behavior Light traffic Arrival Rate &l…

SVG格式的Icon,用了你就知道有多香

继阿里的iconfont之后&#xff0c;字节跳动也出品了自己的矢量图标库&#xff0c;可以实现根据单一SVG源文件变换出多种主题&#xff0c; 具备丰富的分类、更轻量的代码和更灵活的使用场景。矢量SVG图标的出现&#xff0c;完全改变了前端的开发方式&#xff0c;之前总是通过设计…

sql server2016里面的json功能 - 转

测试一下基本的&#xff0c;从查询结果里面构造一个json 的格式 create table t1(ID int identity,name nvarchar(50),Chinese int ,Math int)insert into t1 values (张三,90,80),(李四,75,90),(王五,68,100) select * from t1select * from t1 for json auto--查询结果 ID …

CSS实现TikTok文字抖动效果

前端同学在日常开发中精彩会因为一些动效和设计争的面红耳赤&#xff0c;设计希望用代码实现&#xff0c;前端要设计出gif图&#xff0c;最后谁也不让谁&#xff0c;设计走了&#xff0c;留下了前端独自加班......CSS技术是前端必须掌握的一项技能&#xff0c;不仅要掌握&#…

当心findFirst()和findAny()

过滤Java 8 Stream &#xff0c;通常使用findFirst()或findAny()来获取在过滤器中幸存的元素。 但这可能并不能真正实现您的意思&#xff0c;并且可能会出现一些细微的错误。 那么 从我们的Javadoc&#xff08; 此处和此处 &#xff09;可以看出&#xff0c;这两个方法都从流中…

Intellij新建Spring项目引入用户目录下的Spring jar包

首先&#xff0c;在IntelliJ IDEA中新建module&#xff0c;选择Spring应用&#xff1a; 在初次使用时&#xff0c;如果IDE检测到本地没有spring核心库&#xff0c;则会在新建过程中下载对应库文件&#xff0c;在使用spring框架时&#xff0c;可以细分多种不同应用场景&#xff…