Я следую этому учебнику.
Он отлично работает, но мне бы хотелось, чтобы он возвращал массив со всеми строками вместо одной строки с последним элементом.
Любые идеи, как это сделать?
Я следую этому учебнику.
Он отлично работает, но мне бы хотелось, чтобы он возвращал массив со всеми строками вместо одной строки с последним элементом.
Любые идеи, как это сделать?
Итак, вы хотите создать парсер 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, а просто слушаете события. Наиболее широко используются следующие события:
Пример реализации обработчика с использованием объекта 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, чтобы проверить, где вы находитесь, или более элегантным решением будет какой-то тэг-трекер с использованием стека тегов.
Во многих проблемах необходимо использовать разные типы 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);
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;
}
}
}