Подтвердить что ты не робот

Как улучшить производительность xml файла

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

<?xml version="1.0" encoding="UTF-8" standalone="no" ?> 
<company>
 <staff id="1">
    <firstname>yong</firstname>
    <lastname>mook kim</lastname>
    <nickname>mkyong</nickname>
    <salary>100000</salary>
   </staff>
 <staff id="2">
    <firstname>yong</firstname>
    <lastname>mook kim</lastname>
    <nickname>mkyong</nickname>
    <salary>100000</salary>
   </staff>
 <staff id="3">
    <firstname>yong</firstname>
    <lastname>mook kim</lastname>
    <nickname>mkyong</nickname>
    <salary>100000</salary>
   </staff>
 <staff id="4">
    <firstname>yong</firstname>
    <lastname>mook kim</lastname>
    <nickname>mkyong</nickname>
    <salary>100000</salary>
   </staff>
 <staff id="5">
    <firstname>yong</firstname>
    <lastname>mook kim</lastname>
    <salary>100000</salary>
   </staff>
</company>

Я хочу разбить этот xml на n частей, каждый из которых содержит 1 файл, но элемент staff должен содержать nickname, если он там не нужен. Таким образом, это должно приводить к 4 слотам xml, каждый из которых содержит идентификатор персонала, начиная с 1 до 4.

Вот мой код:

public int split() throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(inputFilePath)));

        String line;
        List<String> tempList = null;

        while((line=br.readLine())!=null){
            if(line.contains("<?xml version=\"1.0\"") || line.contains("<" + rootElement + ">") || line.contains("</" + rootElement + ">")){
                continue;
            }

            if(line.contains("<"+ element +">")){
                tempList = new ArrayList<String>();
            }
            tempList.add(line);

            if(line.contains("</"+ element +">")){
                if(hasConditions(tempList)){
                    writeToSplitFile(tempList);
                    writtenObjectCounter++;
                    totalCounter++;
                }
            }

            if(writtenObjectCounter == itemsPerFile){
                writtenObjectCounter = 0;
                fileCounter++;          
                tempList.clear();
            }
        }

        if(tempList.size() != 0){
        writeClosingRootElement();
        }

        return totalCounter;
    }

    private void writeToSplitFile(List<String> itemList) throws Exception{
        BufferedWriter wr = new BufferedWriter(new FileWriter(outputDirectory + File.separator + "split_" + fileCounter + ".xml", true));
        if(writtenObjectCounter == 0){
        wr.write("<" + rootElement + ">");
        wr.write("\n");
        }

        for (String string : itemList) {
            wr.write(string);
            wr.write("\n");
        }

        if(writtenObjectCounter == itemsPerFile-1)
        wr.write("</" + rootElement + ">");
        wr.close();
    }

    private void writeClosingRootElement() throws Exception{
        BufferedWriter wr = new BufferedWriter(new FileWriter(outputDirectory + File.separator + "split_" + fileCounter + ".xml", true));
        wr.write("</" + rootElement + ">");
        wr.close();
    }

    private boolean hasConditions(List<String> list){
        int matchList = 0;

        for (String condition : conditionList) {
            for (String string : list) {
                if(string.contains(condition)){
                    matchList++;
                }
            }
        }

        if(matchList >= conditionList.size()){
            return true;
        }

        return false;
    }

Я знаю этот поток открытия/закрытия для каждого записанного элемента staff, который влияет на производительность. Но если я пишу один раз за файл (который может содержать n число staff). Естественно, элементы root и split настраиваются.

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

Edit:

Этот XML-пример на самом деле является фиктивным примером: реальный XML, который я пытаюсь разбить, составляет около 300-500 различных элементов под разделенным элементом, все из которых появляются в случайном порядке, а число меняется. Stax может быть не лучшим решением?

Обновление Bounty:

Я ищу решение (код), которое будет:

  • Уметь разделить XML файл на n частей с помощью x разделенных элементов (из фиктивного примера примера XML является разделенный элемент).

  • Содержимое спрятанных файлов должно быть обернуто в корневой элемент из исходного файла (например, в компании-образцовом примере)

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

  • Код не обязательно должен улучшить мое решение (отсутствие хорошей логики и производительности), но оно работает.

И не доволен "но он работает". И я не могу найти достаточно примеров Stax для таких операций, сообщество пользователей также не очень велико. Это также не должно быть решение Stax.

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

