Как анализировать XML с помощью анализатора SAX

Я следую этому руководству .

Он отлично работает, но мне бы хотелось, чтобы он возвращал массив со всеми строками вместо одной строки с последним элементом.

Любые идеи, как это сделать?

Поэтому вы хотите создать синтаксический анализатор XML для синтаксического анализа RSS-канала, подобного этому.

<rss version="0.92"> <channel> <title>MyTitle</title> <link>http://myurl.com</link> <description>MyDescription</description> <lastBuildDate>SomeDate</lastBuildDate> <docs>http://someurl.com</docs> <language>SomeLanguage</language> <item> <title>TitleOne</title> <description><![CDATA[Some text.]]></description> <link>http://linktoarticle.com</link> </item> <item> <title>TitleTwo</title> <description><![CDATA[Some other text.]]></description> <link>http://linktoanotherarticle.com</link> </item> </channel> </rss> 

Теперь у вас есть две реализации SAX, с которыми вы можете работать. Либо вы используете реализацию org.xml.sax либо android.sax . Я собираюсь объяснить, как профессионалы, так и разработчики обоих после публикации короткого примера.

Внедрение android.sax

Начнем с реализации android.sax .

Сначала вам нужно определить структуру XML, используя объекты RootElement и Element .

В любом случае я буду работать с POJO (Plain Old Java Objects), который будет хранить ваши данные. Здесь нужны POJO.

