日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java通过poi-tl模板引擎生成表格(Word)

發布時間:2023/12/29 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java通过poi-tl模板引擎生成表格(Word) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

java通過poi-tl生成表格以及源碼分析

    • 依賴
    • 模板
    • 如何動態生成表格
    • 參考文檔及分析
    • 代碼

最近導出的word文件要求是越來越多了,而且對樣式也做了很多要求,今天參考文檔學習了一下普通表格構建表格、動態構建word表格的方法。

依賴

<dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.10.0</version></dependency>

模板

1. {{ID}}、{{ID}} 普通文本信息占位符;
2. {{#order}} 普通word表格占位符;
3. 最后的表格是動態構建word表格的模板,樣式已經設置好;

如何動態生成表格

當需求中的表格更加復雜的時候,我們完全可以設計好那些固定的部分,將需要動態渲染的部分單元格交給自定義模板渲染策略。poi-tl提供了抽象表格策略 DynamicTableRenderPolicy 來實現這樣的功能。

public abstract class DynamicTableRenderPolicy implements RenderPolicy {public abstract void render(XWPFTable table, Object data); }

{{detailTable}}標簽可以在表格內的任意單元格內,DynamicTableRenderPolicy會獲取XWPFTable對象進而獲得操作整個表格的能力。
1、首先新建渲染策略DetailTablePolicy,繼承于抽象表格策略,重寫其render方法。

public class DetailTablePolicy extends DynamicTableRenderPolicy

2、然后將模板標簽{{detailTable}}設置成此策略。

Configure config = Configure.builder().bind("detailTable", new DetailTablePolicy()).build();

參考文檔及分析

但是在測試是發現文檔中例子始終無法生成最下面的表格,即貨物明細表,
debug發現,策略類中的data為空,難怪沒有生成表格。

但是數據明明已經初始化好了,那接下來就是數據在傳輸的是時候在哪丟失或者傳進去的參數根本就沒被取到。

跟蹤代碼,

最后在ElementProcessor中,有這樣一段代碼

void visit(ElementTemplate eleTemplate) {RenderPolicy policy = eleTemplate.findPolicy(template.getConfig());Objects.requireNonNull(policy, "Cannot find render policy: [" + eleTemplate.getTagName() + "]");if (policy instanceof DocxRenderPolicy) return;logger.info("Start render Template {}, Sign:{}, policy:{}", eleTemplate, eleTemplate.getSign(),ClassUtils.getShortClassName(policy.getClass()));policy.render(eleTemplate, renderDataCompute.compute(eleTemplate.getTagName()), template);}

通過policy.render(eleTemplate, renderDataCompute.compute(eleTemplate.getTagName()), template);這個方法最后進入到我們自己寫的策略類DetailTablePolicy ,真是妙啊!
那既然是從這里進入我們的策略類,那也是參數的入口,就讓我們看看為什么DetailTablePolicy 中data為null?

data就是renderDataCompute.compute(eleTemplate.getTagName()),其中
eleTemplate.getTagName()為detail_table而且renderDataCompute也沒有與detail_table相對應的屬性,雖然理論上應該是獲取detailTable,但是現實是木有取到。因此也驗證了我們前面的猜測,數據確實是傳過來了,只是沒有獲取到。因此我就有個大膽的猜測,是不是因為屬性字段(detail_table和detailTable)沒有完全對應,因此我就把detail_table替換成了detailTable(代碼和docx模板全部替換),結果妹想到呀,成了!!!
哈哈哈,真是走了狗屎運了。還望路過的各位大佬指點指點

補充renderDataCompute.compute(eleTemplate.getTagName()):
雖然走了狗屎運,但是還是想探個究竟,compute到底做了些什么
進入DefaultELRenderDataCompute類中

public Object compute(String el) {try {if (null != envObject && !el.contains("#this")) {try {Object val = envObject.eval(el);if (null != val) {return val;}} catch (Exception e) {// ignore}}// 進入這個方法return elObject.eval(el);} catch (ExpressionEvalException e) {if (isStrict) throw e;// Cannot calculate the expression, the default returns nullreturn null;}}

進入DefaultEL類中

public Object eval(String el) {// THIS是什么? private static final String THIS = "#this";if (THIS.equals(el)) {return model;}Dot dot = new Dot(el);// 繼續進入return dot.eval(this);}

在Dot類中終于找到了!!!

public Object eval(DefaultEL elObject) {// 如果cache中有el相等的key,則返回cache中相應的valueif (elObject.cache.containsKey(el)) return elObject.cache.get(el);// 如果target為null result就為evalKey(elObject.model)// evalKey 是啥??? 真是要了老命了,我以為結束了呢,繼續看方法evalKey吧Object result = null != target ? result = evalKey(target.eval(elObject)) : evalKey(elObject.model);if (null != result) elObject.cache.put(el, result);return result;}private Object evalKey(Object obj) {if (null == obj) {throw new ExpressionEvalException("Error eval " + key + ", the value of " + target + " is null");}final Class<?> objClass = obj.getClass();if (obj instanceof String || obj instanceof Number || obj instanceof java.util.Date || obj instanceof Collection|| obj instanceof Boolean || objClass.isArray() || objClass.isPrimitive()) {throw new ExpressionEvalException("Error eval " + key + ", the type of " + target + "must be Hash, but is " + objClass);}// 重點是這里 如果是Map類型 返回對應的value// 但是我們的elObject.model是Map嗎? 看下圖if (obj instanceof Map) {return ((Map<?, ?>) obj).get(key);}// introspectorMethod readMethod = ReadMethodFinder.find(objClass, key);if (null != readMethod) {try {return readMethod.invoke(obj);} catch (Exception e) {logger.info("Introspector {} fail: {}", key, e.getMessage());}}// 通過對象的類信息反射返回對應屬性的值// reflectField field = FieldFinder.find(objClass, key);if (null == field) {throw new ExpressionEvalException("Cannot find property " + key + " from " + objClass);} else {try {return field.get(obj);} catch (Exception e) {throw new ExpressionEvalException("Error read the property:" + key + " from " + objClass);}}}

elObject.model和elObject.cache的類型

static Field find(Class<?> objClass, String key) {Class<?> clazz = objClass;Field field = null;while (clazz != Object.class) {try {field = findInClazz(clazz, key);// 暴力訪問field.setAccessible(true);return field;} catch (NoSuchFieldException e) {// do nothing, go to super class} catch (Exception e) {logger.warn("Error read the property:" + key + " from " + objClass);}clazz = clazz.getSuperclass();}return null;}static Field findInClazz(Class<?> clazz, String key) throws NoSuchFieldException {Field field = null;try {// 通過類信息獲取對應字段的值field = clazz.getDeclaredField(key);return field;} catch (Exception e) {}Field[] fields = cache.get(clazz);if (null == fields) {fields = clazz.getDeclaredFields();cache.put(clazz, fields);}for (Field f : fields) {Name annotation = f.getAnnotation(Name.class);if (null != annotation && key.equals(annotation.value())) {return f;}}throw new NoSuchFieldException(key);}

Poi-tl Documentation

附上代碼如下:

代碼

  • 實體類DetailData(動態構建word表格的數據)
public class DetailData {private List<RowRenderData> goods;private List<RowRenderData> labors;public List<RowRenderData> getGoods() {return goods;}public void setGoods(List<RowRenderData> goods) {this.goods = goods;}public List<RowRenderData> getLabors() {return labors;}public void setLabors(List<RowRenderData> labors) {this.labors = labors;} }
  • 實體類PaymentData (相當于數據集,包換word模板中需要的數據都在這里)
public class PaymentData {private String NO;private String ID;private String taitou;private String consignee;private String subtotal;private String tax;private String transform;private String other;private String unpay;private String total;private TableRenderData order;private DetailData detailTable;public String getNO() {return NO;}public void setNO(String NO) {this.NO = NO;}public String getID() {return ID;}public void setID(String ID) {this.ID = ID;}public String getTaitou() {return taitou;}public void setTaitou(String taitou) {this.taitou = taitou;}public String getConsignee() {return consignee;}public void setConsignee(String consignee) {this.consignee = consignee;}public String getSubtotal() {return subtotal;}public void setSubtotal(String subtotal) {this.subtotal = subtotal;}public String getTax() {return tax;}public void setTax(String tax) {this.tax = tax;}public String getTransform() {return transform;}public void setTransform(String transform) {this.transform = transform;}public String getOther() {return other;}public void setOther(String other) {this.other = other;}public String getUnpay() {return unpay;}public void setUnpay(String unpay) {this.unpay = unpay;}public String getTotal() {return total;}public void setTotal(String total) {this.total = total;}public TableRenderData getOrder() {return order;}public void setOrder(TableRenderData order) {this.order = order;}public DetailData getDetailTable() {return detailTable;}public void setDetailTable(DetailData detailTable) {this.detailTable = detailTable;} }
  • 策略類(動態構建表格的策略方法)
public class DetailTablePolicy extends DynamicTableRenderPolicy {// 貨品填充數據所在行數int goodsStartRow = 2;// 人工費填充數據所在行數int laborsStartRow = 5;@Overridepublic void render(XWPFTable table, Object data) throws Exception {if (null == data) return;DetailData detailData = (DetailData) data;// 人工費List<RowRenderData> labors = detailData.getLabors();if (null != labors) {table.removeRow(laborsStartRow);// 循環插入行for (int i = 0; i < labors.size(); i++) {XWPFTableRow insertNewTableRow = table.insertNewTableRow(laborsStartRow);for (int j = 0; j < 7; j++) insertNewTableRow.createCell();// 合并單元格TableTools.mergeCellsHorizonal(table, laborsStartRow, 0, 3);// 單行渲染TableRenderPolicy.Helper.renderRow(table.getRow(laborsStartRow), labors.get(i));}}// 貨物List<RowRenderData> goods = detailData.getGoods();if (null != goods) {table.removeRow(goodsStartRow);for (int i = 0; i < goods.size(); i++) {XWPFTableRow insertNewTableRow = table.insertNewTableRow(goodsStartRow);for (int j = 0; j < 7; j++) insertNewTableRow.createCell();TableRenderPolicy.Helper.renderRow(table.getRow(goodsStartRow), goods.get(i));}}} @DisplayName("Example for Table") public class generateTable{String resource = "src/main/resources/payment.docx";PaymentData datas = new PaymentData();// 初始化數據@BeforeEachpublic void init() {datas.setNO("KB.6890451");datas.setID("ZHANG_SAN_091");datas.setTaitou("深圳XX家裝有限公司");datas.setConsignee("丙丁");datas.setSubtotal("8000");datas.setTax("600");datas.setTransform("120");datas.setOther("250");datas.setUnpay("6600");datas.setTotal("總共:7200");RowRenderData header = Rows.of("日期", "訂單編號", "銷售代表", "離岸價", "發貨方式", "條款", "稅號").bgColor("F2F2F2").center().textColor("7F7f7F").textFontFamily("Hei").textFontSize(9).create();RowRenderData row = Rows.of("2018-06-12", "SN18090", "李四", "5000元", "快遞", "附錄A", "T11090").center().create();BorderStyle borderStyle = new BorderStyle();borderStyle.setColor("A6A6A6");borderStyle.setSize(4);borderStyle.setType(XWPFTable.XWPFBorderType.SINGLE);TableRenderData table = Tables.ofA4MediumWidth().addRow(header).addRow(row).border(borderStyle).center().create();datas.setOrder(table);DetailData detailTable = new DetailData();RowRenderData good = Rows.of("4", "墻紙", "書房+臥室", "1500", "/", "400", "1600").center().create();List<RowRenderData> goods = Arrays.asList(good, good, good);RowRenderData labor = Rows.of("油漆工", "2", "200", "400").center().create();List<RowRenderData> labors = Arrays.asList(labor, labor, labor, labor);detailTable.setGoods(goods);detailTable.setLabors(labors);datas.setDetailTable(detailTable);}// 測試方法@Testpublic void testPaymentExample() throws Exception {Configure config = Configure.builder().bind("detailTable", new DetailTablePolicy()).build();XWPFTemplate template = XWPFTemplate.compile(resource, config).render(datas);template.writeToFile("out_example_payment.docx");}}

總結

以上是生活随笔為你收集整理的java通过poi-tl模板引擎生成表格(Word)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 超碰成人在线免费观看 | 涩涩五月天 | av动漫网站 | 四虎5151久久欧美毛片 | 女子spa高潮呻吟抽搐 | 丁香激情五月 | 大片视频免费观看视频 | 国产白浆视频 | 婷婷色综合网 | 亚洲欧美色图片 | 黄色视屏网站 | 国产黄色片在线 | 中国一级特黄真人毛片免费观看 | 国产激情一区二区三区视频免樱桃 | 亚洲一区激情 | 国产精品腿扒开做爽爽爽挤奶网站 | 办公室大战高跟丝袜秘书经理ol | 亚洲精品在线免费观看视频 | 青青青视频在线播放 | 男人天堂中文字幕 | 欧美第三页 | 亚洲熟妇无码乱子av电影 | 九九免费在线视频 | 91精品国产91综合久久蜜臀 | 色中文字幕在线观看 | 青青草91久久久久久久久 | 92av视频 | 国产另类综合 | 荫蒂被男人添免费视频 | 久久黑丝 | 中午字幕在线观看 | 欧美亚洲一区二区三区四区 | 淫片一级国产 | 亚洲精品久久久久avwww潮水 | 青娱乐在线免费视频 | 国产精品网友自拍 | 曰韩一级片 | 欧美三区在线 | 啪啪免费 | 美女精品久久 | 亚洲www在线观看 | 欧美裸体xxxx极品少妇 | 色综合av在线 | 嫩草影院懂你的 | 福利视频一区 | 亚洲一区二区三区麻豆 | 欧美作爱视频 | 精品一区二区免费 | 97精品在线视频 | 久久免费福利 | 67194国产| 日日夜夜国产 | 日韩美女视频 | 欧美日韩123区| 色香五月 | 精品精品| 亚洲av无码专区在线 | 亚洲欧美日韩色图 | 日韩精品网| youjizz.com中国| 亚洲一卡二卡在线 | 日韩少妇精品 | av老司机在线播放 | 风韵丰满熟妇啪啪区老熟熟女 | 苍井空亚洲精品aa片在线播放 | 精品麻豆| 操操操日日日 | 欧美久久久久久久久久久久久久 | 国产视频一| 国产精品一卡二卡 | 色视频2| 日日噜噜夜夜狠狠久久波多野 | 欧美变态网站 | 欧美一区二区三区黄片 | 东方成人av| 性色av无码久久一区二区三区 | 超碰在线观看免费版 | 九九热九九爱 | 水多多在线 | 曰本三级日本三级日本三级 | 波多野结衣中文字幕在线播放 | 在线能看的av | 一区在线不卡 | 亚洲国产精品va在线看黑人 | 国产精品久久二区 | 日本免费福利视频 | 污污视频网站在线 | 99精品久久久 | 国产影视一区二区三区 | 九九热精品在线观看 | 日本精品网站 | 日韩美一级片 | 欧美精品入口蜜桃 | 中出av在线| 91视频三区 | 小镇姑娘国语版在线观看免费 | 私密spa按摩按到高潮 | 乳女教师の诱惑julia | 伊人ab|