11 января 2008

Парсинг XML с помощью DOM

Пусть у нас имеется xml файл в котором хранится список студентов.
<students>
<student>
  <name>Nikolaj</name>
  <surname>Ivanov</surname>
  <age>23</age>
  <group>PF-11</group>
</student>
<student>
  <name>Petr</name>
  <surname>Kilkin</surname>
  <age>22</age>
  <group>FP-22</group>
</student>
</students>
перед нами стоит задача извлечь данные о студентах в список (например ArrayList) для дальнейшей обработки.
Первое что сделаем - напишем класс, который будет описывать студента:
class Student{
private String name="";
private String surname="";
private int age="";
private String group="";
// самостоятельно добавьте конструктор, а также
// getXXX() и setXXX() методы для каждого параметра
}

Теперь переходим к написанию парсера. Основной идеей Dom парсера есть перебор по дереву всех элементов хмл файла. Наш класс - парсер будет читать хмл файл и выбирать все необходимые данные оттуда. Сразу приведу пример такого класса:


//Dom Parser
import java.util.*;
import java.io.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;

public class DomStudParser
{
private Document doc = null;
private String txt = "";
private Student tmpS = null;
private List stds = null;

// fName - имя хмл файла
public DomHostsParser(String fName)
{
  try
  {
   doc = parserXML(new File(fName));
   stds = new ArrayList();
   visit(doc, 0);
   Host tmp=null;
  }
  catch(Exception error)
  {
   error.printStackTrace();
  }
}

public void visit(Node node, int level)
{
  NodeList nl = node.getChildNodes();  
  String parent="";
  for(int i=0, cnt=nl.getLength(); i<cnt; i++)
  {  
   if (nl.item(i).getNodeType()==Node.TEXT_NODE){ // if1
    parent=nl.item(i).getParentNode().getNodeName();
    txt=nl.item(i).getNodeValue();
    if (parent=="name"){ // if1.1
     tmpS.setName(txt);
    }
    if (parent=="surname"){ // if1.2
     tmpS.setSurname(txt);
    }
    if (parent=="age"){  // if1.3
     tmpS.setAge(Integer.valueOf(txt));
    }
    if (parent=="group"){
     tmpS.setGroup(txt);
    }
   } else {
    if (nl.item(i).getNodeName().equals("student")){
     tmpS=new Student();
     stds.add(tmpS);
    }
   }
   System.out.println(nl.item(i).getNodeName() + " = " + nl.item(i).getNodeValue());
   visit(nl.item(i), level+1);
  }
}
public Document parserXML(File file) throws SAXException, IOException, ParserConfigurationException
{
  return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
}
public List getStds() {
  return stds;
}
public void setStds(List stds) {
  this.stds = stds;
}
}

Итак немного объяснений.
Функция parserXML преобразовывает наш хмл файл в хмл документ, над которым может проводить работу.
Основной в данном классе - рекурсивно вызываемый метод visit, который по дереву (поиск в глубину) перебирает все узлы нашего хмл елемента.
Узлы бывают нескольких типов: название атрибута, название элемента, значение элемента, коментарий и т.д.
В нашем случае узлы только 2х типов - это TEXT_NODE (значение элемента) и ELEMENT_NODE (название элемента). Парсер начинает просмотр с элемента "<students>" , далее обработке подвергнется "текст", который находится между тегами <students> и <student> , далее элемент <student> , при достижении которого сработает условие "else if" и будет создан новый экземпляр класса Student, который мы начнём заполнять. Когда дойдём до текста после элемента <name> - "Nikolaj" , сработает условие if1.1.Это произойдт потому что родителем для данного текста есть элемент name и т.д. мы переберём все элементы и в итоге получим List с двумя объектами Student внутри, получить этот список можна с помощью функции getStds().

подробнее:
http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/package-summary.html

4 комментария:

Анонимный комментирует...
Этот комментарий был удален администратором блога.
Funt комментирует...

private int age="";
как то не логично числу строку присваивать!

Rumoku комментирует...

спасибо. код писал на автомате не компилируя.

Demoth комментирует...

как итерироваться то по NodeListу? чтоб без вот этих индексов