女孩做网站工作辛苦吗,vs2012 建网站,泉州企业自助建站,网站建设合同要注意什么1.业务场景分析实际业务中#xff0c;我们常遇到一对多甚至多对多的数据关系。例如#xff0c;一个主实体包含多个一级子项#xff0c;每个一级子项又包含多个二级子项。传统平面表格难以直观展示这种层次关系#xff0c;需要合并单元格和多级表头来优化可读性。2.实现结果…1.业务场景分析实际业务中我们常遇到一对多甚至多对多的数据关系。例如一个主实体包含多个一级子项每个一级子项又包含多个二级子项。传统平面表格难以直观展示这种层次关系需要合并单元格和多级表头来优化可读性。2.实现结果3.依赖dependencies !-- Apache POI 核心库 -- dependency groupIdorg.apache.poi/groupId artifactIdpoi/artifactId version4.1.2/version /dependency !-- 用于处理 .xlsx 格式的Excel文件Office Open XML -- dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version4.1.2/version /dependency /dependencies4.代码package com.example.study.controller.easypoi; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.streaming.SXSSFSheet; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.List; /** * 嵌套列表导出控制器 - 增强空集合处理版本 * 使用Spring Boot的RestController注解标识这是一个RESTful控制器[6,7](ref) * 使用RequestMapping注解定义控制器的基础路径[6](ref) */ RestController RequestMapping(/api/export/two) public class NestedListExportTwoController { /** * 主实体数据类 - 代表最外层的数据结构 * 使用Java Bean规范包含私有字段和公共getter/setter方法 */ public static class MainEntity { private String mainId; // 主实体ID private String mainName; // 主实体名称 private LocalDate createTime; // 创建时间使用Java 8的LocalDate类型 private ListFirstLevelItem firstLevelItems; // 一级子项列表可能为null或空 // 默认构造器Spring框架反射创建对象时需要[6](ref) public MainEntity() {} // 全参数构造器便于快速创建测试数据 public MainEntity(String mainId, String mainName, LocalDate createTime, ListFirstLevelItem firstLevelItems) { this.mainId mainId; this.mainName mainName; this.createTime createTime; this.firstLevelItems firstLevelItems; } // Getters and Setters - 遵循Java Bean规范便于框架访问属性[6](ref) public String getMainId() { return mainId; } public void setMainId(String mainId) { this.mainId mainId; } public String getMainName() { return mainName; } public void setMainName(String mainName) { this.mainName mainName; } public LocalDate getCreateTime() { return createTime; } public void setCreateTime(LocalDate createTime) { this.createTime createTime; } public ListFirstLevelItem getFirstLevelItems() { return firstLevelItems; } public void setFirstLevelItems(ListFirstLevelItem firstLevelItems) { this.firstLevelItems firstLevelItems; } } /** * 一级子项数据类 - 代表嵌套的第一层数据结构 */ public static class FirstLevelItem { private String firstId; // 一级子项ID private String firstName; // 一级子项名称 private ListSecondLevelItem secondLevelItems; // 二级子项列表 private Integer firstLevelCount; // 一级子项数量可自动计算 public FirstLevelItem() {} public FirstLevelItem(String firstId, String firstName, ListSecondLevelItem secondLevelItems, Integer firstLevelCount) { this.firstId firstId; this.firstName firstName; this.secondLevelItems secondLevelItems; this.firstLevelCount firstLevelCount; } // Getters and Setters public String getFirstId() { return firstId; } public void setFirstId(String firstId) { this.firstId firstId; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName firstName; } public ListSecondLevelItem getSecondLevelItems() { return secondLevelItems; } public void setSecondLevelItems(ListSecondLevelItem secondLevelItems) { this.secondLevelItems secondLevelItems; } /** * 智能获取一级子项数量优先使用显式设置的值否则计算二级子项列表大小 * 使用Optional类安全处理null值避免空指针异常[1](ref) */ public Integer getFirstLevelCount() { return firstLevelCount ! null ? firstLevelCount : (secondLevelItems ! null ? secondLevelItems.size() : 0); } public void setFirstLevelCount(Integer firstLevelCount) { this.firstLevelCount firstLevelCount; } } /** * 二级子项数据类 - 代表嵌套的第二层数据结构 */ public static class SecondLevelItem { private String secondId; // 二级子项ID private String secondName; // 二级子项名称 private Integer secondCount; // 二级子项数量 private Integer secondYear; // 二级子项年份 public SecondLevelItem() {} public SecondLevelItem(String secondId, String secondName, Integer secondCount, Integer secondYear) { this.secondId secondId; this.secondName secondName; this.secondCount secondCount; this.secondYear secondYear; } // Getters and Setters public String getSecondId() { return secondId; } public void setSecondId(String secondId) { this.secondId secondId; } public String getSecondName() { return secondName; } public void setSecondName(String secondName) { this.secondName secondName; } public Integer getSecondCount() { return secondCount; } public void setSecondCount(Integer secondCount) { this.secondCount secondCount; } public Integer getSecondYear() { return secondYear; } public void setSecondYear(Integer secondYear) { this.secondYear secondYear; } } /** * 生成包含各种空集合情况的测试数据 * 模拟真实业务场景中可能遇到的各种数据边界情况 * 使用return注解明确返回值类型和含义[1](ref) */ private ListMainEntity generateTestData() { ListMainEntity mainEntities new ArrayList(); // 主实体1正常数据有完整的一级和二级子项- 典型业务场景 ListFirstLevelItem firstLevelItems1 new ArrayList(); ListSecondLevelItem secondItems1 Arrays.asList( new SecondLevelItem(S12-001, 二级子项1, 10, null), new SecondLevelItem(S22-001, 二级子项2, 14, 2025) ); firstLevelItems1.add(new FirstLevelItem(S1-001, 一级子项1, secondItems1, 2)); ListSecondLevelItem secondItems2 Arrays.asList( new SecondLevelItem(S12-002, 二级子项1, 14, 2025) ); firstLevelItems1.add(new FirstLevelItem(S2-002, null, secondItems2, 1)); MainEntity mainEntity1 new MainEntity(M001, 主实体1, LocalDate.of(2024, 1, 15), firstLevelItems1); // 主实体2正常数据 - 另一个典型业务场景 ListFirstLevelItem firstLevelItems2 new ArrayList(); ListSecondLevelItem secondItems3 Arrays.asList( new SecondLevelItem(S12-002, 二级子项2, 5, 2023), new SecondLevelItem(S22-002, 二级子项2, 8, 2024), new SecondLevelItem(S32-002, 二级子项3, 12, 2025) ); firstLevelItems2.add(new FirstLevelItem(S1-003, 一级子项3, secondItems3, 3)); MainEntity mainEntity2 new MainEntity(M002, 主实体2, LocalDate.of(2024, 3, 20), firstLevelItems2); // 主实体3一级子项列表为null测试空指针处理- 边界情况测试 MainEntity mainEntity3 new MainEntity(M003, 主实体3, null, null); // 主实体4一级子项列表不为空但二级子项列表为null - 边界情况测试 ListFirstLevelItem firstLevelItems4 new ArrayList(); FirstLevelItem firstItem4 new FirstLevelItem(S1-004, 一级子项4, null, 0); // 二级子项为null firstLevelItems4.add(firstItem4); MainEntity mainEntity4 new MainEntity(M004, 主实体4, null, firstLevelItems4); // 主实体5一级子项列表为空列表非null- 边界情况测试 MainEntity mainEntity5 new MainEntity(M005, 主实体5, LocalDate.of(2024, 5, 1), Collections.emptyList()); // 主实体6一级子项有数据但二级子项为空列表 - 边界情况测试 ListFirstLevelItem firstLevelItems6 new ArrayList(); FirstLevelItem firstItem6 new FirstLevelItem(S1-006, 一级子项6, Collections.emptyList(), 0); firstLevelItems6.add(firstItem6); MainEntity mainEntity6 new MainEntity(M006, 主实体6, null, firstLevelItems6); // 将所有测试数据添加到返回列表 mainEntities.add(mainEntity1); mainEntities.add(mainEntity2); mainEntities.add(mainEntity3); mainEntities.add(mainEntity4); mainEntities.add(mainEntity5); mainEntities.add(mainEntity6); return mainEntities; } /** * 增强的嵌套列表导出接口 - 支持各种空集合情况 * 使用GetMapping注解映射HTTP GET请求[6,7](ref) * 使用ResponseEntitybyte[]返回文件下载响应支持精确控制HTTP响应头[6](ref) * * param response HttpServletResponse对象用于设置响应参数 * return ResponseEntity包含Excel文件的字节数组和下载头信息 * throws IOException 当文件操作出现IO异常时抛出 */ GetMapping(/nested-list) public ResponseEntitybyte[] exportNestedList(HttpServletResponse response) throws IOException { // 生成测试数据 ListMainEntity dataList generateTestData(); // 创建SXSSFWorkbook使用流式处理避免内存溢出100行缓存 SXSSFWorkbook workbook new SXSSFWorkbook(100); Sheet sheet workbook.createSheet(嵌套列表); // 创建单元格样式 CellStyle headerStyle createHeaderStyle(workbook); CellStyle dataStyle createDataStyle(workbook); // 创建表格标题和表头 createTableHeader(sheet, headerStyle); // 存储合并区域信息 ListCellRangeAddress mergeRegions new ArrayList(); int rowIndex 3; // 数据从第4行开始前3行被表头占用 // 遍历所有主实体数据 for (MainEntity mainEntity : dataList) { int mainEntityStartRow rowIndex; // 记录当前主实体的起始行 int mainEntityRowCount 0; // 记录当前主实体占用的行数 // 关键修复安全获取一级子项列表使用Optional避免空指针异常[1](ref) ListFirstLevelItem firstLevelItems Optional.ofNullable(mainEntity.getFirstLevelItems()) .orElse(Collections.emptyList()); // 处理空一级子项列表的情况 - 为主实体创建一行基础数据 if (firstLevelItems.isEmpty()) { Row row sheet.createRow(rowIndex); createMainEntityRow(row, mainEntity, dataStyle); // 为其他列创建空单元格保持表格结构完整 for (int col 2; col 8; col) { Cell emptyCell row.createCell(col); emptyCell.setCellValue(); emptyCell.setCellStyle(dataStyle); } mainEntityRowCount 1; rowIndex; } else { // 处理有一级子项的情况 for (FirstLevelItem firstItem : firstLevelItems) { // 关键修复安全获取二级子项列表处理null情况 ListSecondLevelItem secondItems Optional.ofNullable(firstItem.getSecondLevelItems()) .orElse(Collections.emptyList()); // 计算当前一级子项需要的行数至少1行 int firstLevelRows Math.max(1, secondItems.size()); int firstLevelStartRow rowIndex; // 创建当前一级子项的所有数据行 for (int j 0; j firstLevelRows; j) { Row row sheet.createRow(rowIndex); // 处理主实体列只在每组的首行填充 if (j 0) { createMainEntityCells(row, mainEntity, dataStyle); } // 处理一级子项列只在每组的首行填充 if (j 0) { createFirstLevelCells(row, firstItem, dataStyle); } // 处理二级子项列 if (j secondItems.size()) { SecondLevelItem secondItem secondItems.get(j); createSecondLevelCells(row, secondItem, dataStyle); } else { // 如果没有二级子项数据创建空单元格保持表格对齐 for (int k 4; k 7; k) { Cell emptyCell row.createCell(k); emptyCell.setCellValue(); emptyCell.setCellStyle(dataStyle); } } // I列一级子项数量每行都填充 Cell firstLevelCountCell row.createCell(8); firstLevelCountCell.setCellValue(firstItem.getFirstLevelCount() ! null ? firstItem.getFirstLevelCount() : 0); firstLevelCountCell.setCellStyle(dataStyle); rowIndex; // 移动到下一行 } // 如果当前一级子项有多行数据添加合并区域信息 if (firstLevelRows 1) { addFirstLevelMergeRegions(mergeRegions, firstLevelStartRow, firstLevelRows); } mainEntityRowCount firstLevelRows; // 累计主实体行数 } } // 如果主实体占用多行添加合并区域信息 if (mainEntityRowCount 1) { addMainEntityMergeRegions(mergeRegions, mainEntityStartRow, mainEntityRowCount); } } // 应用所有合并区域到工作表 for (CellRangeAddress region : mergeRegions) { sheet.addMergedRegion(region); } // 自动调整列宽 autoSizeColumnsWithTracking(sheet, 10); // 将工作簿写入字节数组输出流 ByteArrayOutputStream baos new ByteArrayOutputStream(); workbook.write(baos); workbook.dispose(); // 清理临时文件 // 设置HTTP响应头触发文件下载[6](ref) HttpHeaders headers new HttpHeaders(); headers.add(Content-Disposition, attachment; filenamenested_list_export.xlsx); headers.add(Content-Type, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); // 返回包含Excel文件的响应实体 return ResponseEntity.ok() .headers(headers) .body(baos.toByteArray()); } /** * 创建主实体单元格A列、B列、J列 * 这些单元格在合并区域中会被垂直合并 * * param row 当前行对象 * param mainEntity 主实体数据 * param dataStyle 数据单元格样式 */ private void createMainEntityCells(Row row, MainEntity mainEntity, CellStyle dataStyle) { // A列主实体ID Cell mainIdCell row.createCell(0); mainIdCell.setCellValue(mainEntity.getMainId() ! null ? mainEntity.getMainId() : ); mainIdCell.setCellStyle(dataStyle); // B列实体名 Cell mainNameCell row.createCell(1); mainNameCell.setCellValue(mainEntity.getMainName() ! null ? mainEntity.getMainName() : ); mainNameCell.setCellStyle(dataStyle); // J列主实体创建时间格式化为yyyy-MM-dd字符串 Cell createTimeCell row.createCell(9); if (mainEntity.getCreateTime() ! null) { createTimeCell.setCellValue(mainEntity.getCreateTime().format(DateTimeFormatter.ofPattern(yyyy-MM-dd))); } else { createTimeCell.setCellValue(); } createTimeCell.setCellStyle(dataStyle); } /** * 创建一级子项单元格C列、D列 * 这些单元格在合并区域中会被垂直合并 * * param row 当前行对象 * param firstItem 一级子项数据 * param dataStyle 数据单元格样式 */ private void createFirstLevelCells(Row row, FirstLevelItem firstItem, CellStyle dataStyle) { // C列一级子项ID Cell firstIdCell row.createCell(2); firstIdCell.setCellValue(firstItem.getFirstId() ! null ? firstItem.getFirstId() : ); firstIdCell.setCellStyle(dataStyle); // D列一级子项名称 Cell firstNameCell row.createCell(3); firstNameCell.setCellValue(firstItem.getFirstName() ! null ? firstItem.getFirstName() : ); firstNameCell.setCellStyle(dataStyle); } /** * 创建二级子项单元格E列、F列、G列、H列 * 这些单元格不会被合并每行都有独立数据 * * param row 当前行对象 * param secondItem 二级子项数据 * param dataStyle 数据单元格样式 */ private void createSecondLevelCells(Row row, SecondLevelItem secondItem, CellStyle dataStyle) { // E列二级子项ID Cell secondIdCell row.createCell(4); secondIdCell.setCellValue(secondItem.getSecondId() ! null ? secondItem.getSecondId() : ); secondIdCell.setCellStyle(dataStyle); // F列二级子项名称 Cell secondNameCell row.createCell(5); secondNameCell.setCellValue(secondItem.getSecondName() ! null ? secondItem.getSecondName() : ); secondNameCell.setCellStyle(dataStyle); // G列二级子项数量处理null值 Cell secondCountCell row.createCell(6); if (secondItem.getSecondCount() ! null) { secondCountCell.setCellValue(secondItem.getSecondCount()); } else { secondCountCell.setCellValue(0); } secondCountCell.setCellStyle(dataStyle); // H列二级子项年份处理null值 Cell secondYearCell row.createCell(7); if (secondItem.getSecondYear() ! null) { secondYearCell.setCellValue(secondItem.getSecondYear()); } else { secondYearCell.setCellValue(); } secondYearCell.setCellStyle(dataStyle); } /** * 创建完整的主实体行用于空一级子项的情况 * 当一级子项为空时只为最外层主实体创建一行数据 * * param row 当前行对象 * param mainEntity 主实体数据 * param dataStyle 数据单元格样式 */ private void createMainEntityRow(Row row, MainEntity mainEntity, CellStyle dataStyle) { createMainEntityCells(row, mainEntity, dataStyle); } /** * 添加一级子项合并区域 * 当一级子项对应多个二级子项时需要合并一级子项的单元格 * * param mergeRegions 合并区域列表 * param startRow 起始行索引 * param rowCount 需要合并的行数 */ private void addFirstLevelMergeRegions(ListCellRangeAddress mergeRegions, int startRow, int rowCount) { if (rowCount 1) { // 合并C列一级子项ID mergeRegions.add(new CellRangeAddress(startRow, startRow rowCount - 1, 2, 2)); // 合并D列一级子项名称 mergeRegions.add(new CellRangeAddress(startRow, startRow rowCount - 1, 3, 3)); // 合并I列一级子项数量 mergeRegions.add(new CellRangeAddress(startRow, startRow rowCount - 1, 8, 8)); } } /** * 添加主实体合并区域 * 当主实体有多个一级子项时需要合并主实体的单元格 * * param mergeRegions 合并区域列表 * param startRow 起始行索引 * param rowCount 需要合并的行数 */ private void addMainEntityMergeRegions(ListCellRangeAddress mergeRegions, int startRow, int rowCount) { if (rowCount 1) { // 合并A列主实体ID mergeRegions.add(new CellRangeAddress(startRow, startRow rowCount - 1, 0, 0)); // 合并B列实体名 mergeRegions.add(new CellRangeAddress(startRow, startRow rowCount - 1, 1, 1)); // 合并J列主实体创建时间 mergeRegions.add(new CellRangeAddress(startRow, startRow rowCount - 1, 9, 9)); } } /** * 创建表头单元格样式 * 使用粗体、居中显示增强可读性 * * param workbook 工作簿对象 * return 配置好的单元格样式 */ private CellStyle createHeaderStyle(Workbook workbook) { CellStyle style workbook.createCellStyle(); Font font workbook.createFont(); font.setBold(true); // 设置粗体 font.setFontHeightInPoints((short) 12); // 设置字体大小 style.setFont(font); style.setAlignment(HorizontalAlignment.CENTER); // 水平居中 style.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中 return style; } /** * 创建数据单元格样式 * 使用居中对齐保持数据整洁 * * param workbook 工作簿对象 * return 配置好的单元格样式 */ private CellStyle createDataStyle(Workbook workbook) { CellStyle style workbook.createCellStyle(); style.setAlignment(HorizontalAlignment.CENTER); // 水平居中 style.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中 return style; } /** * 创建表格标题和表头 * 使用三级表头结构清晰展示数据层级关系 * * param sheet 工作表对象 * param headerStyle 表头单元格样式 */ private void createTableHeader(Sheet sheet, CellStyle headerStyle) { // 第一行表头大标题合并A-J列 Row row1 sheet.createRow(0); Cell titleCell row1.createCell(0); titleCell.setCellValue(嵌套列表导出); titleCell.setCellStyle(headerStyle); sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 9)); // 第二行表头分组标题展示数据层级关系 Row row2 sheet.createRow(1); // 主实体分组A-B列 Cell mainEntityHeader1 row2.createCell(0); mainEntityHeader1.setCellValue(主实体); mainEntityHeader1.setCellStyle(headerStyle); sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 1)); // 一级子项分组C-D列 Cell firstLevelHeader1 row2.createCell(2); firstLevelHeader1.setCellValue(一级子项列表); firstLevelHeader1.setCellStyle(headerStyle); sheet.addMergedRegion(new CellRangeAddress(1, 1, 2, 3)); // 二级子项分组E-H列 Cell secondLevelHeader row2.createCell(4); secondLevelHeader.setCellValue(二级子项列表); secondLevelHeader.setCellStyle(headerStyle); sheet.addMergedRegion(new CellRangeAddress(1, 1, 4, 7)); // 一级子项数量I列 Cell firstLevelHeader2 row2.createCell(8); firstLevelHeader2.setCellValue(一级子项列表); firstLevelHeader2.setCellStyle(headerStyle); // 主实体创建时间J列 Cell mainEntityHeader2 row2.createCell(9); mainEntityHeader2.setCellValue(主实体); mainEntityHeader2.setCellStyle(headerStyle); // 第三行表头具体列名 Row row3 sheet.createRow(2); String[] columnHeaders { 主实体ID, 实体名, 一级子项, 一级子项名称, 二级子项ID, 二级子项名称, 二级子项数量, 二级子项年份, 一级子项数量, 主实体创建时间 }; // 为每个列创建表头单元格 for (int i 0; i columnHeaders.length; i) { Cell headerCell row3.createCell(i); headerCell.setCellValue(columnHeaders[i]); headerCell.setCellStyle(headerStyle); } } /** * 自动调整列宽增强版 * 针对SXSSFWorkbook的特殊处理避免自动调整列宽时的性能问题 * * param sheet 工作表对象 * param columnCount 需要调整的列数 */ private void autoSizeColumnsWithTracking(Sheet sheet, int columnCount) { // 针对SXSSFSheet的特殊处理跟踪列以支持自动调整列宽 if (sheet instanceof SXSSFSheet) { SXSSFSheet sxsSheet (SXSSFSheet) sheet; for (int i 0; i columnCount; i) { sxsSheet.trackColumnForAutoSizing(i); } } // 调整每一列的宽度 for (int i 0; i columnCount; i) { int minWidth 2000; // 设置最小列宽约2个字符 sheet.setColumnWidth(i, minWidth); sheet.autoSizeColumn(i); // 自动调整列宽 int currentWidth sheet.getColumnWidth(i); // 确保列宽不小于最小值 if (currentWidth minWidth) { sheet.setColumnWidth(i, minWidth); currentWidth minWidth; } // 增加10%的边距避免内容过于拥挤 int newWidth (int)(currentWidth * 1.1); if (newWidth 255 * 256) { // Excel最大列宽限制 sheet.setColumnWidth(i, newWidth); } } } /** * 测试数据接口 - 返回增强的测试数据信息 * 使用GetMapping注解映射HTTP GET请求[7](ref) * * return 包含测试数据和统计信息的Map对象 */ GetMapping(/test-data) public MapString, Object getTestData() { ListMainEntity data generateTestData(); MapString, Object result new HashMap(); result.put(code, 200); result.put(message, 成功 - 增强的空集合处理版本); result.put(data, data); result.put(count, data.size()); // 统计各种边界情况的行数用于验证导出逻辑 int totalRows 0; int nullFirstLevelCount 0; int emptyFirstLevelCount 0; int nullSecondLevelCount 0; int emptySecondLevelCount 0; // 遍历数据统计各种边界情况 for (MainEntity entity : data) { if (entity.getFirstLevelItems() null) { nullFirstLevelCount; totalRows 1; // 即使一级子项为null也会创建一行 } else if (entity.getFirstLevelItems().isEmpty()) { emptyFirstLevelCount; totalRows 1; // 即使一级子项为空列表也会创建一行 } else { for (FirstLevelItem firstItem : entity.getFirstLevelItems()) { if (firstItem.getSecondLevelItems() null) { nullSecondLevelCount; totalRows 1; } else if (firstItem.getSecondLevelItems().isEmpty()) { emptySecondLevelCount; totalRows 1; } else { totalRows firstItem.getSecondLevelItems().size(); } } } } // 添加统计信息到返回结果 result.put(totalRows, totalRows); result.put(excelColumns, 10); result.put(nullFirstLevelEntities, nullFirstLevelCount); result.put(emptyFirstLevelEntities, emptyFirstLevelCount); result.put(nullSecondLevelItems, nullSecondLevelCount); result.put(emptySecondLevelItems, emptySecondLevelCount); result.put(features, 支持所有空集合情况一级为null、一级为空列表、二级为null、二级为空列表); return result; } }