2011-08-26 8 views
5

È possibile fornire espressioni di percorso nel parser SAX? Ho un file XML che ha alcuni tag con lo stesso nome, ma sono in elementi diversi. C'è un modo per differenziare tra loro. Ecco l'XML:utilizzando parser SAX, come si analizza un file xml che ha lo stesso nome ma in elementi diversi?

<Schools> 
    <School> 
     <ID>335823</ID> 
     <Name>Fairfax High School</Name> 
     <Student> 
      <ID>4195653</ID> 
      <Name>Will Turner</Name> 
     </Student> 
     <Student> 
      <ID>4195654</ID> 
      <Name>Bruce Paltrow</Name> 
     </Student> 
     <Student> 
      <ID>4195655</ID> 
      <Name>Santosh Gowswami</Name> 
     </Student> 
    </School> 
    <School> 
     <ID>335824</ID> 
     <Name>FallsChurch High School</Name> 
     <Student> 
      <ID>4153</ID> 
      <Name>John Singer</Name> 
     </Student> 
     <Student> 
      <ID>4154</ID> 
      <Name>Shane Warne</Name> 
     </Student> 
     <Student> 
      <ID>4155</ID> 
      <Name>Eddie Diaz</Name> 
     </Student> 
    </School> 
</Schools> 

voglio distinguere tra il nome e l'ID di uno studente il nome e l'ID di una scuola.

Grazie per la risposta:

ho creato un POJO studente che ha il seguente settori- school_id, school_name, student_id e metodi student_name e getter e setter per loro. Questa è la mia implementazione temporanea del parser. Quando analizzo l'xml, devo inserire i valori del nome della scuola, id, nome dello studente, id nel pojo e restituirlo. Puoi dirmi su come dovrei implementare lo stack per la differenziazione. Questo è il mio framework del parser ::

import org.xml.sax.Attributes; 
import org.xml.sax.SAXException; 
import org.xml.sax.helpers.DefaultHandler; 

public class HandleXML extends DefaultHandler { 

    private student info; 
    private boolean school_id = false; 
    private boolean school_name = false; 
    private boolean student_id = false; 
    private boolean student_name = false; 
    private boolean student = false; 
    private boolean school = false; 


    public HandleXML(student record) { 
     super(); 
     this.info = record; 
     school_id = false; 
     school_name = false; 
     student_id = false; 
     student_name = false; 
     student = false; 
     school = false; 
    } 

    @Override 
    public void startElement(String uri, String localName, 
      String qName, Attributes attributes) 
      throws SAXException { 
    if (qName.equalsIgnoreCase("student")) { 
      student = true; 
     } 
    if (qName.equalsIgnoreCase("school")) { 
      school_id = true; 
     } 
    if (qName.equalsIgnoreCase("school_id")) { 
      school_id = true; 
     } 
    if (qName.equalsIgnoreCase("student_id")) { 
      student_id = true; 
     } 
    if (qName.equalsIgnoreCase("school_name")) { 
      school_name = true; 
     } 
    if (qName.equalsIgnoreCase("student_name")) { 
      student_name = true; 
     } 
    } 

    @Override 
    public void endElement(String uri, String localName, 
      String qName) 
      throws SAXException { 
    } 

    @Override 
    public void characters(char ch[], int start, int length) 
      throws SAXException { 

     String data = new String(ch, start, length); 

    } 
} 
+0

Verificate anche: http://stackoverflow.com/questions/1863250/ - Ci sono alcuni progetti che consentono di utilizzare un sottoinsieme di XPath con un documento in streaming. Se riesci ad adattare il tuo problema a quel sottoinsieme, il codice risultante sarebbe di gran lunga preferibile a qualsiasi codice di gestore SAX laminato a mano. –

+0

L'analisi SAX con contesto è come una macchina a stati: https://en.wikipedia.org/wiki/Finite-state_machine, è necessario disporre di alcune bandiere che è possibile attivare/disattivare per sapere dove ci si trova, ma può rapidamente diventare molto caotico e dovresti considerare le alternative prima di andare troppo lontano. –

