consultas comandos java xml xml-parsing sax

comandos - Cómo analizar grandes archivos XML(50 GB) en Java



comandos elasticsearch (2)

Actualmente estoy intentando usar un SAX Parser, pero aproximadamente 3/4 a través del archivo, se congela completamente, he intentado asignar más memoria, etc. pero no obteniendo ninguna mejora.

Hay alguna manera de acelerar esto? ¿Un método mejor?

Lo despojé por completo, así que ahora tengo el siguiente código y cuando lo ejecuto en la línea de comandos, todavía no funciona tan rápido como me gustaría.

Al ejecutarlo con "java -Xms-4096m -Xmx8192m -jar reader.jar" obtengo un límite de sobrecarga del GC excedido alrededor del artículo 700000

Principal:

public class Read { public static void main(String[] args) { pages = XMLManager.getPages(); } }

XMLManager

public class XMLManager { public static ArrayList<Page> getPages() { ArrayList<Page> pages = null; SAXParserFactory factory = SAXParserFactory.newInstance(); try { SAXParser parser = factory.newSAXParser(); File file = new File("..//enwiki-20140811-pages-articles.xml"); PageHandler pageHandler = new PageHandler(); parser.parse(file, pageHandler); pages = pageHandler.getPages(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return pages; } }

PageHandler

public class PageHandler extends DefaultHandler{ private ArrayList<Page> pages = new ArrayList<>(); private Page page; private StringBuilder stringBuilder; private boolean idSet = false; public PageHandler(){ super(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { stringBuilder = new StringBuilder(); if (qName.equals("page")){ page = new Page(); idSet = false; } else if (qName.equals("redirect")){ if (page != null){ page.setRedirecting(true); } } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (page != null && !page.isRedirecting()){ if (qName.equals("title")){ page.setTitle(stringBuilder.toString()); } else if (qName.equals("id")){ if (!idSet){ page.setId(Integer.parseInt(stringBuilder.toString())); idSet = true; } } else if (qName.equals("text")){ String articleText = stringBuilder.toString(); articleText = articleText.replaceAll("(?s)<ref(.+?)</ref>", " "); //remove references articleText = articleText.replaceAll("(?s)//{//{(.+?)//}//}", " "); //remove links underneath headings articleText = articleText.replaceAll("(?s)==See also==.+", " "); //remove everything after see also articleText = articleText.replaceAll("//|", " "); //Separate multiple links articleText = articleText.replaceAll("//n", " "); //remove new lines articleText = articleText.replaceAll("[^a-zA-Z0-9- //s]", " "); //remove all non alphanumeric except dashes and spaces articleText = articleText.trim().replaceAll(" +", " "); //convert all multiple spaces to 1 space Pattern pattern = Pattern.compile("([//S]+//s*){1,75}"); //get first 75 words of text Matcher matcher = pattern.matcher(articleText); matcher.find(); try { page.setSummaryText(matcher.group()); } catch (IllegalStateException se){ page.setSummaryText("None"); } page.setText(articleText); } else if (qName.equals("page")){ pages.add(page); page = null; } } else { page = null; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { stringBuilder.append(ch,start, length); } public ArrayList<Page> getPages() { return pages; } }


El enfoque de Don Roby recuerda algo al enfoque que seguí al crear un generador de código diseñado para resolver este problema en particular (una versión anterior se concibió en 2008). Básicamente, cada complexType tiene su equivalente de Java POJO y los controladores para el tipo particular se activan cuando el contexto cambia a ese elemento. Utilicé este enfoque para SEPA, transacciones bancarias y, por ejemplo, discogs (30 GB). Puede especificar qué elementos desea procesar en tiempo de ejecución, utilizando de forma declarativa un archivo de propiedades.

XML2J utiliza la asignación de complexTypes a Java POJOs por un lado, pero le permite especificar los eventos que desea escuchar. P.ej

account/@process = true account/accounts/@process = true account/accounts/@detach = true

La esencia está en la tercera línea. La separación se asegura de que las cuentas individuales no se agreguen a la lista de cuentas. Así que no se desbordará.

class AccountType { private List<AccountType> accounts = new ArrayList<>(); public void addAccount(AccountType tAccount) { accounts.add(tAccount); } // etc. };

En su código, debe implementar el método de proceso (de manera predeterminada, el generador de código genera un método vacío:

class AccountsProcessor implements MessageProcessor { static private Logger logger = LoggerFactory.getLogger(AccountsProcessor.class); // assuming Spring data persistency here final String path = new ClassPathResource("spring-config.xml").getPath(); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path); AccountsTypeRepo repo = context.getBean(AccountsTypeRepo.class); @Override public void process(XMLEvent evt, ComplexDataType data) throws ProcessorException { if (evt == XMLEvent.END) { if( data instanceof AccountType) { process((AccountType)data); } } } private void process(AccountType data) { if (logger.isInfoEnabled()) { // do some logging } repo.save(data); } }

Tenga en cuenta que XMLEvent.END marca la etiqueta de cierre de un elemento. Entonces, cuando lo estás procesando, está completo. Si tiene que relacionarlo (usando un FK) con su objeto principal en la base de datos, puede procesar XMLEvent.BEGIN para el principal, crear un marcador de posición en la base de datos y usar su clave para almacenar con cada uno de sus secundarios. En el último XMLEvent.END luego actualizaría el padre.

Tenga en cuenta que el generador de código genera todo lo que necesita. Solo tienes que implementar ese método y por supuesto el código de pegamento DB.

Hay muestras para empezar. El generador de código incluso genera sus archivos POM, por lo que puede generar su proyecto inmediatamente después de la generación.

El método de proceso predeterminado es así:

@Override public void process(XMLEvent evt, ComplexDataType data) throws ProcessorException { /* * TODO Auto-generated method stub implement your own handling here. * Use the runtime configuration file to determine which events are to be sent to the processor. */ if (evt == XMLEvent.END) { data.print( ConsoleWriter.out ); } }

Descargas:

Primero, mvn clean install el núcleo (tiene que estar en el repositorio local de maven), luego el generador. Y no olvide configurar la variable de entorno XML2J_HOME según las instrucciones en el manual de usuario.


Es probable que su código de análisis funcione bien, pero el volumen de datos que está cargando es probablemente demasiado grande para almacenar en la memoria en esa lista de ArrayList .

Necesita algún tipo de canalización para pasar los datos a su destino real sin tener que almacenarlos todos en la memoria a la vez.

Lo que algunas veces he hecho para este tipo de situación es similar a lo siguiente.

Crea una interfaz para procesar un solo elemento:

public interface PageProcessor { void process(Page page); }

Proporcione una implementación de esto al PageHandler través de un constructor:

public class Read { public static void main(String[] args) { XMLManager.load(new PageProcessor() { @Override public void process(Page page) { // Obviously you want to do something other than just printing, // but I don''t know what that is... System.out.println(page); } }) ; } } public class XMLManager { public static void load(PageProcessor processor) { SAXParserFactory factory = SAXParserFactory.newInstance(); try { SAXParser parser = factory.newSAXParser(); File file = new File("pages-articles.xml"); PageHandler pageHandler = new PageHandler(processor); parser.parse(file, pageHandler); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }

Envíe datos a este procesador en lugar de ponerlos en la lista:

public class PageHandler extends DefaultHandler { private final PageProcessor processor; private Page page; private StringBuilder stringBuilder; private boolean idSet = false; public PageHandler(PageProcessor processor) { this.processor = processor; } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { //Unchanged from your implementation } @Override public void characters(char[] ch, int start, int length) throws SAXException { //Unchanged from your implementation } @Override public void endElement(String uri, String localName, String qName) throws SAXException { // Elide code not needing change } else if (qName.equals("page")){ processor.process(page); page = null; } } else { page = null; } } }

Por supuesto, puede hacer que su interfaz maneje trozos de múltiples registros en lugar de solo uno, y PageHandler el PageHandler recopile las páginas localmente en una lista más pequeña y periódicamente envíe la lista para su procesamiento y borre la lista.

O (quizás mejor) podría implementar la interfaz de PageProcessor como se define aquí y construir la lógica allí que almacena en búfer los datos y los envía para su posterior manejo en trozos.