生成XML文件-这样子用更明了
本文以“项目报告与附件 XML 生成工具”为主题,分享如何在生产环境中使用 JAXB 与 Lombok 快速构建 Java 对象模型,并将后台业务数据转换成层次分明、易于扩展的 XML 文档。
为什么要这样做?
-
可读性:层次分明,任何人一眼就能找到“报告编号”“附件列表”“每个附件的属性”。
-
可扩展性:未来要加“上传人”“审核状态”……只需在模型里多写几个字段即可。
-
兼容性:XML 是标准,大部分系统都能轻松解析与交互。
一、起因与需求
在大型后端系统中,往往存在“报表+附件”或“档案+文档”这样的场景,需要把数据库或服务返回的文件树结构导出成标准 XML,供上游系统或第三方存储解析。
-
业务侧数据:
List<Map<String,Object>> fileTreeList,每条Map包含分类名称 (name) 与该分类下若干FileInfo列表。 -
目标 XML:
<report title="项目报告与附件"> <ReportID title="报告编号">RPT-20250509-001</ReportID> <Attachments title="所有附件"> <Category title="项目评审材料"> <Attachment title="设计方案.pdf"> <SEQ title="序号">1</SEQ> <PATH title="存储路径">/path/设计方案.pdf</PATH> <FORMAT title="格式">PDF</FORMAT> <NAME title="文件名">设计方案</NAME> <SIZE title="文件大小">2.4MB</SIZE> <CREATED title="创建时间">2025-05-08 14:22:30</CREATED> </Attachment> <!-- …更多附件… --> </Category> <!-- …更多分类… --> </Attachments> </report>
二、模型设计
借助 JAXB(Java XML Binding)进行序列化,利用 Lombok 省略样板代码,使模型定义简洁明了。
XML对象:ReportXml
@XmlRootElement(name = "report") @XmlAccessorType(XmlAccessType.FIELD) @Data public class ReportXml { /** 报表标题,对应根节点的 title 属性 */ @XmlAttribute(name = "title") private String title; /** 报告编号节点 */ @XmlElement(name = "ReportID") private ReportID reportID; /** 附件容器节点 */ @XmlElement(name = "Attachments") private Attachments attachments; @Data @XmlAccessorType(XmlAccessType.FIELD) public static class ReportID { @XmlAttribute(name = "title") private String title; // 如 "报告编号" @XmlValue private String value; // 编号内容 } @Data @XmlAccessorType(XmlAccessType.FIELD) public static class Attachments { @XmlAttribute(name = "title") private String title; // 如 "所有附件" @XmlElement(name = "Category") private List<Category> categories; // 多个分类 } @Data @XmlAccessorType(XmlAccessType.FIELD) public static class Category { @XmlAttribute(name = "title") private String title; // 分类名称 @XmlElement(name = "Attachment") private List<Attachment> attachments; // 多个附件 } @Data @XmlAccessorType(XmlAccessType.FIELD) public static class Attachment { @XmlAttribute(name = "title") private String title; // 文件名(带扩展名) @XmlElement(name = "SEQ") private TagWithTitle seq; // 序号 @XmlElement(name = "PATH") private TagWithTitle path; // 存储路径 @XmlElement(name = "FORMAT") private TagWithTitle format; // 格式 @XmlElement(name = "NAME") private TagWithTitle name; // 文件名(不含扩展名) @XmlElement(name = "SIZE") private TagWithTitle size; // 文件大小 @XmlElement(name = "CREATED") private TagWithTitle created; // 创建时间 } @Data @XmlAccessorType(XmlAccessType.FIELD) public static class TagWithTitle { @XmlAttribute(name = "title") private String title; // 节点描述,如“序号”“存储路径” @XmlValue private String value; // 节点值 } }
文件对象:FileInfo
/** * 业务层通用文件信息对象 */ @Data public class FileInfo { /** * 序号 */ private int pieceNo; /** * 文件在磁盘或存储服务上的完整路径 */ private String filePath; /** * 文件格式(如 "PDF"、"JPG" 等) */ private String format; /** * 计算机端不带扩展名的文件名 */ private String computerFileName; /** * 带单位的文件大小字符串(如 "2.4MB") */ private String size; /** * 文件创建或生成时间字符串(格式 "yyyy-MM-dd HH:mm:ss") */ private String createTime; /** * 原始文件名(带扩展名) */ private String originalFilename; }
三、核心代码
/** * 生成报告 XML 文件 * * @param fileTreeList 项目文件树列表 * @param outFilePath 输出文件路径 * @param reportId 报告编号 * @return 文件路径 */ private static String createReportXmlFile(List<Map<String, Object>> fileTreeList, String outFilePath, String reportId) { // 1. 根与编号 ReportXml xml = new ReportXml(); xml.setTitle("项目报告与附件"); ReportXml.ReportID rid = new ReportXml.ReportID(); rid.setTitle("报告编号"); rid.setValue(reportId); xml.setReportID(rid); // 2. 附件容器 ReportXml.Attachments atts = new ReportXml.Attachments(); atts.setTitle("所有附件"); // 3. 分类和附件 List<ReportXml.Category> categories = new ArrayList<>(); for (Map<String, Object> catMap : fileTreeList) { ReportXml.Category cat = new ReportXml.Category(); cat.setTitle((String) catMap.get("name")); List<FileInfo> files = (List<FileInfo>) catMap.get("fileList"); List<ReportXml.Attachment> atList = new ArrayList<>(); int seq = 1; for (FileInfo info : files) { ReportXml.Attachment att = new ReportXml.Attachment(); att.setTitle(info.getOriginalFilename()); att.setSeq(newTag("序号", String.valueOf(seq++))); att.setPath(newTag("存储路径", info.getFilePath())); String fmt = info.getFilePath() .substring( info.getFilePath().lastIndexOf('.') + 1); att.setFormat(newTag("格式", fmt.toUpperCase())); String nameOnly = info.getOriginalFilename() .replace("." + fmt, ""); att.setName(newTag("文件名", nameOnly)); att.setSize(newTag("文件大小", info.getSize())); att.setCreated(newTag("创建时间", info.getCreateTime())); atList.add(att); } cat.setAttachments(atList); categories.add(cat); } atts.setCategories(categories); xml.setAttachments(atts); // 4. 写文件 try { JAXBContext ctx = JAXBContext.newInstance(ReportXml.class); Marshaller m = ctx.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); File out = new File(outFilePath); if (out.getParentFile() != null) out.getParentFile().mkdirs(); m.marshal(xml, out); return out.getAbsolutePath(); } catch (Exception e) { throw new RuntimeException("生成报告 XML 失败", e); } }
/** * 快速构造带 title 的节点 * * @param title 节点标题 * @param value 节点值 * @return null */ private static ReportXml.TagWithTitle newTag(String title, String value) { ReportXml.TagWithTitle tag = new ReportXml.TagWithTitle(); tag.setTitle(title); tag.setValue(value); return tag; }

浙公网安备 33010602011771号