risposta

13

Beh, non ho giocato negli anni con SAX in Java, ecco il mio prendere su di esso:

package play.xml.sax; 

import org.xml.sax.Attributes; 
import org.xml.sax.SAXException; 
import org.xml.sax.helpers.DefaultHandler; 

import javax.xml.parsers.ParserConfigurationException; 
import javax.xml.parsers.SAXParser; 
import javax.xml.parsers.SAXParserFactory; 
import java.io.IOException; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.Stack; 

public class Test1 { 
    public static void main(String[] args) { 
     SAXParserFactory spf = SAXParserFactory.newInstance(); 
     SchoolsHandler handler = new SchoolsHandler(); 
     try { 
      SAXParser sp = spf.newSAXParser(); 
      sp.parse("schools.xml", handler); 
      System.out.println("Number of read schools: " + handler.getSchools().size()); 
     } catch (SAXException se) { 
      se.printStackTrace(); 
     } catch (ParserConfigurationException pce) { 
      pce.printStackTrace(); 
     } catch (IOException ie) { 
      ie.printStackTrace(); 
     } 
    } 
} 

class SchoolsHandler extends DefaultHandler { 
    private static final String TAG_SCHOOLS = "Schools"; 
    private static final String TAG_SCHOOL = "School"; 
    private static final String TAG_STUDENT = "Student"; 
    private static final String TAG_ID = "ID"; 
    private static final String TAG_NAME = "Name"; 

    private final Stack<String> tagsStack = new Stack<String>(); 
    private final StringBuilder tempVal = new StringBuilder(); 

    private List<School> schools; 
    private School school; 
    private Student student; 

    public void startElement(String uri, String localName, String qName, Attributes attributes) { 
     pushTag(qName); 
     tempVal.setLength(0); 
     if (TAG_SCHOOLS.equalsIgnoreCase(qName)) { 
      schools = new ArrayList<School>(); 
     } else if (TAG_SCHOOL.equalsIgnoreCase(qName)) { 
      school = new School(); 
     } else if (TAG_STUDENT.equalsIgnoreCase(qName)) { 
      student = new Student(); 
     } 
    } 

    public void characters(char ch[], int start, int length) { 
     tempVal.append(ch, start, length); 
    } 

    public void endElement(String uri, String localName, String qName) { 
     String tag = peekTag(); 
     if (!qName.equals(tag)) { 
      throw new InternalError(); 
     } 

     popTag(); 
     String parentTag = peekTag(); 

     if (TAG_ID.equalsIgnoreCase(tag)) { 
      int id = Integer.valueOf(tempVal.toString().trim()); 
      if (TAG_STUDENT.equalsIgnoreCase(parentTag)) { 
       student.setId(id); 
      } else if (TAG_SCHOOL.equalsIgnoreCase(parentTag)) { 
       school.setId(id); 
      } 
     } else if (TAG_NAME.equalsIgnoreCase(tag)) { 
      String name = tempVal.toString().trim(); 
      if (TAG_STUDENT.equalsIgnoreCase(parentTag)) { 
       student.setName(name); 
      } else if (TAG_SCHOOL.equalsIgnoreCase(parentTag)) { 
       school.setName(name); 
      } 
     } else if (TAG_STUDENT.equalsIgnoreCase(tag)) { 
      school.addStudent(student); 
     } else if (TAG_SCHOOL.equalsIgnoreCase(tag)) { 
      schools.add(school); 
     } 
    } 

    public void startDocument() { 
     pushTag(""); 
    } 

    public List<School> getSchools() { 
     return schools; 
    } 

    private void pushTag(String tag) { 
     tagsStack.push(tag); 
    } 

    private String popTag() { 
     return tagsStack.pop(); 
    } 

