Мне нужно преобразовать файл XML в CSV, но я должен прочитать файл только один раз, и заголовки в теге записи могут измениться, есть идеи? - PullRequest
1 голос
/ 27 февраля 2020

У меня есть несколько очень больших 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"}

This может быть неправильно - я быстро набрал его - но вы поняли.

Есть некоторые ограничения, которые следует иметь в виду:

  1. Файл очень большой - 1 ГБ + - поэтому я могу не загружайте это в память.
  2. Я должен прочитать это только один раз.
  3. (obvioulsy) данные не могут быть потеряны или неверны.

Хорошо, так что я в настоящее время иметь синтаксический анализатор, который преобразует простой XML файл в csv с заданным «ROWTAG», т. е. тегом, в котором есть записи (person в этом примере). Вы можете видеть это здесь .

Но на это есть некоторые ограничения. Я знаю, как исправить / устранить большинство из них, но есть две, которые я не могу понять, не нарушая ограничений.

  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

Имя Майкла было записано как Майкл Джордан Роуз, когда это должен был быть Майкл Роуз Джордан.

Если атрибуты добавлены, удалены или изменены, программа не отражает это. Это потому, что программа просматривает только теги из первого элемента записи и не заботится о тегах в следующих элементах (как показано в первом примере).

Давайте рассмотрим этот пример :

<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. Во время первой итерации он также сохраняет все заголовки, с которыми сталкивается, а затем, перед тем как сбросить значения записей после достижения конечного тега, он сначала сбросит заголовки.

Теперь ... как я упомянуто, это создает проблему с сохранением порядка и с любыми измененными, удаленными или добавленными тегами. У меня есть решение, которое решает заказ, и оно должно решить проблему заголовка, не будучи обновленной, но я не уверен, выполнимо ли это для моего сценария использования (который я объясню почему через минуту).

Моя идея состоит в том, чтобы иметь что-то вроде хэш-карты, которая собирает значения тегов и порядок, с которым они встречаются с течением времени. Ключом будет значение тега, а значением будет порядок первого появления тега.

Когда мы собираем записи по мере продвижения по программе, мы помещаем их в массив, который является настолько большим как хэш-карта в правильном месте, он должен быть. Если мы столкнемся с новыми тегами, мы просто изменим размер массива и добавим тег в хэш-карту НЕ со значением порядка, с которым он встречался в данный момент (потому что это может что-то перезаписать), а скорее со значением предыдущего элемента + 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>

Массив во время второго запуска будет нулевым во втором значении (потому что middlename там нет), что мы просто конвертируем в пустую строку, и к ней добавляется запятая, как обычно.

Интересная часть, когда что-то добавляется:

<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, это нормально? Круто.

В любом случае - теперь я думаю, что проблема в этом. На самом деле я не использую bufferedreader / bufferedwriter в java. Мы используем потоковое считывающее / записывающее устройство, которое поставляется с Azure, потому что, конечно, все эти файлы находятся в облаке, а под капотом это в основном просто остальные вызовы API. Поэтому я не думаю, что смогу вывести заголовки в первую строку файла. Я даже не уверен, что это было бы возможно, независимо от того.

Итак. Есть гении, у которых есть идеи?

1 Ответ

0 голосов
/ 27 февраля 2020
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);
        }


    }

}
`
...