Java XML (DOM)

di il
11 risposte

Java XML (DOM)

Salve a tutti,
sto utilizzando DOM per scansionare una serie di file XML ed effettuare delle operazioni di conteggio.

La cosa strana è che, mentre nei file c'è il metodo close per rilasciare il file, per xml non ci sono tali metodi.

Domanda: Ma quando apro il file xml mediante DOM, poi come si chiude?

Domando questo poichè dopo aver aperto l'ennesimo xml, java va in out of memory.

Grazie a tutti

11 Risposte

  • Re: Java XML (DOM)

    cnesan ha scritto:


    La cosa strana è che, mentre nei file c'è il metodo close per rilasciare il file, per xml non ci sono tali metodi.

    Domanda: Ma quando apro il file xml mediante DOM, poi come si chiude?
    Se hai usato il parsing con approccio DOM tramite la API JAXP standard, allora hai usato DocumentBuilderFactory/DocumentBuilder. E su quest'ultimo hai invocato uno dei parse(). Ce ne sono svariati, in overload con argomenti diversi (es. File, InputStream ecc...).
    Tutti però alla fin fine sono fatti in funzione di quello che riceve un org.xml.sax.InputSource. E la documentazione di questo è abbastanza chiara:
    An InputSource object belongs to the application: the SAX parser shall never modify it in any way (it may modify a copy if necessary). However, standard processing of both byte and character streams is to close them on as part of end-of-parse cleanup, so applications should not attempt to re-use such streams after they have been handed to a parser.
    Ovvero, è il parser che si occupa di chiudere gli stream.

    cnesan ha scritto:


    Domando questo poichè dopo aver aperto l'ennesimo xml, java va in out of memory.
    Così su due piedi non saprei dire a cosa è dovuto ... potrebbe essere causato da ben altri motivi. Senza vedere il codice non saprei che dirti. Se non farti altre domande: quanto sono grandi gli XML che tratti? Cosa fai con il DOM in memoria? Quali algoritmi applichi? Quanto heap-space dai alla JVM?
  • Re: Java XML (DOM)

    Ci sono due modi per fare il parsing di un file xml

    1) mediante la creazione di un DOM (document object model): il file viene aperto, analizzato completamente, creata la struttura dati in memoria e chiuso (quindi non lo devi chiudere tu)
    2) mediante l'intercettazione di eventi SAX, (simple api for xml) in cui il parser, per ogni tag di apertura (<tag>) e chiusura (</tag>), genera un evento. In questo caso il file rimane aperto fino all'evento EndDocument, il quale lo chiude automaticamente. In alternativa, se ti fermi prima, lo chiudi tu esplicitamente.


    Il metodo 2 si usa quando il file xml e' molto grande.

    Se la tua applicaziond va in outofmemory, allora vuol dire che stai mantenendo, da qualche parte, un puntatore ad un nodo del dom del file che hai processato, e questo per TUTTI i file che stai processando!

    ATTENZIONE: se mantieni un puntatore ad un nodo del dom, poiche questo ha un puntarore verso il padre e verso tutti i suoi figli, STAI TENENDO IN MEMORIA L'INTERO DOM!!!


    Alta banale banalita': la quantita' di memoria allocata per il dom, non e' uguale alla dimensione del file, ma [dimensione del file]*K, con K > 1 (probabilmente tra 2 e 4)
  • Re: Java XML (DOM)

    Grazie a tutti,
    ho verificato il codice e avevo impostato quanto segue:
    Inizio ciclo per scansionare un ArrayList<String> FileToOpenXML
    - Apertura e parse DOM del FileToOpen(n)
    - Ricerca di un nodo con TAG x
    - Se trovato, inserimento del nodo in un array nodelist
    fine ciclo

    Generazione in scrittura di un file XML
    - Inserimento nodo root
    - Inserimento dei nodi sotto root presenti nel nodelist
    Salvataggio del file XML

    L'errore l'avevo nel ciclo for. La modifica, che sembra reggere, è quello di
    1) Aprire un file in scrittura XML
    2) Iniziare il ciclo come sopra
    3) Se trovo il nodo, lo copio direttamente ne file XML aperto in scrittura.

    Sembra funzionare ma se ciò è vero significa che quando copio un nodo (con l'intero albero) in un NodeList in realtà mantengo in memoria l'intero oggetto DOM con tutti il file originario?

    Di seguito:
    1) un pezzo del codice che avevo scritto (ora è modificato poichè passo anche il riferimento del file in cui scrivere i nodi:
    2) L'errore avviene in getAllNodeFromArrayList()
    
    	public void OpenReadXML(String FileXML) throws ParserConfigurationException, SAXException, IOException {
    		if(FileXML.isEmpty()==true || FileXML.trim().length()==0) { IOException e = new IOException(); throw e; }		
    		this._fileName = FileXML;	
    
    		this._documentFactory = DocumentBuilderFactory.newInstance();			
    		this._documentBuilder = this._documentFactory.newDocumentBuilder(); 
    		this._document = this._documentBuilder.parse(this._fileName); 
    		this._document.getDocumentElement().normalize();
    
    		this._rootNode = this._document.getChildNodes().item(0);
    		this._rootNodeName = this._rootNode.getNodeName();
    	}
    
    
    public ArrayList<Node> getAllNodeFromArrayList(ArrayList<String> InArrayFile, String RootNodeName, String TagNodeName) {
     if(InArrayFile.isEmpty()==true || InArrayFile.size()==0) { return null; }	//Controllo iniziale
     libStreamXML XMLout  = new libStreamXML();	//Istanza per aprire i file di input
     ArrayList<Node> AllOutNode = new ArrayList<Node>();	//ArrayList che conterrà tutti i nodi principali del file XML di uscita
    		
     for(int i=0; i < InArrayFile.size(); i++) {									//Scansione ArrayList File xml
      try {
    	XMLout.OpenReadXML(InArrayFile.get(i));								//Apre il file 'n'.xml
    	Node nl = XMLout.getNode(RootNodeName);								//Importa un nodelist avente TagNode
    	for(int k=0; k < nl.getChildNodes().getLength() ; k++) {			//Scansione dei nodi figli del nodo principale					
       	  if(nl.getChildNodes().item(k).getNodeName()==TagNodeName) {		//Se il nodo figlio del RootNodeName contiene il TagNodeName
    		AllOutNode.add(XMLout.getNode(nl.getChildNodes().item(k), TagNodeName) );	//Aggiunge il nodo figlio
    	  }
            }				
       } catch (ParserConfigurationException | SAXException | IOException e) { e.printStackTrace(); }						
     }
     return AllOutNode;
    }
    
    public org.w3c.dom.Node getNode(org.w3c.dom.Node NodeStart, String ElementName) {
     if(NodeStart.getNodeName()==ElementName) { return NodeStart; }			//Caso in cui l'elemento di NodeStart è l'elemento ricercato
      NodeList alpha = NodeStart.getChildNodes();								//Ottiene l'elenco dei nodi figli
      for(int i = 0; i<alpha.getLength(); i++) {								//Avvio scansione degli elementi figli
    	if(alpha.item(i).getNodeName()==ElementName) { 						//Se l'elemento i ha il nome ricercato
    		return alpha.item(i); 											//- Ritorna il nodo dell'elemento
    	} else { 															//Oppure
    		if(alpha.item(i).hasChildNodes()==true) {						// Se l'elemento a sua volta a nodi figli
    			org.w3c.dom.Node beta = getNode(alpha.item(i),ElementName);	// - Richiama il metodo (ricorsivo) per cercare nell'albero
    			if(beta!=null) { return beta; }	//   Se il metodo restituisce un riferimento al nodo valido, il metodo termina
    		}																//
    	}											//Continua il ciclo.
      }
     return null;
    }
    
    
  • Re: Java XML (DOM)

    Il metodo di seguito aggiunto che sostituisce il precedente:
    
    public boolean getAllNodeFromArrayList(org.w3c.dom.Node DestinationNode, ArrayList<String> InArrayFile, String RootNodeName, String TagNodeName) {
    		
     if(InArrayFile.isEmpty()==true || InArrayFile.size()==0) { return false; }	//Controllo iniziale
       StreamXML XMLout  = new StreamXML();						//Istanza per aprire i file di input
       for(int i=0; i < InArrayFile.size(); i++) {						//Scansione ArrayList File xml
        try {
          XMLout.OpenReadXML(InArrayFile.get(i));			//Apre il file 'n'.xml
          Node nl = XMLout.getNode(RootNodeName);		//Importa un nodelist avente TagNode
          for(int k=0; k < nl.getChildNodes().getLength() ; k++) { //Scansione dei nodi figli del nodo principale					
    	if(nl.getChildNodes().item(k).getNodeName()==TagNodeName) {		//Se il nodo figlio del RootNodeName contiene il TagNodeName
    	  CopyNodeInto(DestinationNode, XMLout.getNode(nl.getChildNodes().item(k), TagNodeName));						
    	}
          }
         } catch (ParserConfigurationException | SAXException | IOException e) { e.printStackTrace(); }						
        }		
        return true;
    }	
    
    	public void CopyNodeInto(org.w3c.dom.Node NodeBase, org.w3c.dom.Node NodeToInsert) { 			
    		//DocumentBuilderFactory DocFactory = DocumentBuilderFactory.newInstance();
    		//DocumentBuilder DocBuilder = null;
    		//try { DocBuilder = DocFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { e.printStackTrace(); }
    		//Document Doc = DocBuilder.newDocument();
    		org.w3c.dom.Node importedNode = ((org.w3c.dom.Document) this._document).importNode(NodeToInsert, true);
    		//this._Document.importNode(importedNode, true);
    		//this._Document.importNode(importedNode, true);		
    		NodeBase.appendChild(importedNode); 
    		//this.MoveNode(NodeBase, importedNode);
    		//return importedNode;
    		//System.out.println();
    	}
    
    
    
    StreamXML è una libreria che ho fatto per gestire i file XML. Ho usato un livello astratto, nel senso che le operazioni sono definite nella libreria ma gli argomenti vengono definiti dall'utilizzatore.

    Sono ben accette consigli.

    Grazie
  • Re: Java XML (DOM)

    Cnesan, il tuo codice è parecchio contorto e confuso. Innanzitutto per come è scritto e perché non hai nemmeno usato le convenzioni di denominazione standard.
    Inoltre vedo cose del tipo:

    if(NodeStart.getNodeName()==ElementName)

    Il contenuto delle stringhe si confronta con equals() ... non con ==. Il tuo codice è sicuramente sbagliato.

    Quindi te lo dico gentilmente (e senza offesa): prima di affrontare XML, librerie e quant'altro, dovresti affrontare meglio questi aspetti del linguaggio.
  • Re: Java XML (DOM)

    Si anche questo è vero....

    Non sono nato imparato e di certo non mi offendo perchè ora mi sto avvicinando ai linguaggi OOP e Java.

    Anzi, rilevare queste cose è molto importante per me, pertanto sono io a ringraziarti per i suggerimenti.

    Sto effettuando le correzioni in merito poichè infatti è un errore l'uso == (penso che ciò è per 15 anni di VB6... Arggggg!)
  • Re: Java XML (DOM)

    Sto leggendo qualcosa in merito a: "le convenzioni di denominazione standard", per applicarle.
  • Re: Java XML (DOM)

    cnesan ha scritto:


    Sto leggendo qualcosa in merito a: "le convenzioni di denominazione standard", per applicarle.
    Code Conventions for the Java Programming Language

    Il documento è "vecchio" (lo dice anche Oracle in quella pagina) ma è tuttora valido ed appropriato.
  • Re: Java XML (DOM)

    Andbin,
    ti ringrazio del link e lo sto leggendo.

    Già sto modificando tutta la libreria (package / classi / metodi / campi / Costanti / Docs) secondo quanto definito dal link.
    Beato colui che ha ideato il refactor.

    Nel rivedere tutta la libreria e ridefinendo correttamente tutti i campi, metodi e classi (dove serve) ho notato che posso effettivamente attuare delle migliorie al codice.
    Inoltre ho visto che ho incanalato troppi richiami di metodi innestandoli in altri passati come argomento!! Anche questo è in fase di rettifica.

    Per quanto riguarda i testi:
    Vedendo su "Play Store" ho preso anche un eBook "Java Best Practice" che mi sembra abbastanza interessante.
    Ho preso anche l'altro testo che mi hai consigliato sui thread, "Java Concurrency in Practice" (tuo consiglio nell'altro post) relativo alla programmazione concorrente, in formato eBook.

    Ritornando all'inizio del post:
    Premetto che:
    Sono consapevole che il tipo String è un tipo di oggetto e che la comparazione "==" è ammessa solo per i tipi base.
    Per gli oggetti, invece, devono essere utilizzati i metodi specifici poichè con i "==" viene comparato SOLO l'indirizzo (diciamo così).
    La cosa è che non mi ero accorto della cosa infatti ho fatto un controllo in tutta la libreria e non ho trovato la stessa cosa.
    Tuttavia io non utilizzo il metodo equal() ma contentEquals() poichè sapevo che equal() non è sempre attendibile e deve sempre essere ridefinito in base alla situazione.

    La cosa che non capisco è perchè, facendo quella comparazione, funziona.
    E' come se Java effettuasse una uguaglianza per valore!!!! Dovrebbe, in linea teorica, restituire sempre False poichè un'operando è una stringa mentre l'altro è un ritorno di un metodo di tipo stringa e quindi due oggetti teoricamente diversi.

    Devo pensare/intendere che se la Virtual Macchine di Java verifica che il contenuto di entrambi gli oggetti è lo stesso, la JVM assegna lo stesso spazio di memoria e quindi memorizza lo stesso indirizzo per entrambi? Può essere?

    Grazie ancora (vale ancha per migliorabile ovviamente)
  • Re: Java XML (DOM)

    >E' come se Java effettuasse una uguaglianza per valore!!!! Dovrebbe, in linea teorica, restituire sempre False poichè un'operando è una stringa >mentre l'altro è un ritorno di un metodo di tipo stringa e quindi due oggetti teoricamente diversi.
    sei sicuro non sia un operazione tra oggetti stringa?
    per quanto riguarda l'uso di dom in passato dovevo caricare dei file xml per inserire dei dati in un db oracle e con dom l'immagine del file in memoria era 10 volte la grandezza del file però usavo c#
  • Re: Java XML (DOM)

    Infatti migliorabile (sopra) lo dice:

    Alta banale banalita': la quantita' di memoria allocata per il dom, non e' uguale alla dimensione del file, ma [dimensione del file]*K, con K > 1 (probabilmente tra 2 e 4)
Devi accedere o registrarti per scrivere nel forum
11 risposte