четверг, 8 декабря 2011 г.

Работа с XML в JAVA

XML - расширенный язык разметки, как говорит википедия. Используется он сейчас практически везде: и в качестве конфигурационных файлов, и для импорта/экспорта и для приема/передачи. Всё это весело и удобно.

Итак, ниже кое что из того, что я ноковырял...

для работы с XML я использовал следующие пакеты:
import javax.xml.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
Открываем файл ():
File xmlfile = new File("Sites.xml");

Проверка документа

На этом этапе мы можем проверить xml файл на соответствие XML Schema(xsd), если такая предусмотрена (про xsd можно почитать тут, тут и тут. Если коротко, то это своеобразный шаблон xml-документа). Да и вообще, на правильность синтаксиса.
//создаем фабрику
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");

//открываем файл со схемой, инициализируем схему, инициализируем валидатор 
File loc = new File("Site.xsd"); 
Schema schema = factory.newSchema(loc);
Validator validator = schema.newValidator();

//специфический объект для хранения проверяемого xml 
Source source = new StreamSource(xmlfile);
      
//непосредственно проверка
try {
     validator.validate(source);
     System.out.println(xml + " is valid.");
}
catch (SAXException ex) {
     System.out.println(xml + " is not valid because ");
     System.out.println(ex.getMessage());
}

Подготовка


Продолжаем. создаем объект в котором будет храниться наш распарсеный xml-документ:
Document doc = null;
Создаем и инициализируем фабрику для парсинга документа:
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
DocumentBuilder db = f.newDocumentBuilder();
Разбираем документ и записываем результат в doc
try {
   doc = db.parse(file);
} catch (IOException e) {
   System.out.println("Error!");
   return;
}

теперь мы можем работать с нашим документом.

Получение данных


Работая с объектами Document, мы работаем с DOM. XML-документ в этих объектах представлен в виде дерева, которое обходится рекурсивной функцией. Ниже приведен пример работы. В нем осуществляется обход XML и инициализируется массив объектов.

Анализируемый XML:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<p:Sites xmlns:p="http://www.example.org/Site" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/Site Site.xsd ">
  <Site>
      <Name>Первый сайт</Name>
    <Type>Рекламный</Type>
    <Chars>
      <Email>true</Email>
      <News>true</News>
      <Arch>true</Arch>
      <Votes>анонимно</Votes>
      <Free>true</Free>
    </Chars>
    <Authorize>true</Authorize>
  </Site>
  <Site>
      <Name>Второй сайт</Name>
      <Type>страница новостей</Type>
      <Chars>
          <Email>true</Email>
          <News>true</News>
          <Arch>true</Arch>
          <Votes>анонимно</Votes>
          <Free>true</Free>
      </Chars>
      <Authorize>true</Authorize>
  </Site>
</p:Sites>
А теперь пример обработки:
    static Site tempsite = new Site();
    public static void initDom(Node node,String parent) // Node - узел дерева
    {
         //анализируем тип узла
         int type = node.getNodeType(); // 
         switch (type)
         {
             
              case Node.DOCUMENT_NODE:  // фактически - корень документа
                {
                  
                  initDom(((Document)node).getDocumentElement(),"");  
                  break;
                }

             case Node.ELEMENT_NODE:  //элемент
                {
                 
                  //для данного примера "Site" - основной элемент, сколько таких элементов в XML, столько объектов в нем описано  
                  if (node.getNodeName()=="Site") { 
                      sitescount++;
                      tempsite = new Site();
                  }

                 
                  //если у элемента есть вложенные элементы, обходим по всем 
                  if (node.hasChildNodes())
                  {
                    NodeList children = node.getChildNodes();
                    for (int i = 0; i < children.getLength(); i++)
                        initDom(children.item(i),node.getNodeName());
                  }
                
                //предыдущий заполнен и может быть добавлен в массив
                if (node.getNodeName()=="Site") {
                        sites.add( tempsite);
                   
                  }
                  break;
                }

               //Значения атрибутов. parant - имя атрибута
              case Node.TEXT_NODE: 
                {
                  if (parent=="Name") {
                      tempsite.setName(node.getNodeValue()); //получаем значение
                  }
                  if (parent=="Authorize") {
                      tempsite.setAuthorize(Boolean.getBoolean(node.getNodeValue()));
                  }
                  
                  if (parent=="Email") {
                      tempsite.getChars().email=Boolean.getBoolean(node.getNodeValue());
                  }
                  if (parent=="Arch") {
                      tempsite.getChars().arch=Boolean.getBoolean(node.getNodeValue());
                  }
                  if (parent=="News") {
                      tempsite.getChars().news=Boolean.getBoolean(node.getNodeValue());
                  }
                  if (parent=="Free") {
                      tempsite.getChars().free=Boolean.getBoolean(node.getNodeValue());
                  }
                  if (parent=="Type") {
                      tempsite.setType(node.getNodeValue());
                  }
                  if (parent=="Votes") {
                      tempsite.getChars().vote=node.getNodeValue();
                  }
                    
                  break;
                }

            }

    }
Структуру объекта Site я не привожу, ибо она проста и понятна из выше приведенного кода. Главное - понять алгоритм обработки, показанный в примере.

Добавление элементов

Допустим, мы хотим добавить в хмл, приведенный выше, следующее:
<Site>
<Name>New site</Name>
</Site>
Первым делом создаем объект, являющийся корневым узлом нашего xml-дерева
Element root = doc.getDocumentElement();
Затем создаем необходимые элементы:
Element elem = doc.createElement("Site");
Element elem2 = doc.createElement("Name");
Создаем "текстовый узел". Т.е. по сути - значение. То, что находится между тегами
Text value = doc.createTextNode("New site"); 
Теперь нам надо задать подчиненность (что из описанных элементов основное, а что внутри).
elem2.appendChild(value);
elem.appendChild(elem2);
И все это должно быть внутри корневого элемента:
root.appendChild(elem);
И последним шагом мы должны переписать физический xml-файл. Без этого мы сможем работать с документом, но как только приложение завершит работу, все изменения будут потеряны.
TransformerFactory tranformerFactory = TransformerFactory.newInstance();
Transformer tr = tranformerFactory.newTransformer();   
tr.setOutputProperty(OutputKeys.INDENT, "yes");
tr.transform(new DOMSource(doc), new StreamResult(new File("Sites.xml")));
Пока всё.