Преобразование XML в CSV максимально быстро для файлов среднего размера - PullRequest
0 голосов
/ 21 июня 2019

Итак, я посмотрел и попробовал несколько вещей, чтобы преобразовать файл XML в файл CSV.Я попробовал следующие способы:

  1. XSLT: получить XSLT для данного XML, а затем сформировать CSV.Но это слишком сложно поддерживать, так как мы не знаем, какой XML-файл мы получим, что делает его не общим решением.
  2. Использование Apache Digester
  3. SAXP иJAXP Проблема двух вышеперечисленных подходов заключается в том, что для этого требуется определить ваши java-объекты ранее, и, таким образом, это опять-таки узкое место для создания такого количества классов, что мы не знаем, какой там будет шаблон XML.Он будет меняться каждый раз.
  4. Использование DocumentBuildFactory и его разбор.Это работает для общих файлов XML, но медленно для файлов, которые находятся в диапазоне от 5 МБ до 1 ГБ.Мои XML-файлы точно не будут больше 1 ГБ.

Помимо этих подходов, которые я уже пробовал, есть какие-нибудь идеи о том, как я могу достичь этого программно и быстрее, чем выше?Я рассмотрел несколько онлайн-инструментов, которые за очень короткое время преобразуют любой XML-файл в CSV-файлы, и они, похоже, работают для любого общего XML-файла.Любые предложения?

Вот несколько примеров, которые также могут измениться:

    <?xml version="1.0"?>
<Company>
  <Employee id="1">
      <Email>tp@xyz.com</Email>
      <artist>Bob Dylan</artist>
    <country>USA</country>
  </Employee>

</Company>

Это самый простой.Ожидаемый результат:

Company/Employee/Email,Company/Employee/artist,Company/Employee/country,Company/Employee/_id
tp@xyz.com,Bob Dylan,USA,1

Другой пример

<?xml version="1.0"?>
<Company>
  <Employee id="1">
      <Email>tp@xyz.com</Email>
      <UserData id="id32" type="AttributesInContext">
<UserValue value="7in" title="Height"></UserValue>
<UserValue value="" title="Weight"></UserValue></UserData>
  </Employee>
  <Employee id="2">
      <Email>tp@xyz.com</Email>
      <UserData id="id33" type="AttributesInContext">
<UserValue value="6in" title="Height"></UserValue>
<UserValue value="" title="Weight"></UserValue></UserData>
  </Employee>
  <Employee id="3">
      <Email>tp@xyz.com</Email>
      <UserData id="id34" type="AttributesInContext">
<UserValue value="4in" title="Height"></UserValue>
<UserValue value="" title="Weight"></UserValue></UserData>
  </Employee>
</Company>

Ожидаемый результат:

Email,UserData/UserValue/0/_value,UserData/UserValue/0/_title,UserData/UserValue/1/_value,UserData/UserValue/1/_title,UserData/_id,UserData/_type,_id
tp@xyz.com,7in,Height,,Weight,id32,AttributesInContext,1
tp@xyz.com,6in,Height,,Weight,id33,AttributesInContext,2
tp@xyz.com,4in,Height,,Weight,id34,AttributesInContext,3

Это сложный бит.И это может стать более сложным и вложенным и может достигать 1 ГБ при макс.

1 Ответ

1 голос
/ 21 июня 2019

Вы можете попытаться использовать Java StAX API для этого предложения.

Например:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

public class XmlToCSV {

    public static void convert(InputStream xml, OutputStream csv) throws Exception {
        try (StringWriter header = new StringWriter(4096); StringWriter content = new StringWriter(4096)) {
            XMLInputFactory factory = XMLInputFactory.newInstance();
            XMLEventReader xmlEventReader = factory.createXMLEventReader(xml);
            XMLEvent xmlEvent;
            long nestingLevel = -1;
            StringBuilder line = null;
            while (xmlEventReader.hasNext()) {
                xmlEvent = xmlEventReader.nextEvent();
                switch (xmlEvent.getEventType()) {
                case XMLEvent.START_ELEMENT:
                    ++nestingLevel;
                    if (0 == nestingLevel) {
                        break;
                    } else if (1 == nestingLevel) {
                        line = new StringBuilder();
                    }
                    StartElement startElement = xmlEvent.asStartElement();
                    serializeElementHeader(header, line, startElement);
                    break;
                case XMLEvent.CHARACTERS:
                case XMLEvent.CDATA:
                    if (nestingLevel < 1)
                        break;
                    Characters chars = xmlEvent.asCharacters();
                    if (!chars.isWhiteSpace()) {
                        line.append(chars.getData());
                        line.append(',');
                    }
                    break;
                case XMLEvent.END_ELEMENT:
                    if (--nestingLevel == 0) {
                        header.write("0/");
                        for(int i= line.length()-1; ',' == line.charAt(i); --i) {                           
                            line.deleteCharAt(i);
                        }
                        content.write(line.toString());
                        content.write('\n');
                    }
                    break;
                default:
                    break;
                }
            }
            // write csv
            try (Writer cvsWriter = new OutputStreamWriter(csv, StandardCharsets.UTF_8.name())) {
                cvsWriter.write(header.toString());
                cvsWriter.write('\n');
                cvsWriter.write(content.toString());
            }
        }
    }

    private static void serializeElementHeader(StringWriter header, StringBuilder line,
            StartElement startElement) {
        header.write(startElement.getName().getLocalPart());
        header.write('/');
        Iterator<Attribute> it = startElement.getAttributes();
        while(it.hasNext()) {
            Attribute attr = it.next();
            header.write('_');
            header.write(attr.getName().getLocalPart());
            header.write('/');
            line.append(attr.getValue());
            line.append(',');
        }
    }

    private static String TEST_XML = "<?xml version='1.0'?>" 
            + "<Company>" 
            + "  <Employee id='1'>"
            + "      <Email>tp@xyz.com</Email>" 
            + "      <UserData id='id32' type='AttributesInContext'>"
            + "         <UserValue value='7in' title='Heigh'></UserValue>"
            + "         <UserValue value='' title='Weight'></UserValue>" 
            + "      </UserData>" 
            + "  </Employee>"
            + "  <Employee id='2'>"
            + "      <Email>tp@xyz.com</Email>" 
            + "      <UserData id='id33' type='AttributesInContext'>"
            + "         <UserValue value='6in' title='Heigh'></UserValue>"
            + "         <UserValue value='' title='Weight'></UserValue>" 
            + "      </UserData>" 
            + "  </Employee>"
            + "  <Employee id='3'>"
            + "      <Email>tp@xyz.com</Email>" 
            + "      <UserData id='id34' type='AttributesInContext'>"
            + "         <UserValue value='4in' title='Heigh'></UserValue>"
            + "         <UserValue value='' title='Weight'></UserValue>" 
            + "      </UserData>" 
            + "  </Employee>"
            + "</Company>";

    public static void main(String[] args) throws Exception {

        try (InputStream in = new ByteArrayInputStream(TEST_XML.getBytes(Charset.defaultCharset()));
                ByteArrayOutputStream out = new ByteArrayOutputStream(4096)) {
            convert(in, out);
            System.out.print(out.toString());
        }

    }
...