ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JQuick-Excel 字段映射实战:用 MAPPING 固化 Excel 表头与业务字段契约

JQuick-Excel 字段映射实战:用 MAPPING 固化 Excel 表头与业务字段契约 JQuick-Excel 字段映射实战用 MAPPING 固化 Excel 表头与业务字段契约tags: #JQuickExcel #Java #Excel导入导出 #字段映射 #XMLDSL简介本文围绕 JQuick-Excel 已公开示例中的MAPPING展开完整说明导出“字段到表头”和导入“表头到字段”两种方向给出 XML、服务接口、JObjectConverter、JQuickRow、JQuickExcelExportXmlParseFactory、JQuickXmlFactory的组合方式并说明怎样验收映射是否真正生效。前言Excel 文件同时面对业务人员和程序。业务人员关心“学号、姓名、年龄”是否写在正确列Java 代码则更适合使用studentNo、name、age这样的稳定字段名。两套命名并不冲突真正容易出错的是它们之间的对应关系散落在循环、列号和 if 判断里。模板改一次标题代码往往也要跟着改导入文件多一个空格数据又可能落不到预期字段。JQuick-Excel 的 XML DSL 将这种对应关系放进MAPPING。从 README-CN.md 可以确认导出和导入都支持字段映射测试资源jquick-excel.xml也给出了EXPORT WITH、IMPORT WITH、HEADERtrue和 JSON 风格映射对象的实际写法。本文只讨论这些已出现的范围不假定存在字段自动猜测、标题模糊匹配、别名回退或未声明列的自动处理。理解MAPPING最重要的一点是方向。导出规则把当前行字段写到 Excel 标题列形式是字段:表头导入规则从 Excel 的表头找到列再将单元格值放到返回行字段形式是表头:字段。左右两边刚好相反。很多“导出正常、导入为空”的问题不是流、代理或工作簿格式问题而是把导出映射原样复制到了导入规则。本篇以学生数据为例。Java 准备studentNo、name、age三个字段导出的第一行显示为“学号、姓名、年龄”同一类文件导入后读取结果仍使用英文业务字段。这样模板语言可以保持面向用户业务代码则不必依赖中文标题。环境与依赖README-CN.md 标明 JQuick-Excel 需要 Java 8 或更高版本支持xls与xlsx。本文使用 Maven 坐标io.github.paohaijiao:jquick-excel版本采用 README 中的3.6.0。XML 配置应位于类路径例如src/main/resources/jquick-excel.xml由JQuickXmlFactory通过资源名加载。dependencygroupIdio.github.paohaijiao/groupIdartifactIdjquick-excel/artifactIdversion3.6.0/version/dependency运行前还要明确四个契约。第一XML 的namespace是服务接口的全限定名。第二excel name与接口方法名对应。第三导出数据中的键必须与导出MAPPING左侧字段一致。第四导入工作表的第一行标题必须与导入MAPPING左侧文本一致因为示例使用HEADERtrue。这些条件不是额外的框架功能而是公开示例能够工作的必要前提。尤其是标题文本应把空格、大小写、全半角和业务名称变更都视为模板契约的一部分。不要把“文件能打开”误当作“字段一定能正确映射”。代码示例下面的 XML 同时定义一个导出方法和一个导入方法。导出时左侧是studentNo、name、age导入时左侧变为真实出现于首行的“学号、姓名、年龄”。SHEET、HEADER与MAPPING都采用 README 和测试 XML 中已验证的写法。?xml version1.0 encodingUTF-8?!DOCTYPEexcelsPUBLIC-//PAOHAIJIAO//DTD API EXCEL 1.0//ENclasspath:paohaijiao/dtd/Jquick-excel.dtdexcelsnamespacecom.example.StudentExcelServiceexcelnameexportStudentsreturnClassvoid![CDATA[ EXPORT WITH SHEET学生表, HEADERtrue, MAPPING{ studentNo:学号, name:姓名, age:年龄 } ]]/excelexcelnameimportStudentsreturnClassjava.util.List![CDATA[ IMPORT WITH SHEET学生表, HEADERtrue, MAPPING{ 学号:studentNo, 姓名:name, 年龄:age } ]]/excel/excels服务接口只声明 XML 中存在的方法。README 的示例为参数使用Param并让导入方法返回ListJQuickRow。本文沿用这一调用边界不在接口里增加没有被公开示例验证的参数类型或代理约定。importcom.github.paohaijiao.statement.JQuickRow;importcom.github.paohaijiao.xml.param.Param;importjava.util.List;publicinterfaceStudentExcelService{voidexportStudents(Param(field)Stringfield,Param(value)Stringvalue);ListJQuickRowimportStudents(Param(field)Stringfield,Param(value)Stringvalue);}导出时先准备 Map 列表调用JObjectConverter.convert(data)再通过JQuickRow.toRows(...)得到导出解析器使用的行列表。README 已给出了这一完整转换路径。这里使用LinkedHashMap只是让示例的数据构造顺序更容易阅读真正决定导出列的仍然是 XML 中的MAPPING。importcom.github.paohaijiao.convert.JObjectConverter;importcom.github.paohaijiao.statement.JQuickRow;importcom.github.paohaijiao.xml.JQuickFactory;importcom.github.paohaijiao.xml.JQuickXmlFactory;importcom.github.paohaijiao.xml.parse.JQuickParseHandler;importcom.github.paohaijiao.xml.parse.excel.JQuickExcelExportXmlParseFactory;importjava.io.FileOutputStream;importjava.io.OutputStream;importjava.util.ArrayList;importjava.util.LinkedHashMap;importjava.util.List;importjava.util.Map;ListMapString,ObjectstudentsnewArrayList();MapString,ObjectstudentnewLinkedHashMap();student.put(studentNo,S1001);student.put(name,Alice);student.put(age,20);students.add(student);ListJQuickRowrowsJQuickRow.toRows(JObjectConverter.convert(students));try(OutputStreamoutputnewFileOutputStream(students.xlsx)){JQuickParseHandlerparsernewJQuickExcelExportXmlParseFactory(rows,output);JQuickFactoryfactorynewJQuickXmlFactory(parser,jquick-excel.xml);StudentExcelServiceservicefactory.createApi(StudentExcelService.class);service.exportStudents(field,value);}导入时需要的是输入流和JContext。即使本例不使用字典转换也仍按 README 的已验证构造方式传入一个JContext。XML 中HEADERtrue表示第一行参与标题识别因此前一段代码生成的“学生表”可以作为本段的输入文件。importcom.github.paohaijiao.context.JContext;importcom.github.paohaijiao.statement.JQuickRow;importcom.github.paohaijiao.xml.JQuickFactory;importcom.github.paohaijiao.xml.JQuickXmlFactory;importcom.github.paohaijiao.xml.parse.JQuickParseHandler;importcom.github.paohaijiao.xml.parse.excel.JQuickExcelImportXmlParseFactory;importjava.io.FileInputStream;importjava.io.InputStream;importjava.util.List;try(InputStreaminputnewFileInputStream(students.xlsx)){JContextcontextnewJContext();JQuickParseHandlerparsernewJQuickExcelImportXmlParseFactory(context,input);JQuickFactoryfactorynewJQuickXmlFactory(parser,jquick-excel.xml);StudentExcelServiceservicefactory.createApi(StudentExcelService.class);ListJQuickRowimportedservice.importStudents(field,value);System.out.println(imported.size());}如果业务字段已经是id、name、gender、age也可以直接采用测试 XML 的导出结构id:主键、name:姓名、gender:性别、age:年龄。导入时则需要把同一份标题放在左侧例如姓名:name。关键不在字段语言而在两端的方向必须与当前操作一致。原理说明XML 服务代理的入口是JQuickXmlFactory。它读取资源中的excels定义并根据namespace与createApi的接口建立服务代理。调用exportStudents时代理查找同名excel的EXPORT WITH规则调用importStudents时代理查找对应的IMPORT WITH规则。方法名是 XML 规则的路由键因此改接口方法名时必须同步改 XML 的name。导出解析器JQuickExcelExportXmlParseFactory在构造时接收两样内容已经准备好的ListJQuickRow和输出流。每个JQuickRow提供当前记录的字段值。对于导出MAPPING{studentNo:学号}左侧studentNo用来从当前行取值右侧“学号”用于表头。再结合HEADERtrue生成的首行就是“学号”。后续行在这一列写入studentNo的值例如S1001。导入解析器JQuickExcelImportXmlParseFactory接收JContext与输入流。规则先由SHEET学生表定位数据页再由HEADERtrue赋予第一行标题语义。对于导入MAPPING{学号:studentNo}左侧“学号”是输入文件中要找的列标题右侧studentNo是返回JQuickRow采用的字段名。数据行中的S1001因此可通过studentNo被后续业务代码读取。这解释了一个实用事实映射的职责是列归属不是数据加工。比如性别代码转中文、日期输出格式化等应使用 README 已列出的TRANSFORM或FORMAT范围而不是试图通过把标题改成表达式来实现。映射也不负责自动推断缺失字段。若 Java 行里没有studentNo或者导入首行没有“学号”本文所依据的公开配置没有承诺会替开发者猜测其它字段。映射顺序在导出场景也很重要。业务人员看到的列顺序应由 XML 显式定义而不是由 Map 的偶然遍历顺序决定。将MAPPING作为模板的一部分后可以对 XML 做评审第一列是不是学号第二列是不是姓名新增列是否同步提供了业务字段。代码和模板的变更边界会更清晰。对于双向模板建议把导出映射的右侧和导入映射的左侧成对维护。例如导出age:年龄导入就写年龄:age。这样导出的工作簿能够作为导入样例人工验收也只需要关注标题是否完全一致。这里说的是配置一致性并不代表框架会自动生成另一方向的规则两份规则仍应明确写在 XML 中。注意事项第一不能混淆映射方向。EXPORT WITH的左侧是行字段、右侧是 Excel 标题IMPORT WITH的左侧是 Excel 标题、右侧是返回行字段。把{studentNo:学号}用于导入会让规则寻找名为studentNo的标题而不是寻找“学号”。第二导入标题要以实际首行文本为准。使用HEADERtrue时输入工作表第一行不是普通数据。标题前后空格、错别字、全半角不同、名称改版都应在测试文件中验证并同步更新 XML。不要依赖未在 README 或测试配置中说明的模糊匹配、自动别名或按字段名兜底。第三工作表与标题是两个层次。SHEET学生表选择的是页签“学号”是该页第一行的单元格内容。工作表名写对但首行标题不一致映射仍然不能按预期工作标题写对但选错工作表同样会读到错误区域。第四Java 数据键必须匹配导出映射左侧。上例 XML 使用studentNoJava 就应写student.put(studentNo, S1001)而不是写student.put(学号, S1001)。表头属于 XML 右侧展示名业务数据仍以字段名组织。数据值为数字、日期等类型时也不要为了映射而强制转换成字符串。第五XML 必须在运行时类路径中。JQuickXmlFactory(parser, jquick-excel.xml)读取的是资源名仅在本地目录存在文件并不能保证打包后可加载。还要检查 XML 的namespace、接口全限定名、excel name、接口方法名和returnClass是否互相对应。第六输出流不能在代理调用前关闭输入流也应在导入完成前保持可读。示例使用 try-with-resources使资源在代理方法执行完成后自动关闭。对于 Web 上传和下载应把受控的请求输入流、响应输出流接入相同构造链路而不要为了字段映射去绕开 XML 代理。第七验收应覆盖内容而不仅是文件存在。导出后打开“学生表”确认第一行依次为“学号、姓名、年龄”第二行对应S1001、Alice、20。再将文件导入检查返回行数量并确认业务代码能以studentNo、name、age取到相同值。这样可以同时验证SHEET、HEADER、MAPPING与数据准备。总结MAPPING是 JQuick-Excel 中连接 Excel 展示字段和 Java 业务字段的明确契约。导出遵循“字段到表头”导入遵循“表头到字段”两者的左右方向相反但应围绕同一份模板标题成对维护。结合JObjectConverter.convert、JQuickRow.toRows、导出或导入 XML 解析工厂以及JQuickXmlFactory创建的服务代理可以把列规则集中在 XML把业务数据准备保留在 Java。实践中最值得坚持的是显式验证字段名对齐映射左侧工作表名对齐SHEET首行标题对齐导入映射左侧服务接口对齐 XML 的 namespace 和方法名。这样表头变更会变成可见的配置改动而不是隐藏在列号循环中的运行时问题。
返回列表