Channel.java

 public class Channel implements Serializable { private Items items; private String title; private String link; private String description; private String lastBuildDate; private String docs; private String language; public Channel() { setItems(null); setTitle(null); // set every field to null in the constructor } public void setItems(Items items) { this.items = items; } public Items getItems() { return items; } public void setTitle(String title) { this.title = title; } public String getTitle() { return title; } // rest of the class looks similar so just setters and getters } 

Этот класс реализует интерфейс Serializable поэтому вы можете поместить его в Bundle и сделать с ним что-то.

Теперь нам нужен класс для хранения наших предметов. В этом случае я просто собираюсь расширить класс ArrayList .

Items.java

 public class Items extends ArrayList<Item> { public Items() { super(); } } 

Это для нашего контейнера товаров. Теперь нам нужен класс для хранения данных каждого отдельного элемента.

Item.java

 public class Item implements Serializable { private String title; private String description; private String link; public Item() { setTitle(null); setDescription(null); setLink(null); } public void setTitle(String title) { this.title = title; } public String getTitle() { return title; } // same as above. } 

Пример:

 public class Example extends DefaultHandler { private Channel channel; private Items items; private Item item; public Example() { items = new Items(); } public Channel parse(InputStream is) { RootElement root = new RootElement("rss"); Element chanElement = root.getChild("channel"); Element chanTitle = chanElement.getChild("title"); Element chanLink = chanElement.getChild("link"); Element chanDescription = chanElement.getChild("description"); Element chanLastBuildDate = chanElement.getChild("lastBuildDate"); Element chanDocs = chanElement.getChild("docs"); Element chanLanguage = chanElement.getChild("language"); Element chanItem = chanElement.getChild("item"); Element itemTitle = chanItem.getChild("title"); Element itemDescription = chanItem.getChild("description"); Element itemLink = chanItem.getChild("link"); chanElement.setStartElementListener(new StartElementListener() { public void start(Attributes attributes) { channel = new Channel(); } }); // Listen for the end of a text element and set the text as our // channel's title. chanTitle.setEndTextElementListener(new EndTextElementListener() { public void end(String body) { channel.setTitle(body); } }); // Same thing happens for the other elements of channel ex. // On every <item> tag occurrence we create a new Item object. chanItem.setStartElementListener(new StartElementListener() { public void start(Attributes attributes) { item = new Item(); } }); // On every </item> tag occurrence we add the current Item object // to the Items container. chanItem.setEndElementListener(new EndElementListener() { public void end() { items.add(item); } }); itemTitle.setEndTextElementListener(new EndTextElementListener() { public void end(String body) { item.setTitle(body); } }); // and so on // here we actually parse the InputStream and return the resulting // Channel object. try { Xml.parse(is, Xml.Encoding.UTF_8, root.getContentHandler()); return channel; } catch (SAXException e) { // handle the exception } catch (IOException e) { // handle the exception } return null; } } 

Теперь это был очень быстрый пример, как вы можете видеть. Основным преимуществом использования реализации android.sax SAX является то, что вы можете определить структуру XML, которую вы должны проанализировать, а затем просто добавить прослушиватель событий к соответствующим элементам. Недостатком является то, что код становится довольно повторяющимся и раздутым.

Реализация org.xml.sax

Реализация обработчика org.xml.sax SAX немного отличается.

Здесь вы не указываете или не объявляете структуру XML, а просто слушаете события. Наиболее широко используются следующие события:

  • Начало документа
  • Окончание документа
  • Начало элемента
  • Элементный конец
  • Символы между Element Start и Element End

Пример реализации обработчика с использованием объекта Channel выше выглядит следующим образом.

пример

 public class ExampleHandler extends DefaultHandler { private Channel channel; private Items items; private Item item; private boolean inItem = false; private StringBuilder content; public ExampleHandler() { items = new Items(); content = new StringBuilder(); } public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { content = new StringBuilder(); if(localName.equalsIgnoreCase("channel")) { channel = new Channel(); } else if(localName.equalsIgnoreCase("item")) { inItem = true; item = new Item(); } } public void endElement(String uri, String localName, String qName) throws SAXException { if(localName.equalsIgnoreCase("title")) { if(inItem) { item.setTitle(content.toString()); } else { channel.setTitle(content.toString()); } } else if(localName.equalsIgnoreCase("link")) { if(inItem) { item.setLink(content.toString()); } else { channel.setLink(content.toString()); } } else if(localName.equalsIgnoreCase("description")) { if(inItem) { item.setDescription(content.toString()); } else { channel.setDescription(content.toString()); } } else if(localName.equalsIgnoreCase("lastBuildDate")) { channel.setLastBuildDate(content.toString()); } else if(localName.equalsIgnoreCase("docs")) { channel.setDocs(content.toString()); } else if(localName.equalsIgnoreCase("language")) { channel.setLanguage(content.toString()); } else if(localName.equalsIgnoreCase("item")) { inItem = false; items.add(item); } else if(localName.equalsIgnoreCase("channel")) { channel.setItems(items); } } public void characters(char[] ch, int start, int length) throws SAXException { content.append(ch, start, length); } public void endDocument() throws SAXException { // you can do something here for example send // the Channel object somewhere or whatever. } } 

Теперь, честно говоря, я не могу сказать вам реальных преимуществ реализации этого обработчика над android.sax . Тем не менее, я могу сказать вам о недостатке, который должен быть довольно очевидным. Взгляните на инструкцию else if в методе startElement . Из-за того, что у нас есть теги <title> , link и description мы должны отслеживать там в структуре XML, на которой мы сейчас находимся. То есть, если мы встретим начальный тег <item> мы установили флаг inItem в true чтобы убедиться, что мы сопоставляем правильные данные с правильным объектом, а в методе endElement мы устанавливаем этот флаг как false если мы сталкиваемся с </item> , Чтобы сигнализировать, что мы закончили с этим тегом элемента.

В этом примере довольно легко справиться с этим, но для анализа сложной структуры с повторяющимися тегами на разных уровнях становится сложно. Там вам нужно будет либо использовать Enums, например, чтобы установить текущее состояние и множество параметров переключателя / случая, чтобы проверить, где вы находитесь, или более элегантное решение – это какой-то тэг-трекер, использующий стек тегов.

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

Java, возможно, мой любимый язык программирования. Кроме того, эта любовь усиливается тем фактом, что вы можете решить любую проблему и придумать велосипед не требуется.

Таким образом, мне потребовалось создать кучу клиент-сервера, на котором была запущена база данных, которая позволяла бы удаленному клиенту удалять записи на сервере базы данных. Излишне проверять входные данные и т. Д. И т. Д., Но дело не в этом.

В качестве принципа работы я без колебаний выбрал передачу информации в виде XML-файла. Из следующих типов:

 <? xml version = "1.0" encoding = "UTF-8" standalone = "no"?> <doc> <id> 3 </ id> <fam> Ivanov </ fam> <name> Ivan </ name> <otc> I. </ otc> <dateb> 10-03-2005 </ dateb> <datep> 10-03-2005 </ datep> <datev> 10-03-2005 </ datev> <datebegin> 09-06-2009 </ datebegin> <dateend> 10-03-2005 </ dateend> <vdolid> 1 </ vdolid> <specid> 1 </ specid> <klavid> 1 </ klavid> <stav> 2.0 </ stav> <progid> 1 </ progid> </ doc> 

Упростите читать дальше, кроме как сказать, что это информация о медицинских учреждениях. Фамилия, имя, уникальный идентификатор и т. Д. В общем, ряд данных. Этот файл безопасно попал на сервер, а затем начал разбор файла.

Из двух вариантов синтаксического анализа (SAX vs DOM) я выбрал SAX-представление о том, что он работает более ярким, и он был первым, кто попал в руки 🙂

Так. Как вы знаете, для успешной работы с парсером нам необходимо переопределить необходимые методы DefaultHandler. Для начала подключите необходимые пакеты.

 import org.xml.sax.helpers.DefaultHandler; import org.xml.sax. *; 

Теперь мы можем начать писать наш синтаксический анализатор

 public class SAXPars extends DefaultHandler {  ... } 

Начнем с метода startDocument (). Он, как следует из названия, реагирует на начало события документа. Здесь вы можете повесить различные действия, такие как выделение памяти, или сбросить значения, но наш пример довольно прост, поэтому просто отметьте начало работы соответствующего сообщения:

 Override public void startDocument () throws SAXException {  System.out.println ("Start parse XML ..."); } 

Следующий. Парсер проходит через документ, соответствующий элементу его структуры. Запускает метод startElement (). И на самом деле его внешний вид: startElement (String namespaceURI, String localName, String qName, Attributes atts). Здесь namespaceURI – пространство имен, localName – локальное имя элемента, qName – комбинация локального имени с пространством имен (разделенная двоеточием) и atts – атрибуты этого элемента. В этом случае все просто. Достаточно использовать qName'om и выбросить его в некоторую служебную линию thisElement. Таким образом, мы отмечаем, в каком элементе в данный момент мы находимся.

 @Override public void startElement (String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {  thisElement = qName; } 

Затем, пункт собрания, мы получим его значение. Сюда входят методы characters (). Он имеет форму: characters (char [] ch, int start, int length). Ну вот все понятно. Ch – файл, содержащий собственно значение строки в этом элементе. Начало и длина – номер службы, указывающий начальную точку в строке и длине.

 @Override public void characters (char [] ch, int start, int length) throws SAXException {  if (thisElement.equals ("id")) {     doc.setId (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("fam")) {     doc.setFam (new String (ch, start, length));  }  if (thisElement.equals ("name")) {     doc.setName (new String (ch, start, length));  }  if (thisElement.equals ("otc")) {     doc.setOtc (new String (ch, start, length));  }  if (thisElement.equals ("dateb")) {     doc.setDateb (new String (ch, start, length));  }  if (thisElement.equals ("datep")) {     doc.setDatep (new String (ch, start, length));  }  if (thisElement.equals ("datev")) {     doc.setDatev (new String (ch, start, length));  }  if (thisElement.equals ("datebegin")) {     doc.setDatebegin (new String (ch, start, length));  }  if (thisElement.equals ("dateend")) {     doc.setDateend (new String (ch, start, length));  }  if (thisElement.equals ("vdolid")) {     doc.setVdolid (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("specid")) {     doc.setSpecid (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("klavid")) {     doc.setKlavid (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("stav")) {     doc.setStav (new Float (new String (ch, start, length)));  }  if (thisElement.equals ("progid")) {     doc.setProgid (new Integer (new String (ch, start, length)));  } } 

О да. Я почти забыл. В качестве объекта которого будут складываться напарные данные, относится к типу Докторов. Этот класс определен и имеет все необходимые сеттер-получатели.

Следующий очевидный элемент заканчивается, и за ним следует следующий. Ответственный за прекращение endElement (). Это сигнализирует нам, что элемент закончился, и вы можете сделать что-нибудь в это время. Будет продолжаться. Очистить элемент.

 @Override public void endElement (String namespaceURI, String localName, String qName) throws SAXException {  thisElement = ""; } 

Итак, весь документ, мы подошли к концу файла. Work endDocument (). В нем мы можем освободить память, сделать некоторую диагностическую печать и т. Д. В нашем случае просто напишите о том, что заканчивается синтаксический анализ.

 @Override public void endDocument () {  System.out.println ("Stop parse XML ..."); } 

Итак, мы получили класс для анализа XML-формата. Вот полный текст:

 import org.xml.sax.helpers.DefaultHandler; import org.xml.sax. *; public class SAXPars extends DefaultHandler { Doctors doc = new Doctors (); String thisElement = ""; public Doctors getResult () {  return doc; } @Override public void startDocument () throws SAXException {  System.out.println ("Start parse XML ..."); } @Override public void startElement (String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {  thisElement = qName; } @Override public void endElement (String namespaceURI, String localName, String qName) throws SAXException {  thisElement = ""; } @Override public void characters (char [] ch, int start, int length) throws SAXException {  if (thisElement.equals ("id")) {     doc.setId (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("fam")) {     doc.setFam (new String (ch, start, length));  }  if (thisElement.equals ("name")) {     doc.setName (new String (ch, start, length));  }  if (thisElement.equals ("otc")) {     doc.setOtc (new String (ch, start, length));  }  if (thisElement.equals ("dateb")) {     doc.setDateb (new String (ch, start, length));  }  if (thisElement.equals ("datep")) {     doc.setDatep (new String (ch, start, length));  }  if (thisElement.equals ("datev")) {     doc.setDatev (new String (ch, start, length));  }  if (thisElement.equals ("datebegin")) {     doc.setDatebegin (new String (ch, start, length));  }  if (thisElement.equals ("dateend")) {     doc.setDateend (new String (ch, start, length));  }  if (thisElement.equals ("vdolid")) {     doc.setVdolid (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("specid")) {     doc.setSpecid (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("klavid")) {     doc.setKlavid (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("stav")) {     doc.setStav (new Float (new String (ch, start, length)));  }  if (thisElement.equals ("progid")) {     doc.setProgid (new Integer (new String (ch, start, length)));  } } @Override public void endDocument () {  System.out.println ("Stop parse XML ..."); } } 

Надеюсь, эта тема помогла легко представить суть анализатора SAX.

Не судите строго в первую статью 🙂 Я надеюсь, что это был хотя бы кто-то полезный.

UPD: чтобы запустить этот синтаксический анализатор, вы можете использовать этот код:

 SAXParserFactory factory = SAXParserFactory.newInstance (); SAXParser parser = factory.newSAXParser (); SAXPars saxp = new SAXPars (); parser.parse (new File ("..."), saxp);