4b9b3361

Ответ 1

Первый совет: не пытайтесь написать свой собственный код обработки XML. Используйте синтаксический анализатор XML - он будет намного надежнее и, возможно, быстрее.

Если вы используете синтаксический анализатор XML (например, StAX), вы должны иметь возможность читать элемент за раз и записывать его на диск, никогда не читая весь документ за один раз.

Ответ 2

Вот мое предложение. Для этого требуется потоковый процессор XSLT 3.0: это на практике означает, что ему нужен Saxon-EE 9.3.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">

<xsl:mode streamable="yes">

<xsl:template match="/">
  <xsl:apply-templates select="company/staff"/>
</xsl:template>

<xsl:template match=staff">
  <xsl:variable name="v" as="element(staff)">
    <xsl:copy-of select="."/>
  </xsl:variable>
  <xsl:if test="$v/nickname">
    <xsl:result-document href="{@id}.xml">
      <xsl:copy-of select="$v"/>
    </xsl:result-document>
  </xsl:if>
</xsl:template>

</xsl:stylesheet>
Тем не менее, на практике, если у вас нет сотен мегабайт данных, я подозреваю, что не потоковое решение будет достаточно быстрым и, вероятно, быстрее, чем ваш написанный вручную Java-код, учитывая, что ваш Java-код не является ничем взволнован по поводу. Во всяком случае, дайте XSLT-решение попробовать, прежде чем писать строки с низким уровнем Java. Это обычная проблема, в конце концов.

Ответ 3

Вы можете сделать следующее с помощью StAX:

Алгоритм

  • Прочитайте и удерживайте событие root.
  • Прочитайте первый фрагмент XML:
    • События очереди до тех пор, пока условие не будет выполнено.
    • Если условие выполнено:
      • Записать исходное событие документа.
      • Записать событие с корневым элементом запуска
      • Вывести событие с событием разбитого старта
      • Выйти из очереди в очереди
      • Запишите оставшиеся события для этого раздела.
    • Если условие не было выполнено, ничего не делайте.
  • Повторите шаг 2 со следующим фрагментом XML

Код для вашего случая использования

В следующем коде используются API-интерфейсы StAX для разбивки документа, как указано в вашем вопросе:

package forum7408938;

import java.io.*;
import java.util.*;

import javax.xml.namespace.QName;
import javax.xml.stream.*;
import javax.xml.stream.events.*;

public class Demo {

    public static void main(String[] args) throws Exception  {
        Demo demo = new Demo();
        demo.split("src/forum7408938/input.xml", "nickname");
        //demo.split("src/forum7408938/input.xml", null);
    }

    private void split(String xmlResource, String condition) throws Exception {
        XMLEventFactory xef = XMLEventFactory.newFactory();
        XMLInputFactory xif = XMLInputFactory.newInstance();
        XMLEventReader xer = xif.createXMLEventReader(new FileReader(xmlResource));
        StartElement rootStartElement = xer.nextTag().asStartElement(); // Advance to statements element
        StartDocument startDocument = xef.createStartDocument();
        EndDocument endDocument = xef.createEndDocument();

        XMLOutputFactory xof = XMLOutputFactory.newFactory();
        while(xer.hasNext() && !xer.peek().isEndDocument()) {
            boolean metCondition;
            XMLEvent xmlEvent = xer.nextTag();
            if(!xmlEvent.isStartElement()) {
                break;
            }
            // BOUNTY CRITERIA
            // Be able to split XML file into n parts with x split elements(from
            // the dummy XML example staff is the split element).
            StartElement breakStartElement = xmlEvent.asStartElement();
            List<XMLEvent> cachedXMLEvents = new ArrayList<XMLEvent>();

            // BOUNTY CRITERIA
            // I'd like to be able to specify condition that must be in the 
            // split element i.e. I want only staff which have nickname, I want 
            // to discard those without nicknames. But be able to also split 
            // without conditions while running split without conditions.
            if(null == condition) {
                cachedXMLEvents.add(breakStartElement);
                metCondition = true;
            } else {
                cachedXMLEvents.add(breakStartElement);
                xmlEvent = xer.nextEvent();
                metCondition = false;
                while(!(xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().equals(breakStartElement.getName()))) {
                    cachedXMLEvents.add(xmlEvent);
                    if(xmlEvent.isStartElement() && xmlEvent.asStartElement().getName().getLocalPart().equals(condition)) {
                        metCondition = true;
                        break;
                    }
                    xmlEvent = xer.nextEvent();
                }
            }

            if(metCondition) {
                // Create a file for the fragment, the name is derived from the value of the id attribute
                FileWriter fileWriter = null;
                fileWriter = new FileWriter("src/forum7408938/" + breakStartElement.getAttributeByName(new QName("id")).getValue() + ".xml");

                // A StAX XMLEventWriter will be used to write the XML fragment
                XMLEventWriter xew = xof.createXMLEventWriter(fileWriter);
                xew.add(startDocument);

                // BOUNTY CRITERIA
                // The content of the spitted files should be wrapped in the 
                // root element from the original file(like in the dummy example
                // company)
                xew.add(rootStartElement);

                // Write the XMLEvents that were cached while when we were
                // checking the fragment to see if it matched our criteria.
                for(XMLEvent cachedEvent : cachedXMLEvents) {
                    xew.add(cachedEvent);
                }

                // Write the XMLEvents that we still need to parse from this
                // fragment
                xmlEvent = xer.nextEvent();
                while(xer.hasNext() && !(xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().equals(breakStartElement.getName()))) {
                    xew.add(xmlEvent);
                    xmlEvent = xer.nextEvent();
                }
                xew.add(xmlEvent);

                // Close everything we opened
                xew.add(xef.createEndElement(rootStartElement.getName(), null));
                xew.add(endDocument);
                fileWriter.close();
            }
        }
    }

}

