能否程序化向Asciidoc模板注入数据生成发票?求技术方向
嘿,我完全懂你找了好久没头绪的感觉——其实用AsciidoctorJ向Asciidoc模板注入数据生成发票类文档是完全可行的,给你几个具体的技术方向参考:
1. 用AsciidoctorJ属性(Attributes)注入基础键值对数据
这是最直接的方案,Asciidoctor本身支持通过属性传递动态值,适合客户名称、发票编号、日期这类单一值数据。
- 你的Asciidoc模板可以这样写(用
{属性名}做占位符):= 正式发票 :customer-name: {customer-name} :invoice-id: {invoice-id} :invoice-date: {invoice-date} 客户名称: {customer-name} 发票编号: {invoice-id} 开票日期: {invoice-date} - 然后在Java代码里通过
Attributes对象注入数据,再触发PDF转换:// 初始化属性并注入数据 Attributes attributes = Attributes.builder() .attribute("customer-name", "张三科技有限公司") .attribute("invoice-id", "INV-2024-0078") .attribute("invoice-date", "2024-05-20") .build(); // 创建Asciidoctor实例并执行转换 Asciidoctor asciidoctor = Asciidoctor.Factory.create(); asciidoctor.convertFile( new File("invoice-template.adoc"), Options.builder() .attributes(attributes) .backend("pdf") .outputDir(new File("output")) .build() );
2. 自定义预处理器(Preprocessor)处理复杂列表/表格数据
如果发票包含商品明细这类需要循环生成的复杂数据,属性就不够用了。这时候可以写自定义预处理器,在渲染前动态生成Asciidoc内容。
思路是:在模板里留一个标记(比如[[invoice-items]]),预处理器读取这个标记后,把你的商品列表转换成Asciidoc表格语法,替换掉标记。
- 示例预处理器代码:
public class InvoiceItemsPreprocessor implements Preprocessor { private List<InvoiceItem> itemList; public InvoiceItemsPreprocessor(List<InvoiceItem> itemList) { this.itemList = itemList; } @Override public Reader process(Document document, Reader reader) { try { // 读取原始模板内容 String templateContent = IOUtils.toString(reader); // 生成Asciidoc表格格式的商品明细 StringBuilder itemsTable = new StringBuilder(); itemsTable.append("[cols=\"1,3,2,2,2\", width=\"100%\"]\n"); itemsTable.append("|===\n"); itemsTable.append("|序号|商品名称|单价|数量|小计\n"); int index = 1; for (InvoiceItem item : itemList) { double subtotal = item.getPrice() * item.getQuantity(); itemsTable.append(String.format( "|%d|%s|%.2f|%d|%.2f\n", index++, item.getName(), item.getPrice(), item.getQuantity(), subtotal )); } itemsTable.append("|===\n"); // 替换模板中的标记 String processedContent = templateContent.replace("[[invoice-items]]", itemsTable.toString()); return new StringReader(processedContent); } catch (IOException e) { throw new RuntimeException("处理模板失败", e); } } } - 注册预处理器并执行转换:
Asciidoctor asciidoctor = Asciidoctor.Factory.create(); // 注册自定义预处理器,传入商品数据 asciidoctor.javaExtensionRegistry().preprocessor(new InvoiceItemsPreprocessor(getInvoiceItems())); // 执行PDF转换 asciidoctor.convertFile(new File("invoice-template.adoc"), Options.builder().backend("pdf").build());
3. 结合通用模板引擎(Freemarker/Thymeleaf)生成完整Asciidoc
如果你的发票逻辑特别复杂(比如需要条件判断、嵌套循环),可以先把Asciidoc当成模板,用Freemarker这类成熟的模板引擎先填充数据生成完整的Asciidoc文件,再用AsciidoctorJ转PDF。
- 示例Freemarker模板(
invoice-template.ftl):= 正式发票 客户名称: ${customerName} 发票编号: ${invoiceId} 开票日期: ${invoiceDate} [cols="1,3,2,2,2"] |=== |序号|商品名称|单价|数量|小计 <#list items as item> |${item_index + 1}|${item.name}|${item.price?string("0.00")}|${item.quantity}|${(item.price * item.quantity)?string("0.00")} </#list> |=== - 先用Freemarker生成
.adoc文件,再调用AsciidoctorJ转换即可。
内容的提问来源于stack exchange,提问作者Gabriel Tiongco
相关产品推荐
相关产品推荐

