我需要将XML文件转换为CSV,但必须只读取一次文件,并且记录标记中的标题可能会改变,有什么想法吗?

问题描述 投票:1回答:1

我有一些非常大的XML文件,我需要解析这些文件并将相关数据提取到csv文件中,本质上是对XML文档进行了部分展平。 XML文件将具有一个“记录标签”,用于存储所有记录。看起来很像这样,例如:

<persons>
    <person id="1">
        <firstname>James</firstname>
        <lastname>Smith</lastname>
        <middlename></middlename>
        <dob_year>1980</dob_year>
        <dob_month>1</dob_month>
        <gender>M</gender>
        <salary currency="Euro">10000</salary>   
    </person>
    <person id="2">
        <firstname>Michael</firstname>
        <lastname></lastname>
        <middlename>Rose</middlename>
        <dob_year>1990</dob_year>
        <dob_month>6</dob_month>
        <gender>M</gender>
        <salary currency="Dollor">10000</salary>
</persons>

这里的记录标签是'person',并且最终转换为CSV看起来像这样:

_id, dob_month, dob_year,firstname,gender,lastname, middlename  salary 
1,1,1980, James,M,Smith,,{"_VALUE":10000,"_currency":"Euro"}
2,6,1990, Michael M,, Rose,{"_VALUE":10000,"_currency":"Dollor"}

这可能不正确-我很快就输入了-但您明白了。