Ответ 4

@Jon Skeet - это место, как обычно, в его советах. @Blaise Doughan дал вам очень общую картину использования StAX (что было бы моим предпочтительным выбором, хотя вы можете сделать в основном то же самое с SAX). Кажется, вы ищете что-то более явное, поэтому здесь есть псевдо-код, чтобы вы начали (на основе StAX):

  • найдите первый "персонал" StartElement
  • установите флаг, указывающий, что вы находитесь в элементе "staff" и начинаете отслеживать глубину (StartElement равен +1, EndElement равен -1)
  • теперь обрабатывайте подэлементы "staff" , захватывайте любые данные, которые вам нужны, и помещайте их в файл (или где-либо когда-либо).
  • продолжайте обработку до тех пор, пока ваша глубина не достигнет 0 (когда вы найдете соответствующий "персонал" EndElement)
  • отключить флаг, указывающий, что вы находитесь в элементе "staff" .
  • найдите следующий "штат" StartElement.
  • если найдено, перейдите к 2. и повторите
  • если не найден, документ завершен

EDIT:

Вау, я должен сказать, что я поражен количеством людей, желающих сделать для них кого-то другого. я не понял, что SO была в основном бесплатной версией рента-кодера.

Ответ 5

@Gandalf StormCrow: Позвольте мне разделить вашу проблему на три отдельных вопроса: i) Чтение XML и одновременное разделение XML наилучшим образом

ii) Проверка состояния в файле split

iii) Если условие выполнено, обработайте этот разлитый файл.

для i), существуют mutliple решения: SAX, STAX и другие синтаксические анализаторы и так же просто, как вы упомянули, только что прочитанные с помощью простых операций java io и поиска тегов.

Я считаю SAX/STAX/простой java IO, все будет сделано. Я принял ваш пример в качестве основы для моего решения.

ii) Проверка состояния в файле split: вы использовали метод contains() для проверки существования псевдонима. Это не выглядит наилучшим образом: что, если ваши условия настолько сложны, как если бы ник не присутствовал, но длинa > 5 или зарплата должна быть числовой и т.д.

Я бы использовал новую инфраструктуру проверки XML java для этого, которая использует использование XML-схемы. Обратите внимание, что мы можем кэшировать объект схемы в памяти, чтобы повторно использовать его снова и снова. Эта новая система проверки довольно быстро.

iii) Если условие выполнено, обработайте этот пролитый файл. Вы можете использовать java-совместимые API для отправки асинхронных задач (класс ExecutorService) для обеспечения параллельного выполнения для повышения производительности.

Таким образом, рассматривая вышеописанные точки, одним из возможных решений может быть: -

