it-roy-ru.com

Как разобрать XML с помощью SAX-парсера

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

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

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

58
Johan

Итак, вы хотите создать синтаксический анализатор 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 Реализация

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

179
Octavian Damiean

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

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

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

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

2
Sergey Shustikov
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;
        }
    }
}
0
vimal singh