【解决方式】xml文件定义与对象映射
XML文件是我们开发过程中常用的一种数据格式,由于它成熟的校验机制与固定的语法格式,虽然较为复杂但也没有失去他的作用;业务开发过程中,需要采集Excel的文件内容且与数据库表结果相映射,就开发了一套代码用于处理这种较为固定的Excel数据的采集;其中定义了一种固定的xml文件格式,与读取xml文件的一种方式。
业务目的:
通过配置不同的xml配置文件,达到适配不同Excel文件格式并采集相应数据的功能,中间转化的文件以CSV格式,有利于直接Load到对应的数据库表中。
满足的功能点:
(1)文件校验;
(2)文件预处理;
(3)文件数据提取;
(4)文件行列转换;
(5)文件某行或某列聚合(加法、乘法、列表);
(6)根据数据某一列排序;
(7)文件数据采集后处理;
(8)文件数据入库前处理;
(9)文件数据导入数据库;
(10)数据入库前处理;
(11)数据入库后处理。
POI、XML、Java反射(可以用spring替代)
XML文件中可以定义多个采集子项目,每个采集子项目中可以定义多个文件采集任务,文件采集选项使用名称正则匹配。其中可以定义多个文件处理器,如文件校验,文件采集前置处理,后置处理,入库逻辑处理等。代码也可配置到xml文件中,实现处理逻辑的灵活选择,xml中也可以定义文件数据采集逻辑。
<CollectFileCfg>
<SubItems>
<SubItem name="item1">
<FileOptions>
<FileOption name="\d{4}?日前联络线计划\d{4}-\d{2}-\d{2}总加.xls" suffix=".xls" dir="" toDir="" mode="row" group="true" header="true">
<!-- 暂时未扩展的功能-->
<!-- <Parameters>-->
<!-- <Parameter key="" value="" type=""/>-->
<!-- </Parameters>-->
<MatchFileMethod>
<Method class="com.fp.collect.util.FieldTransMethod" name="matchFileMethodTemplate" ref="0"/>
</MatchFileMethod>
<ValidFileMethods>
<Method class="com.fp.collect.util.FieldTransMethod" name="vaildMethodTemplate" ref="0"/>
</ValidFileMethods>
<!-- 抽取前文件处理 参照样例方法-->
<PreHandleFileMethod>
<Method class="com.fp.collect.util.FieldTransMethod" name="fileMethodTemplate" ref="0"/>
</PreHandleFileMethod>
<!-- 可代替实际抽取逻辑的方法配置 参照样例方法-->
<HandleFileMethod>
<Method class="com.fp.collect.util.FieldTransMethod" name="fileMethodTemplate" ref="0"/>
</HandleFileMethod>
<Sheets>
<Sheet index="0">
<!-- <Grids>-->
<!-- <Grid column="1"></Grid>-->
<!-- <Grid column="3"></Grid>-->
<!-- </Grids>-->
<Groups>
<Group by="0" collect="3" sort="2" operator="list">
<!-- <Method class="com.fp.databasepmtest.collect.util.FieldTransMethod" name="getGroup"/>-->
</Group>
</Groups>
</Sheet>
</Sheets>
<!-- 抽取后文件处理 参照样例方法-->
<AfterHandleFileMethod>
<Method class="com.fp.collect.util.FieldTransMethod" name="fileMethodTemplate" ref="0"/>
</AfterHandleFileMethod>
<!-- 入库前文件处理 参照样例方法-->
<PreLoadTableMethod>
<Method class="com.fp.collect.util.FieldTransMethod" name="fileMethodTemplate" ref="1"/>
</PreLoadTableMethod>
<!-- 入库文件处理 可代替入库 参照样例方法-->
<LoadTableMethod>
<Method class="com.fp.collect.util.FieldTransMethod" name="loadMethodTemplate" ref="1"/>
</LoadTableMethod>
<Tables>
<Table id="1" name="market_publish_point" dir="E:\test_table.csv" sheet="0" isLoad="true">
<Fields>
<Field name="id" type="UUID">
<Method class="com.fp.databasepmtest.collect.util.FieldTransMethod" name="getIdByDate" args="1"/>
</Field>
</Fields>
</Table>
</Tables>
<!-- 入库后文件处理 参照样例方法-->
<AfterLoadTableMethod>
<Method class="com.fp.collect.util.FieldTransMethod" name="loadMethodTemplate" ref="1"/>
</AfterLoadTableMethod>
</FileOption>
</FileOptions>
</SubItem>
</SubItems>
</CollectFileCfg>
可以在xsd文件中定义xml文件节点的具体格式与数据类型校验。
<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="FieldType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="Method" type="MethodType" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="type" type="TypeType"/>
<xs:attribute name="collect" type="IntStringType"/>
</xs:complexType>
<xs:simpleType name="TypeType">
<xs:restriction base="xs:string">
<xs:pattern value="String|char|int|long|BigDecimal|double|float|UUID|date"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="MethodType">
<xs:attribute name="class" type="xs:string"/>
<xs:attribute name="name" type="xs:string"/>
<xs:attribute name="args" type="xs:string"/>
<xs:attribute name="ref" type="xs:string"/>
</xs:complexType>
<xs:complexType name="FieldsType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="Field" type="FieldType" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="TableType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="Fields" type="FieldsType"/>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required"/>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="dir" type="xs:string"/>
<xs:attribute name="sheet" type="xs:int" use="required"/>
<xs:attribute name="isLoad" type="xs:boolean" use="required"/>
</xs:complexType>
<xs:complexType name="TablesType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="Table" type="TableType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GridType">
<xs:attribute name="row" type="IntStringType"/>
<xs:attribute name="column" type="IntStringType"/>
<xs:attribute name="type" type="TypeType"/>
<xs:attribute name="format" type="xs:string"/>
</xs:complexType>
<xs:complexType name="SheetType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="Grids" type="GridsType" minOccurs="0"/>
<xs:element name="Groups" type="GroupsType" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="index" type="xs:int" use="required"/>
</xs:complexType>
<xs:simpleType name="IntStringType">
<xs:restriction base="xs:string">
<xs:pattern value="\d"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="GridsType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="Grid" type="GridType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="SheetsType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="Sheet" type="SheetType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="FileOptionType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="Parameters" type="ParametersType" minOccurs="0"/>
<xs:element name="MatchFileMethod" type="methodsType" minOccurs="0"/>
<xs:element name="ValidFileMethods" type="methodsType" minOccurs="0"/>
<xs:element name="PreHandleFileMethod" type="methodsType" minOccurs="0"/>
<xs:element name="HandleFileMethod" type="methodsType" minOccurs="0"/>
<xs:element name="Sheets" type="SheetsType"/>
<xs:element name="AfterHandleFileMethod" type="methodsType" minOccurs="0"/>
<xs:element name="PreLoadTableMethod" type="methodsType" minOccurs="0"/>
<xs:element name="LoadTableMethod" type="methodsType" minOccurs="0"/>
<xs:element name="Tables" type="TablesType"/>
<xs:element name="AfterLoadTableMethod" type="methodsType" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string"/>
<xs:attribute name="suffix" type="SuffixType" use="required"/>
<xs:attribute name="dir" type="xs:string"/>
<xs:attribute name="toDir" type="xs:string"/>
<xs:attribute name="rename" type="xs:string"/>
<xs:attribute name="protocol" type="ProtocolType"/>
<xs:attribute name="mode" type="ModeType" use="required"/>
<xs:attribute name="group" type="BoolStringType" use="required"/>
<xs:attribute name="header" type="BoolStringType" use="required"/>
</xs:complexType>
<xs:simpleType name="SuffixType">
<xs:restriction base="xs:string">
<xs:pattern value=".xls|.xlsx"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ParametersType">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="Parameter" type="ParameterType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ParameterType">
<xs:attribute name="key" type="xs:string" use="required"/>
<xs:attribute name="value" type="xs:string" use="required"/>
<xs:attribute name="type" type="TypeType" use="required"/>
</xs:complexType>
<xs:complexType name="methodsType">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="Method" type="MethodType"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="BoolStringType">
<xs:restriction base="xs:string">
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="GroupsType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="Group" type="GroupType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GroupType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="Method" type="MethodType" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="by" type="IntStringType" use="required"/>
<xs:attribute name="sort" type="IntStringType"/>
<xs:attribute name="collect" type="IntStringType" use="required"/>
<xs:attribute name="operator" type="OperatorType"/>
</xs:complexType>
<xs:simpleType name="OperatorType">
<xs:restriction base="xs:string">
<xs:pattern value="\+|\*|list"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ProtocolType">
<xs:restriction base="xs:string">
<xs:pattern value="sftp|local|io"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ModeType">
<xs:restriction base="xs:string">
<xs:pattern value="row|column"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="FileOptionsType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="FileOption" type="FileOptionType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="SubItemType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="FileOptions" type="FileOptionsType" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string"/>
</xs:complexType>
<xs:complexType name="SubItemsType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="SubItem" type="SubItemType" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:element name="CollectFileCfg">
<xs:complexType>
<xs:sequence maxOccurs="unbounded">
<xs:element name="SubItems" type="SubItemsType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
通过以下方法把xml文件中的数据注入到根节点对象中去。
private static final String COLLECT_FILE_CFG_XML_PATH = "classpath:collect/CollectFileCfg.xml";
private static final String COLLECT_FILE_CFG_XSD_PATH = "collect/CollectFileCfgXsd.xml";
private static final String COLLECT_FILE_CFG_XSD_TMP_PATH = "./CollectFileCfgXsd.xml";
private static final CollectFileCfg COLLECTFILECFG = initCollectFileCfg();
private static CollectFileCfg initCollectFileCfg() {
try {
// xsd文件在类文件路径下放置
ClassPathResource classPathResource = new ClassPathResource(COLLECT_FILE_CFG_XSD_PATH);
@Cleanup InputStream xsdFileInput = classPathResource.getInputStream();
// xsd文件暂存
File xsdFile = new File(COLLECT_FILE_CFG_XSD_TMP_PATH);
FileUtils.copyInputStreamToFile(xsdFileInput, xsdFile);
// 读取xml文件
File xmlFile = ResourceUtils.getFile(COLLECT_FILE_CFG_XML_PATH);
// xml文件跟节点对象上下文
JAXBContext context = JAXBContext.newInstance(CollectFileCfg.class);
// 创建xml文件对象解释器
Unmarshaller unmarshaller = context.createUnmarshaller();
// 导入xsd文件
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsdFile);
// 解释器设置校验xsd
unmarshaller.setSchema(schema);
return (CollectFileCfg) unmarshaller.unmarshal(xmlFile);
} catch (IOException e) {
logger.error("Get file failed. {}", e.getMessage());
throw new RuntimeException("Get file failed.", e);
} catch (SAXException saxException) {
logger.error("Init xml schema failed. {}", saxException.getMessage());
throw new RuntimeException("Init xml schema failed.", saxException);
} catch (JAXBException jaxbException) {
logger.error("Parse xml error. {}", jaxbException.getMessage());
throw new RuntimeException("Parse xml error.", jaxbException);
}
}
(1)根节点对象
@Data
@XmlRootElement(name = "CollectFileCfg")
@XmlAccessorType(XmlAccessType.FIELD)
public class CollectFileCfg {
@XmlElement(name = "SubItem")
@XmlElementWrapper(name="SubItems")
private List<SubItem> subItems;
}
(2)子项目对象
@Data
@XmlRootElement(name = "SubItem")
@XmlAccessorType(XmlAccessType.FIELD)
public class SubItem {
@XmlAttribute(name = "name")
private String name;
@XmlElement(name = "FileOption")
@XmlElementWrapper(name = "FileOptions")
private List<FileOption> fileOptions;
}
(3)文件处理对象
@Data
@XmlRootElement(name = "FileOption")
@XmlAccessorType(XmlAccessType.FIELD)
public class FileOption {
@XmlAttribute(name = "name")
private String name;
@XmlAttribute(name = "suffix")
private String suffix;
@XmlAttribute(name = "dir")
private String dir;
@XmlAttribute(name = "toDir")
private String toDir;
@XmlAttribute(name = "rename")
private String rename;
@XmlAttribute(name = "protocol")
private String protocol;
@XmlAttribute(name = "mode")
private String mode;
@XmlAttribute(name = "group")
private Boolean group;
@XmlAttribute(name = "header")
private Boolean header;
@XmlElementWrapper(name = "Sheets")
@XmlElement(name = "Sheet")
private List<Sheet> sheets;
@XmlElementWrapper(name = "Tables")
@XmlElement(name = "Table")
private List<Table> tables;
@XmlElementWrapper(name = "Parameters")
@XmlElement(name = "Parameter")
private List<Parameter> parameters;
@XmlElementWrapper(name = "ValidFileMethods")
@XmlElement(name = "Method")
private List<Method> validFileMethods;
@XmlElementWrapper(name = "MatchFileMethod")
@XmlElement(name = "Method")
private List<Method> matchFileMethod;
@XmlElementWrapper(name = "PreHandleFileMethod")
@XmlElement(name = "Method")
private List<Method> preHandleFileMethod;
@XmlElementWrapper(name = "HandleFileMethod")
@XmlElement(name = "Method")
private List<Method> handleFileMethod;
@XmlElementWrapper(name = "AfterHandleFileMethod")
@XmlElement(name = "Method")
private List<Method> afterHandleFileMethod;
@XmlElementWrapper(name = "PreLoadTableMethod")
@XmlElement(name = "Method")
private List<Method> preLoadTableMethod;
@XmlElementWrapper(name = "LoadTableMethod")
@XmlElement(name = "Method")
private List<Method> loadTableMethod;
@XmlElementWrapper(name = "AfterLoadTableMethod")
@XmlElement(name = "Method")
private List<Method> afterLoadTableMethod;
}
………………
其他对象可以照这xml文件一一定义。
具体的业务逻辑代码有点多而且写的比较粗糙,这里只是提供一个思路,可以在码云上https://gitee.com/niudongjun_gitee/other.git excel-collect查看拉取。
因篇幅问题不能全部显示,请点此查看更多更全内容