生活随笔
收集整理的這篇文章主要介紹了
java通过poi-tl模板引擎生成表格(Word)
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
java通過poi-tl生成表格以及源碼分析
- 依賴
- 模板
- 如何動(dòng)態(tài)生成表格
- 參考文檔及分析
- 代碼
最近導(dǎo)出的word文件要求是越來越多了,而且對(duì)樣式也做了很多要求,今天參考文檔學(xué)習(xí)了一下普通表格構(gòu)建表格、動(dòng)態(tài)構(gòu)建word表格的方法。
依賴
<dependency><groupId>com
.deepoove
</groupId
><artifactId>poi
-tl
</artifactId
><version>1.10.0</version
></dependency
>
模板
1. {{ID}}、{{ID}} 普通文本信息占位符;
2. {{#order}} 普通word表格占位符;
3. 最后的表格是動(dòng)態(tài)構(gòu)建word表格的模板,樣式已經(jīng)設(shè)置好;
如何動(dòng)態(tài)生成表格
當(dāng)需求中的表格更加復(fù)雜的時(shí)候,我們完全可以設(shè)計(jì)好那些固定的部分,將需要?jiǎng)討B(tài)渲染的部分單元格交給自定義模板渲染策略。poi-tl提供了抽象表格策略 DynamicTableRenderPolicy 來實(shí)現(xiàn)這樣的功能。
public abstract class DynamicTableRenderPolicy implements RenderPolicy {public abstract void render(XWPFTable table
, Object data
);
}
{{detailTable}}標(biāo)簽可以在表格內(nèi)的任意單元格內(nèi),DynamicTableRenderPolicy會(huì)獲取XWPFTable對(duì)象進(jìn)而獲得操作整個(gè)表格的能力。
1、首先新建渲染策略DetailTablePolicy,繼承于抽象表格策略,重寫其render方法。
public class DetailTablePolicy extends DynamicTableRenderPolicy
2、然后將模板標(biāo)簽{{detailTable}}設(shè)置成此策略。
Configure config
= Configure.builder().bind("detailTable", new DetailTablePolicy()).build();
參考文檔及分析
但是在測(cè)試是發(fā)現(xiàn)文檔中例子始終無法生成最下面的表格,即貨物明細(xì)表,
debug發(fā)現(xiàn),策略類中的data為空,難怪沒有生成表格。
但是數(shù)據(jù)明明已經(jīng)初始化好了,那接下來就是數(shù)據(jù)在傳輸?shù)氖菚r(shí)候在哪丟失或者傳進(jìn)去的參數(shù)根本就沒被取到。
跟蹤代碼,
最后在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);這個(gè)方法最后進(jìn)入到我們自己寫的策略類DetailTablePolicy ,真是妙啊!
那既然是從這里進(jìn)入我們的策略類,那也是參數(shù)的入口,就讓我們看看為什么DetailTablePolicy 中data為null?
data就是renderDataCompute.compute(eleTemplate.getTagName()),其中
eleTemplate.getTagName()為detail_table而且renderDataCompute也沒有與detail_table相對(duì)應(yīng)的屬性,雖然理論上應(yīng)該是獲取detailTable,但是現(xiàn)實(shí)是木有取到。因此也驗(yàn)證了我們前面的猜測(cè),數(shù)據(jù)確實(shí)是傳過來了,只是沒有獲取到。因此我就有個(gè)大膽的猜測(cè),是不是因?yàn)閷傩宰侄?#xff08;detail_table和detailTable)沒有完全對(duì)應(yīng),因此我就把detail_table替換成了detailTable(代碼和docx模板全部替換),結(jié)果妹想到呀,成了!!!
哈哈哈,真是走了狗屎運(yùn)了。還望路過的各位大佬指點(diǎn)指點(diǎn)
補(bǔ)充renderDataCompute.compute(eleTemplate.getTagName()):
雖然走了狗屎運(yùn),但是還是想探個(gè)究竟,compute到底做了些什么
進(jìn)入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
) {}}return elObject
.eval(el
);} catch (ExpressionEvalException e
) {if (isStrict
) throw e
;return null;}}
進(jìn)入DefaultEL類中
public Object eval(String el
) {if (THIS
.equals(el
)) {return model
;}Dot dot
= new Dot(el
);return dot
.eval(this);}
在Dot類中終于找到了!!!
public Object eval(DefaultEL elObject
) {if (elObject
.cache
.containsKey(el
)) return elObject
.cache
.get(el
);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
);}if (obj
instanceof Map) {return ((Map<?, ?>) obj
).get(key
);}Method readMethod
= ReadMethodFinder.find(objClass
, key
);if (null != readMethod
) {try {return readMethod
.invoke(obj
);} catch (Exception e
) {logger
.info("Introspector {} fail: {}", key
, e
.getMessage());}}Field 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
) {} 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
附上代碼如下:
代碼
- 實(shí)體類DetailData(動(dòng)態(tài)構(gòu)建word表格的數(shù)據(jù))
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
;}
}
- 實(shí)體類PaymentData (相當(dāng)于數(shù)據(jù)集,包換word模板中需要的數(shù)據(jù)都在這里)
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
;}
}
- 策略類(動(dòng)態(tài)構(gòu)建表格的策略方法)
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("日期", "訂單編號(hào)", "銷售代表", "離岸價(jià)", "發(fā)貨方式", "條款", "稅號(hào)").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");}}
總結(jié)
以上是生活随笔為你收集整理的java通过poi-tl模板引擎生成表格(Word)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。