Как разобрать XML с помощью SAX-парсера - PullRequest
58 голосов
/ 28 января 2011

Я следую этому учебнику .

Это прекрасно работает, но я бы хотел вернуть массив со всеми строками вместо одной строки с последним элементом.

Есть идеи, как это сделать?

Ответы [ 3 ]

179 голосов
/ 28 января 2011

Итак, вы хотите создать синтаксический анализатор 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 (обычными старыми объектами Java), в которых будут храниться вашиданные.Вот необходимые 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-структуру, а просто прослушиваете события.Наиболее широко используются следующие события:

  • Начало документа
  • Конец документа
  • Начало элемента
  • Конец элемента
  • Символы между началом и концом элемента

Пример реализации обработчика с использованием объекта 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, например, чтобы установить ваше текущее состояние, а также множество переключателей / регистров состояний, чтобы проверить, где вы находитесь, или более элегантным решением будет какой-то трекер тегов, использующий стек тегов.

2 голосов
/ 24 октября 2014

Во многих задачах необходимо использовать разные типы 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 view из-за того, что он работает ярче, и он первым попал мне в руки :)

Итак.Как вы знаете, для успешной работы с парсером нам нужно переопределить необходимые методы 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'ом и бросить его в какую-нибудь служебную строку thisElement.Таким образом мы отмечаем, в каком элементе в данный момент мы находимся.

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

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

@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 = ""; 
} 

Получив весь документ, мы подходим к концу файла.Работа 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); 

0 голосов
/ 18 января 2019
public class MainActivity extends AppCompatActivity {
   ListView lvPcsPost;
    ArrayList<String> name;
    ArrayList<String> price;
    ArrayList<String> Description;
    LayoutInflater layoutInflater;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        lvPcsPost = (ListView) findViewById(R.id.lvPcsPost);
        name = new ArrayList<>();
        price = new ArrayList<>();
        Description = new ArrayList<>();
        new PostAsync().execute();
    }

    class PostAsync extends AsyncTask<Void, Void, Void> {
        ProgressDialog pd;
        XMLHelper helper;


        @Override
        protected void onPreExecute() {
            pd = ProgressDialog.show(MainActivity.this, "", "Loading...", true, false);
        }

        @Override
        protected Void doInBackground(Void... arg0) {
            helper = new XMLHelper();
            helper.get();
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            PostBaseAdapter postBaseAdapter = new PostBaseAdapter();
            lvPcsPost.setAdapter(postBaseAdapter);
            pd.dismiss();
        }

    }

    public class XMLHelper extends DefaultHandler {

        private String URL_MAIN = "http://uat.winitsoftware.com/ThemeManager/Data/Products/Products.xml";
        String TAG = "XMLHelper";

        Boolean currTag = false;
        String currTagVal = "";

        public void get() {
            try {
                SAXParserFactory factory = SAXParserFactory.newInstance();
                SAXParser mSaxParser = factory.newSAXParser();
                XMLReader mXmlReader = mSaxParser.getXMLReader();
                mXmlReader.setContentHandler(this);
                InputStream mInputStream = new URL(URL_MAIN).openStream();
                mXmlReader.parse(new InputSource(mInputStream));
            } catch (Exception e) {
                Log.e(TAG, "Exception: " + e.getMessage());
            }
        }

        @Override
        public void characters(char[] ch, int start, int length)
                throws SAXException {
            if (currTag) {
                currTagVal = currTagVal + new String(ch, start, length);
                currTag = false;
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName)
                throws SAXException {
            currTag = false;

            if (localName.equalsIgnoreCase("Name"))
                name.add(currTagVal);

            else if (localName.equalsIgnoreCase("Description"))
             Description.add(currTagVal);

            else if (localName.equalsIgnoreCase("Price"))
              price.add(currTagVal);

        }
        @Override
        public void startElement(String uri, String localName, String qName,
                                 Attributes attributes) throws SAXException {
            Log.i(TAG, "TAG: " + localName);

            currTag = true;
            currTagVal = "";
            if (localName.equals("Products"));
        }
    }

    public class PostBaseAdapter extends BaseAdapter {

        public PostBaseAdapter() {

        }

        @Override
        public int getCount() {
            return name.size();
        }

        @Override
        public Object getItem(int position) {
            return name.get(position);
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            layoutInflater = LayoutInflater.from(getApplicationContext());

            convertView = layoutInflater.inflate(R.layout.list_item_post, parent, false);
            TextView  txtPrice = (TextView) convertView.findViewById(R.id.txtPrice);
            TextView  txtDescription = (TextView) convertView.findViewById(R.id.txtDescription);
            TextView   txtName = (TextView) convertView.findViewById(R.id.txtName);
            ImageView   image = (ImageView) convertView.findViewById(R.id.Image);
            ImageView  bigImage = (ImageView) convertView.findViewById(R.id.BigImage);

                txtPrice.setText("Price : "+price.get(position));
                 txtDescription.setText("Description : "+Description.get(position));
                txtName.setText("Name : "+name.get(position));

            return convertView;
        }
    }
}
...