Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

Итак, вы хотите создать парсер 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. Я собираюсь объяснить pro и con как после публикации короткого примера.

реализация 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 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, а просто слушаете события. Наиболее широко используются следующие события:

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

Ответ 2

Во многих проблемах необходимо использовать разные типы 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's. Для начала подключите необходимые пакеты.

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

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

public class SAXPars extends DefaultHandler {
   ... 
} 

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

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

Приступая к созданию всего документа, мы подошли к концу файла. 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); 

Ответ 3

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;
        }
    }
}