要记住一些限制:

  1. 文件非常大-1GB +-因此我无法将其加载到内存中。
  2. 我应该只阅读一次。
  3. ((重要的)数据不能丢失或不正确。

[好,因此,我目前有一个解析器,它将使用给定的“ ROWTAG”将一个简单的XML文件转换为一个csv,它是其中包含记录的标记(在此示例中为person)。您可以看到它here

但是有一些限制。我知道如何解决/解决其中的大多数问题,但是有两个我在不打破约束的情况下无法解决的问题。

  1. 基于解析器的实现方式,标记的顺序很重要。假设我有一个看起来像这样的xml文件:
<person id="1">
        <firstname>James</firstname>
        <middlename></middlename> 
        <lastname>Smith</lastname>
    </person>
    <person id="2">
        <firstname>Michael</firstname>
        <lastname>Jordan</lastname>
        <middlename>Rose</middlename>
</person>

第一个记录标签中的中间名排在第二,第二个记录中的中间名排在第三。这将导致一个CSV文件,如下所示:

firstname,middlename,lastname
James,,Smith,
Michael,Jordan,Rose

[Michael的名字被记录为Michael Jordan Rose,那时应该是Michael Rose Jordan。

  1. 如果添加,删除或更改属性,则程序不会反映出来。这是因为该程序仅查看来自第一个记录元素的标签,而不关心接下来的元素中的标签(如示例一所示)。

让我们举个例子:

<person id="1">
        <firstname>James</firstname>
        <middlename></middlename> 
        <lastname>Smith</lastname>
    </person>
    <person id="2">
        <firstname>Michael</firstname>
        <lastname>Jordan</lastname>
        <middlename>Rose</middlename>
        <dob>1/10/11</dob>
</person>

生成的CSV看起来像这样:

firstname,middlename,lastname
James,,Smith,
Michael,Jordan,Rose, 1/10/11

当然,这是个大问题,必须解决。

我的解决方案

在获得解决方案之前,我将非常迅速地总结程序的工作原理。解析器在XML文档中移动,每次遇到标签时,都会将其吐回到我的程序中。我的程序有一个“ rowTag”,正如我已经解释过的那样,该程序将寻找它。一旦遇到它,我的程序便开始查看该rowTag内部的所有标签和值,并将它们保存在StringBuilder中。当遇到结尾的rowTag时,它们将转储该信息。在第一次迭代期间,它还将保存遇到的所有标头,然后在到达end标签后转储记录的值之前,将首先转储标头。

现在...正如我所提到的,这在保存顺序以及任何更改,删除或添加的标签方面造成了问题。我有一个解决该订单的解决方案,并且它[[应该解决了标题问题没有更新,但是我不确定它对我的用例是否可行(我将在稍后解释原因)。

我的想法是像哈希图这样收集标记的值以及它们在一段时间内遇到的顺序。关键是标签的值,而值将是标签首次出现的顺序。

当我们沿着程序移动时收集记录时,我们会将它们放置在一个与哈希图一样大的数组中,并将其放置在所需的正确位置。如果遇到新标签,我们将简单地调整数组大小并将标签添加到哈希图中,而不添加当前遇到的顺序的值(因为这可能会覆盖某些内容),而是上一个元素的值+ 1(这将是一个有序的哈希图,所以我会知道前面的元素是什么。]

一旦我们完全完成了程序,我们便会将收集的标头转储到文件的第一行。

所以,让我们看第一个例子:

<person id="1"> <firstname>James</firstname> <middlename></middlename> <lastname>Smith</lastname> </person> <person id="2"> <firstname>Michael</firstname> <lastname>Jordan</lastname> <middlename>Rose</middlename> </person>

第一次运行后,哈希图将如下所示:

{firstname: 0, middlename: 1, lastname: 2}

[当我们转到第二个记录时,中间名和姓氏的位置已切换,我们将简单地将姓氏放在array[2]点中,然后将中间名放在array[1]点中。

如果我们缺少某些东西,那么例如这样的东西:

<person id="1"> <firstname>James</firstname> <middlename></middlename> <lastname>Smith</lastname> </person> <person id="2"> <firstname>Michael</firstname> <lastname>Rose</lastname> </person>

[数组,在第二次运行中,第二个值将为null(因为中间名不存在),我们只将其转换为空字符串,然后像往常一样在其中添加逗号。

有趣的部分是添加内容时:

<person id="1"> <firstname>James</firstname> <middlename></middlename> <lastname>Smith</lastname> </person> <person id="2"> <firstname>Michael</firstname> <lastname>Rose</lastname> <dob></dob> </person>

这将导致看起来像这样的CSV:

firstname,middlename,lastname,dob James,,Smith Michael,,Rose,1/10/11

第一列在,之后没有多余的Smith,但即使那不是有效的CSV,也可以吗?酷。

无论如何,现在我想到了真正的问题。我实际上并没有在Java中使用bufferedreader / bufferedwriter。我们使用的是Azure附带的流读取器/写入器,因为所有这些文件当然都在云中,而在引擎盖下,它基本上只是静态的api调用。因此,我认为我无法将标头转储到文件的第一行。我什至不知道那是否有可能,无论如何。

所以有什么想法的天才吗?

java xml stream azure-blob-storage woodstox
1个回答
0
投票
xls to csv convert public class DeviceLibraryModel { private String parameterName; private String dataType; private String noOfRegister; private String address; public String getParameterName() { return parameterName; } public String getDataType() { return dataType; } public String getNoOfRegister() { return noOfRegister; } public String getAddress() { return address; } public void setParameterName(String parameterName) { this.parameterName = parameterName; } public void setDataType(String dataType) { this.dataType = dataType; } public void setNoOfRegister(String noOfRegister) { this.noOfRegister = noOfRegister; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "DeviceLibraryModel{" + "ParameterName=" + parameterName + ", DataType=" + dataType + ", NoOfRegister=" + noOfRegister + ", Address=" + address + '}'; } } > public class HeaderNameIndex { private int pratameterNameIndex; private int dataTypeIndex; private int noOfRegister; private int address; public HeaderNameIndex(){ } public int getPratameterNameIndex() { return pratameterNameIndex; } public int getDataTypeIndex() { return dataTypeIndex; } public int getNoOfRegister() { return noOfRegister; } public int getAddress() { return address; } public void setPratameterNameIndex(int pratameterNameIndex) { this.pratameterNameIndex = pratameterNameIndex; } public void setDataTypeIndex(int dataTypeIndex) { this.dataTypeIndex = dataTypeIndex; } public void setNoOfRegister(int noOfRegister) { this.noOfRegister = noOfRegister; } public void setAddress(int address) { this.address = address; } } package model; public interface HeaderNameInt { String PARAMETERNAME="Parameter Name"; String DATATYPE="Data Type"; String NOOFREGISTER="No Of Register"; String ADDRESS="Address"; } package services; public class ReadFromXls extends HeaderNameIndex implements HeaderNameInt { public List<DeviceLibraryModel> xlsConvert(String xlsPath) throws FileNotFoundException, IOException { File file = new File(xlsPath); FileInputStream fi = new FileInputStream(file); List<DeviceLibraryModel> list = new ArrayList<>(); HeaderNameIndex objHeaderNameIndex = new HeaderNameIndex(); Workbook hw = new HSSFWorkbook(fi); Sheet sheet = hw.getSheetAt(0); Iterator<Row> rit = sheet.rowIterator(); int rowNumber = 0; while (rit.hasNext()) { Row next = rit.next(); DeviceLibraryModel dm = new DeviceLibraryModel(); Iterator<Cell> cit = next.cellIterator(); while (cit.hasNext()) { Cell cellit = cit.next(); int iColumnIndex = cellit.getColumnIndex(); DataFormatter dataFormatter = new DataFormatter();//to get all string String formatCellValue = dataFormatter.formatCellValue(cellit); if (rowNumber == 0) { switch (formatCellValue) { case PARAMETERNAME: objHeaderNameIndex.setPratameterNameIndex(iColumnIndex); break; case DATATYPE: objHeaderNameIndex.setDataTypeIndex(iColumnIndex); //System.err.println(objHeaderNameIndex.getDataTypeIndex()); break; case NOOFREGISTER: objHeaderNameIndex.setNoOfRegister(iColumnIndex); //System.err.println(objHeaderNameIndex.getNoOfRegister()); break; case ADDRESS: objHeaderNameIndex.setAddress(iColumnIndex); break; default: System.err.println("nothing"); } } if (rowNumber > 0) { if(iColumnIndex == objHeaderNameIndex.getPratameterNameIndex()) dm.setParameterName(formatCellValue); else if (iColumnIndex == objHeaderNameIndex.getDataTypeIndex()) dm.setDataType(formatCellValue); else if (iColumnIndex == objHeaderNameIndex.getNoOfRegister()) dm.setNoOfRegister(formatCellValue); else if (iColumnIndex == objHeaderNameIndex.getAddress()) dm.setAddress(formatCellValue); } } if (rowNumber > 0) { list.add(dm); } rowNumber++; fi.close(); } // System.err.println(list); return list; } } public class ConvertXlsToCsv { public void toCsv() throws IOException{ ReadFromXls readXls=new ReadFromXls(); String xlsPath="C:\\Users\\admin\\Desktop\\Java Training\\Input file\\Device.xls"; List<DeviceLibraryModel> list = readXls.xlsConvert(xlsPath); String sep=","; String csvPath="C:\\Users\\admin\\Desktop\\Java Training\\Input file\\XlsToCsv.csv"; File file=new File(csvPath); FileWriter writeData=new FileWriter(file,true); for(DeviceLibraryModel dm:list) { if(file.exists()) { String parameterName = dm.getParameterName(); writeData.append(parameterName+ '\n'); writeData.append(sep+dm.getDataType()+sep+ '\n'); writeData.append(dm.getNoOfRegister()+sep+ '\n'); writeData.append(dm.getAddress()+sep+ '\n'); }else { String parameterName = dm.getParameterName(); writeData.write(parameterName); writeData.write(sep+dm.getDataType()+sep); writeData.write(dm.getNoOfRegister()+sep); writeData.write(dm.getAddress()+sep); } } writeData.flush(); writeData.close(); } } ` public class ReadXls { public static void main(String[] args) { ConvertXlsToCsv convert=new ConvertXlsToCsv(); try { convert.toCsv(); } catch (IOException ex) { System.err.println(ex); Logger.getLogger(ReadXls.class.getName()).log(Level.SEVERE, null, ex); } } } `
© www.soinside.com 2019 - 2024. All rights reserved.