    private String peekTag() { 
     return tagsStack.peek(); 
    } 
} 

class School { 
    private int id; 
    private String name; 
    private List<Student> students = new ArrayList<Student>(); 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public int getId() { 
     return id; 
    } 

    public void setId(int id) { 
     this.id = id; 
    } 

    public void addStudent(Student student) { 
     students.add(student); 
    } 

    public List<Student> getStudents() { 
     return students; 
    } 
} 

class Student { 
    private int id; 
    private String name; 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public int getId() { 
     return id; 
    } 

    public void setId(int id) { 
     this.id = id; 
    } 
} 

schools.xml contiene il vostro esempio XML. Nota che ho riempito tutto in un unico file, ma questo è solo per il fatto che stavo solo giocando.

+0

http://www.javatpoint.com/questionanswer.jsp?thread=3474 – Rohit

13

In un parser SAX viene fornito ogni elemento nell'ordine del documento. Devi mantenere uno stack per tracciare il nesting (spingere sullo stack quando gestisci startElement e pop per endElement). Puoi differenziare i diversi elementi <Name> da ciò che è attualmente in pila.

In alternativa, tieni semplicemente una variabile che ti dice se hai riscontrato un tag <School> o un tag <Student> per indicare quale tipo di <Name> stai vedendo.

+4

+1 per tenere uno stack, questa è la strada da percorrere. è possibile generare una stringa simile a Xpath stampando il contenuto corrente dello stack.Usare le bandierine per dire di quale etichetta sei dentro è solo brutto. :-P –

2

Sì, capire xml utilizzando un parser SAX è generalmente un po 'più complicato rispetto al lavoro con DOM. in pratica, è necessario mantenere lo stato/contesto nel parser SAX in modo da poter distinguere tra queste situazioni.

nota, l'altra chiave per l'implementazione di un gestore SAX è la comprensione che i valori possono essere suddivisi in più eventi di carattere.

1

Sax è basato su eventi, tramite callback è possibile leggere il documento XML in modo seriale. Sax è adatto per la lettura di documenti XML di grandi dimensioni poiché l'intero documento non è caricato in memoria. Potresti voler dare un'occhiata a Xpath, ad es.

XPathFactory xPathFactory = XPathFactory.newInstance(); 
XPath xPath = xPathFactory.newXPath(); 
String expression = "/Schools/school/ ..."; 
XPathExpression xPathExpression = xPath.compile(expression); 
// Compile the expression to get a XPathExpression object. 
Object result = xPathExpression.evaluate(xmlDocument); 
+0

È possibile in SAX, come ben spiegato nella risposta di Jim Garrison. –

+1

@Don Roby, la correzione è possibile, ma davvero lo faresti in questo modo con SAX? Penso che usare SAX in questo modo sia eccessivamente complicato e possa essere raggiunto in modo più pulito con XPath (senza bandiere, senza stack), solo la mia opinione però – eon

+1

È più lavoro scrivere un'app SAX ma è perfettamente legittimo se hai bisogno di risparmiare sulla memoria che SAX offre e può permettersi lo sforzo di programmazione extra per raggiungerlo. Tuttavia, penso che tu abbia ragione a essere preoccupato: qualcuno che ha bisogno di porre la domanda posta in questa discussione avrà molte difficoltà a lavorare con SAX. –

0
private boolean isInStudentNode; 
...................................................  

public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 
    // enter node Student 
    if(qName.equalEgnoreCase("Student"){ 
     isInStudentNode = true; 
    } 
    ... 
} 

public void endElement(String uri, String localName, String qName) throws SAXException { 
    // end node Student 
    if(qName.equalEgnoreCase("Student"){ 
     isInStudentNode = false; 
     ........... 
    } 

    // end node Name (school|student) 
    if(qName.equalEgnoreCase("Name"){ 
     if(isInStudentNode) student.setName(...); 
     else school.setName(...); 
    } 
} 

il suo lavoro con me