Вы можете создать файл company.xsd, например: -

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.example.org/NewXMLSchema"
    xmlns:tns="http://www.example.org/NewXMLSchema"
    elementFormDefault="unqualified">
    <element name="company">
        <complexType>
        <sequence>
            <element name="staff" type="tns:stafftype"/>
            </sequence>
        </complexType>

    </element>

    <complexType name="stafftype">
        <sequence>
        <element name="firstname" type="string" minOccurs="0" />
        <element name="lastname" type="string" minOccurs="0" />
        <element name="nickname" type="string" minOccurs="1" />
        <element name="salary" type="int" minOccurs="0" />
        </sequence>

    </complexType>

</schema>

то ваш код Java будет выглядеть так: -

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.xml.sax.SAXException;

public class testXML {
    //  Lookup a factory for the W3C XML Schema language
    static SchemaFactory factory = SchemaFactory
            .newInstance("http://www.w3.org/2001/XMLSchema");

    //  Compile the schema. 
    static File schemaLocation = new File("company.xsd");
    static Schema schema = null;
    static {
        try {
            schema = factory.newSchema(schemaLocation);
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private final ExecutorService pool = Executors.newFixedThreadPool(20);;

    boolean validate(StringBuffer splitBuffer) {
        boolean isValid = false;
        Validator validator = schema.newValidator();
        try {
            validator.validate(new StreamSource(new ByteArrayInputStream(
                    splitBuffer.toString().getBytes())));
            isValid = true;
        } catch (SAXException ex) {
            System.out.println(ex.getMessage());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return isValid;

    }

    void split(BufferedReader br, String rootElementName,
            String splitElementName) {
        StringBuffer splitBuffer = null;
        String line = null;
        String startRootElement = "<" + rootElementName + ">";
        String endRootElement = "</" + rootElementName + ">";

        String startSplitElement = "<" + splitElementName + ">";
        String endSplitElement = "</" + splitElementName + ">";
        String xmlDeclaration = "<?xml version=\"1.0\"";
        boolean startFlag = false, endflag = false;
        try {
            while ((line = br.readLine()) != null) {
                if (line.contains(xmlDeclaration)
                        || line.contains(startRootElement)
                        || line.contains(endRootElement)) {
                    continue;
                }

                if (line.contains(startSplitElement)) {
                    startFlag = true;
                    endflag = false;
                    splitBuffer = new StringBuffer(startRootElement);
                    splitBuffer.append(line);

                } else if (line.contains(endSplitElement)) {
                    endflag = true;
                    startFlag = false;
                    splitBuffer.append(line);
                    splitBuffer.append(endRootElement);

                } else if (startFlag) {
                    splitBuffer.append(line);
                }

                if (endflag) {
                    //process splitBuffer
                    boolean result = validate(splitBuffer);
                    if (result) {
                        //send it to a thread for processing further
                        //it is async so that main thread can continue for next

                        pool.submit(new ProcessingHandler(splitBuffer));

                    }
                }

            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

class ProcessingHandler implements Runnable {
    String splitXML = null;

    ProcessingHandler(StringBuffer splitXMLBuffer) {
        this.splitXML = splitXMLBuffer.toString();
    }

    @Override
    public void run() {
        // do like writing to a file etc.

    }

}

Ответ 6

Посмотрите на это. Это немного переработанный образец из xmlpull.org:

http://www.xmlpull.org/v1/download/unpacked/doc/quick_intro.html

Следующее должно сделать все, что вам нужно, если у вас нет вложенных тегов разделения:

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<company>
    <staff id="1">
        <firstname>yong</firstname>
        <lastname>mook kim</lastname>
        <nickname>mkyong</nickname>
        <salary>100000</salary>
        <other>
            <staff>
            ...
            </staff>
        </other>
    </staff>
</company>

Чтобы запустить его в режиме сквозной передачи, просто передайте null как разделительный тег.

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

public class XppSample {

private String rootTag;
private String splitTag;
private String requiredTag;
private int flushThreshold;
private String fileName;

private String rootTagEnd;

private boolean hasRequiredTag = false;
private int flushCount = 0;
private int fileNo = 0;
private String header;
private XmlPullParser xpp;
private StringBuilder nodeBuf = new StringBuilder();
private StringBuilder fileBuf = new StringBuilder();


public XppSample(String fileName, String rootTag, String splitTag, String requiredTag, int flushThreshold) throws XmlPullParserException, FileNotFoundException {

    this.rootTag = rootTag;
    rootTagEnd = "</" + rootTag + ">";
    this.splitTag = splitTag;
    this.requiredTag = requiredTag;
    this.flushThreshold = flushThreshold;
    this.fileName = fileName; 

    XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null);
    factory.setNamespaceAware(true);
    xpp = factory.newPullParser();
    xpp.setInput(new FileReader(fileName));
}


public void processDocument() throws XmlPullParserException, IOException {
    int eventType = xpp.getEventType();
    do {
        if(eventType == XmlPullParser.START_TAG) {
            processStartElement(xpp);
        } else if(eventType == XmlPullParser.END_TAG) {
            processEndElement(xpp);
        } else if(eventType == XmlPullParser.TEXT) {
            processText(xpp);
        }
        eventType = xpp.next();
    } while (eventType != XmlPullParser.END_DOCUMENT);

    saveFile();
}


public void processStartElement(XmlPullParser xpp) {

    int holderForStartAndLength[] = new int[2];
    String name = xpp.getName();
    char ch[] = xpp.getTextCharacters(holderForStartAndLength);
    int start = holderForStartAndLength[0];
    int length = holderForStartAndLength[1];

    if(name.equals(rootTag)) {
        int pos = start + length;
        header = new String(ch, 0, pos);
    } else {
        if(requiredTag==null || name.equals(requiredTag)) {
            hasRequiredTag = true;
        }
        nodeBuf.append(xpp.getText());
    }
}


public void flushBuffer() throws IOException {
    if(hasRequiredTag) {
        fileBuf.append(nodeBuf);
        if(((++flushCount)%flushThreshold)==0) {
            saveFile();
        }           
    }
    nodeBuf = new StringBuilder();
    hasRequiredTag = false;
}


public void saveFile() throws IOException {
    if(fileBuf.length()>0) {
        String splitFile = header + fileBuf.toString() + rootTagEnd;
        FileUtils.writeStringToFile(new File((fileNo++) + "_" + fileName), splitFile);
        fileBuf = new StringBuilder();
    }
}


public void processEndElement (XmlPullParser xpp) throws IOException {

    String name = xpp.getName();

    if(name.equals(rootTag)) {
        flushBuffer();
    } else {
        nodeBuf.append(xpp.getText());
        if(name.equals(splitTag)) {
            flushBuffer();
        }
    }
}


public void processText (XmlPullParser xpp) throws XmlPullParserException {

    int holderForStartAndLength[] = new int[2];
    char ch[] = xpp.getTextCharacters(holderForStartAndLength);
    int start = holderForStartAndLength[0];
    int length = holderForStartAndLength[1];
    String content = new String(ch, start, length);

    nodeBuf.append(content);
}


public static void main (String args[]) throws XmlPullParserException, IOException {

    //XppSample app = new XppSample("input.xml", "company", "staff", "nickname", 3);
    XppSample app = new XppSample("input.xml", "company", "staff", null, 3);
    app.processDocument();
}

}

Ответ 7

Обычно я предлагаю использовать StAX, но мне непонятно, как "с точки зрения состояния" ваш настоящий XML. Если это просто, используйте SAX для максимальной производительности, если не так просто, используйте StAX. Поэтому вам нужно

  • чтение байтов с диска
  • преобразовать их в символы
  • проанализировать XML
  • определить, следует ли хранить XML или выбрасывать (пропустить поддерево)
  • написать XML
  • конвертировать символы в байты
  • записать на диск

Теперь может показаться, что шаги 3-5 являются наиболее ресурсоемкими, но я бы оценил их как

Большинство: 1 + 7
Средний: 2 + 6
Наименее: 3 + 4 + 5

Поскольку операции 1 и 7 являются отдельными от остальных, вы должны сделать их асинхронно, по крайней мере, создание нескольких небольших файлов лучше всего делать в n других потоках, если вы знакомы с многопоточность. Для повышения производительности вы также можете изучить новый материал ввода-вывода в Java.

Теперь для шагов 2 + 3 и 5 + 6 вы можете пройти долгий путь с FasterXML, это действительно делает много материал, который вы ищете, например, включение внимания JVM в нужные места; может даже поддерживать асинхронное чтение/запись, быстро просматривая код.

Итак, мы остаемся на шаге 5, и в зависимости от вашей логики вы должны либо

а. сделать привязку к объекту, а затем решить, как это сделать б. пишите XML в любом случае, надеясь на лучшее, а затем выбросьте его, если нет элемента "staff".

Что бы вы ни делали, повторное использование объекта разумно. Обратите внимание, что обе альтернативы (потихоньку) требуют одинакового разбора (пропустить из поддерева ASAP), а для альтернативы b - немного лишний XML на самом деле не так плох результативно, в идеале убедитесь, что ваши буферы char - это одна единица,

Альтернатива b проще всего реализовать, просто скопируйте "событие xml" со ​​своего читателя на писателя, например, для StAX:

private static void copyEvent(int event, XMLStreamReader  reader, XMLStreamWriter writer) throws XMLStreamException {
    if (event == XMLStreamConstants.START_ELEMENT) {
        String localName = reader.getLocalName();
        String namespace = reader.getNamespaceURI();
        // TODO check this stuff again before setting in production
        if (namespace != null) {
            if (writer.getPrefix(namespace) != null) {
                writer.writeStartElement(namespace, localName);
            } else {
                writer.writeStartElement(reader.getPrefix(), localName, namespace);
            }
        } else {
            writer.writeStartElement(localName);
        }
        // first: namespace definition attributes
        if(reader.getNamespaceCount() > 0) {
            int namespaces = reader.getNamespaceCount();

            for(int i = 0; i < namespaces; i++) {
                String namespaceURI = reader.getNamespaceURI(i);

                if(writer.getPrefix(namespaceURI) == null) {
                    String namespacePrefix = reader.getNamespacePrefix(i);

                    if(namespacePrefix == null) {
                        writer.writeDefaultNamespace(namespaceURI);
                    } else {
                        writer.writeNamespace(namespacePrefix, namespaceURI);
                    }
                }
            }
        }
        int attributes = reader.getAttributeCount();

        // the write the rest of the attributes
        for (int i = 0; i < attributes; i++) {
            String attributeNamespace = reader.getAttributeNamespace(i);
            if (attributeNamespace != null && attributeNamespace.length() != 0) {
                writer.writeAttribute(attributeNamespace, reader.getAttributeLocalName(i), reader.getAttributeValue(i));
            } else {
                writer.writeAttribute(reader.getAttributeLocalName(i), reader.getAttributeValue(i));
            }
        }
    } else if (event == XMLStreamConstants.END_ELEMENT) {
        writer.writeEndElement();
    } else if (event == XMLStreamConstants.CDATA) {
        String array = reader.getText();
        writer.writeCData(array);
    } else if (event == XMLStreamConstants.COMMENT) {
        String array = reader.getText();
        writer.writeComment(array);
    } else if (event == XMLStreamConstants.CHARACTERS) {
        String array = reader.getText();
        if (array.length() > 0 && !reader.isWhiteSpace()) {
            writer.writeCharacters(array);
        }
    } else if (event == XMLStreamConstants.START_DOCUMENT) {
        writer.writeStartDocument();
    } else if (event == XMLStreamConstants.END_DOCUMENT) {
        writer.writeEndDocument();
    }
}

И для поддерева

private static void copySubTree(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
    reader.require(XMLStreamConstants.START_ELEMENT, null, null);

    copyEvent(XMLStreamConstants.START_ELEMENT, reader, writer);

    int level = 1;
    do {
        int event = reader.next();
        if(event == XMLStreamConstants.START_ELEMENT) {
            level++;
        } else if(event == XMLStreamConstants.END_ELEMENT) {
            level--;
        }

        copyEvent(event, reader, writer);
    } while(level > 0);

}

Из которого вы, вероятно, можете вычесть, как перейти на определенный уровень. В общем случае для анализа StaX с использованием состояния используйте шаблон

private static void parseSubTree(XMLStreamReader reader) throws XMLStreamException {

    int level = 1;
    do {
        int event = reader.next();
        if(event == XMLStreamConstants.START_ELEMENT) {
            level++;
            // do stateful stuff here

            // for child logic:
            if(reader.getLocalName().equals("Whatever")) {
                parseSubTreeForWhatever(reader);
                level --; // read from level 1 to 0 in submethod.
            }

            // alternatively, faster
            if(level == 4) {
                parseSubTreeForWhateverAtRelativeLevel4(reader);
                level --; // read from level 1 to 0 in submethod.
            }


        } else if(event == XMLStreamConstants.END_ELEMENT) {
            level--;
            // do stateful stuff here, too
        }

    } while(level > 0);

}

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

Обратите внимание: если вы выполняете привязку к объекту, эти методы должны быть помещены в этот объект и одинаково для методов сериализации.

Я уверен, что вы получите 10 МБ/с в современной системе, и этого должно быть достаточно. Еще одна проблема, которую следует исследовать, - это подходы к использованию нескольких ядер для фактического ввода, если вы знаете, что подмножество кодировки, например, не сумасшедшее UTF-8 или ISO-8859, тогда возможен случайный доступ → отправить к различным ядрам.

Получайте удовольствие и расскажите, как это получилось;)

Изменить: почти забыл, если вы по какой-то причине тот, кто создает файл в первую очередь, или вы будете читать их после расщепления, вы получите ОГРОМНУЮ прирост производительности с использованием XML бинаризации; существуют генераторы XML Schema, которые снова могут входить в генераторы кода. (И некоторые библиотеки преобразования XSLT также используют генерацию кода.) И запустите с помощью опции -server для JVM.

Ответ 8

Как сделать я быстрее:

  • Использование асинхронной записи, возможно, параллельно, может повысить ваш перфоманс, если у вас есть RAID-X, что-то диск
  • Запись на SSD вместо HDD

Ответ 9

Мое предложение состоит в том, что SAX, STAX или DOM не являются идеальным анализатором xml для вашей проблемы, идеальные решения называются vtd-xml, есть статья на эту тему, объясняющая, почему DOM sax и STAX сделали что-то очень неправильное... приведенный ниже код является самым коротким, что вам нужно написать, но выполняет в 10 раз быстрее, чем DOM или SAX. http://www.javaworld.com/javaworld/jw-07-2006/jw-0724-vtdxml.html

Вот последняя статья под названием Обработка XML с помощью Java - Performance Benchmark: http://recipp.ipp.pt/bitstream/10400.22/1847/1/ART_BrunoOliveira_2013.pdf p >

import com.ximpleware.*;
import java.io.*;
public class gandalf {
    public  static void main(String a[]) throws VTDException, Exception{
        VTDGen vg = new VTDGen();
        if (vg.parseFile("c:\\xml\\gandalf.txt", false)){
            VTDNav vn=vg.getNav();
            AutoPilot ap = new AutoPilot(vn);
            ap.selectXPath("/company/staff[nickname]");
            int i=-1;
            int count=0;
            while((i=ap.evalXPath())!=-1){
                vn.dumpFragment("c:\\xml\\staff"+count+".xml");
                count++;
            }
        }
    }

}

Ответ 10

Вот решение на основе DOM. Я проверил это с помощью xml, который вы предоставили. Это нужно проверить в отношении фактических файлов xml, которые у вас есть.

Так как это основано на DOM-парсере, помните, что для этого потребуется много памяти в зависимости от размера вашего XML файла. Но это намного быстрее, чем DOM.

Алгоритм:

  • Разбор документа
  • Извлечь имя корневого элемента
  • Получить список узлов, основанных на критериях разделения (используя XPath)
  • Для каждого node создайте пустой документ с именем корневого элемента, как показано на шаге # 2
  • Вставьте node в этот новый документ
  • Проверьте, должны ли файлы фильтроваться или нет.
  • Если необходимо отфильтровать узлы, проверьте, присутствует ли указанный элемент во вновь создаваемом документе.
  • Если node нет, не записывайте в файл.
  • Если узлы НЕ должны быть отфильтрованы вообще, не проверяйте условие в # 7 и запишите документ в файл.

Это можно запустить из командной строки следующим образом

java    XMLSplitter xmlFileLocation  splitElement filter filterElement

Для xml, о котором вы упомянули, будет

java    XMLSplitter input.xml  staff  true nickname

Если вы не хотите фильтровать

java    XMLSplitter input.xml  staff 

Вот полный код Java:

пакет com.xml.xpath;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.DOMException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class XMLSplitter {

    DocumentBuilder builder = null;
    XPath xpath = null; 
    Transformer transformer = null;
    String filterElement;
    String splitElement;
    String xmlFileLocation;
    boolean filter = true;


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

        XMLSplitter xMLSplitter = null;
        if(arg.length < 4){

            if(arg.length < 2){
                System.out.println("Insufficient arguments !!!");
                System.out.println("Usage: XMLSplitter xmlFileLocation  splitElement filter filterElement ");
                return;
            }else{
                System.out.println("Filter is off...");
                xMLSplitter = new XMLSplitter();
                xMLSplitter.init(arg[0],arg[1],false,null);
            }

        }else{
            xMLSplitter = new XMLSplitter();
            xMLSplitter.init(arg[0],arg[1],Boolean.parseBoolean(arg[2]),arg[3]);
        }



        xMLSplitter.start();    

    }

    public void init(String xmlFileLocation, String splitElement, boolean filter, String filterElement ) 
                throws ParserConfigurationException, TransformerConfigurationException{

        //Initialize the Document builder
        System.out.println("Initializing..");
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true);   
        builder = domFactory.newDocumentBuilder();

        //Initialize the transformer
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");

        //Initialize the xpath
        XPathFactory factory = XPathFactory.newInstance();
        xpath = factory.newXPath();

        this.filterElement = filterElement;
        this.splitElement = splitElement;
        this.xmlFileLocation = xmlFileLocation;
        this.filter = filter;


    }   


    public void start() throws Exception{

            //Parser the file 
            System.out.println("Parsing file.");
            Document doc = builder. parse(xmlFileLocation);

            //Get the root node name
            System.out.println("Getting root element.");
            XPathExpression rootElementexpr = xpath.compile("/");
            Object rootExprResult = rootElementexpr.evaluate(doc, XPathConstants.NODESET);
            NodeList rootNode = (NodeList) rootExprResult;          
            String rootNodeName = rootNode.item(0).getFirstChild().getNodeName();

            //Get the list of split elements
            XPathExpression expr = xpath.compile("//"+splitElement);
            Object result = expr.evaluate(doc, XPathConstants.NODESET);
            NodeList nodes = (NodeList) result;
            System.out.println("Total number of split nodes "+nodes.getLength());
            for (int i = 0; i < nodes.getLength(); i++) {
                //Wrap each node inside root of the parent xml doc
                Node sigleNode = wrappInRootElement(rootNodeName,nodes.item(i));
                //Get the XML string of the fragment
                String xmlFragment = serializeDocument(sigleNode);
                //System.out.println(xmlFragment);
                //Write the xml fragment in file.
                storeInFile(xmlFragment,i);         
            }

    }

    private  Node wrappInRootElement(String rootNodeName, Node fragmentDoc) 
                throws XPathExpressionException, ParserConfigurationException, DOMException, 
                        SAXException, IOException, TransformerException{

        //Create empty doc with just root node
        DOMImplementation domImplementation = builder.getDOMImplementation();
        Document doc = domImplementation.createDocument(null,null,null);
        Element theDoc = doc.createElement(rootNodeName);
        doc.appendChild(theDoc);

        //Insert the fragment inside the root node 
        InputSource inStream = new InputSource();     
        String xmlString = serializeDocument(fragmentDoc);
        inStream.setCharacterStream(new StringReader(xmlString));       
        Document fr = builder.parse(inStream);
        theDoc.appendChild(doc.importNode(fr.getFirstChild(),true));
        return doc;
    }

    private String serializeDocument(Node doc) throws TransformerException, XPathExpressionException{

        if(!serializeThisNode(doc)){
            return null;
        }

        DOMSource domSource = new DOMSource(doc);                
        StringWriter stringWriter = new StringWriter();
        StreamResult streamResult = new StreamResult(stringWriter);
        transformer.transform(domSource, streamResult);
        String xml = stringWriter.toString();
        return xml;

    }

    //Check whether node is to be stored in file or rejected based on input
    private boolean serializeThisNode(Node doc) throws XPathExpressionException{

         if(!filter){
             return true;
         }

         XPathExpression filterElementexpr = xpath.compile("//"+filterElement);
         Object result = filterElementexpr.evaluate(doc, XPathConstants.NODESET);
         NodeList nodes = (NodeList) result;

         if(nodes.item(0) != null){
             return true;
         }else{
             return false;
         }       
    }

    private void storeInFile(String content, int fileIndex) throws IOException{

        if(content == null || content.length() == 0){
            return;
        }

        String fileName = splitElement+fileIndex+".xml";

        File file = new File(fileName);
        if(file.exists()){
            System.out.println(" The file "+fileName+" already exists !! cannot create the file with the same name ");
            return;
        }
        FileWriter fileWriter = new FileWriter(file);
        fileWriter.write(content);
        fileWriter.close();
        System.out.println("Generated file "+fileName);


    }

}

Сообщите мне, если это работает для вас или любой другой помощи в отношении этого кода.