I am building an RSS feed parser using XmlPullParser in Android Studio. I would like to display the feed images in my app, however I am having trouble retrieving the url attribute value in the <media:thumbnail> tag. As a note: I am only retrieving things inside <item>. After doing considerable research, I learned it has something to do with XML namespaces. I setNamespaceAware(true), with no luck. All my other elements are being retrieved properly, but I keep getting null for my imageLink variable. I have also tried setting
else if (name.equalsIgnoreCase("media:thumbnail")) {
imageLink = xmlPullParser.getAttributeValue(null, "url");
}
to "media:thumbnail" or "thumbnail".
Link to the RSS feed: https://www.goal.com/feeds/en/news
A snippet of the XML code:
<item>
<title>
<![CDATA[Mexico boss Martino responds after USMNT's Berhalter calls on referee to take control of Gold Cup final]]>
</title>
<pubDate>Sat, 31 Jul 2021 20:30:03 GMT</pubDate>
<link>
<![CDATA[https://www.goal.com/en/news/mexico-boss-martino-responds-after-usmnts-berhalter-calls-on/1h5obazjxkl3c1hrevc53ovtfy]]>
</link>
<guid isPermaLink="false">urn:perform:article:1h5obazjxkl3c1hrevc53ovtfy</guid>
<description>
<![CDATA[The two managers went back and forth when discussing each team's ability to manipulate the match official]]>
</description>
<category>#Geo; Everywhere</category>
<category>#Targeting</category>
<category>#Google Newsstand</category>
<category>#Apple News</category>
<category>#News2021</category>
<category>#News - original</category>
<category>#Manager/coach quotes</category>
<media:content url="http://images.daznservices.com/di/library/GOAL/36/54/martino-berhalter-split_1bbaicsveobrw1xcg0fsjk04ra.png?t=43319131" type="image/jpeg" />
<media:thumbnail url="http://images.daznservices.com/di/library/GOAL/36/54/martino-berhalter-split_1bbaicsveobrw1xcg0fsjk04ra.png?t=43319131" />
<media:content url="http://images.daznservices.com/di/library/GOAL/36/54/martino-berhalter-split_1bbaicsveobrw1xcg0fsjk04ra.png?t=43319131" type="image/jpeg" />
<media:thumbnail url="http://images.daznservices.com/di/library/GOAL/36/54/martino-berhalter-split_1bbaicsveobrw1xcg0fsjk04ra.png?t=43319131" />
<media:content url="http://images.daznservices.com/di/library/GOAL/36/54/martino-berhalter-split_1bbaicsveobrw1xcg0fsjk04ra.png?t=43319131" type="image/jpeg" />
<media:thumbnail url="http://images.daznservices.com/di/library/GOAL/36/54/martino-berhalter-split_1bbaicsveobrw1xcg0fsjk04ra.png?t=43319131" />
<media:content url="http://images.daznservices.com/di/library/GOAL/36/54/martino-berhalter-split_1bbaicsveobrw1xcg0fsjk04ra.png?t=43319131" type="image/jpeg" />
<media:thumbnail url="http://images.daznservices.com/di/library/GOAL/36/54/martino-berhalter-split_1bbaicsveobrw1xcg0fsjk04ra.png?t=43319131" />
<df:language>en</df:language>
<dc:language>en</dc:language>
<df:authors/>
<dc:creator/>
</item>
This is my code
public List<RssFeedModel> parseFeed(InputStream inputStream) throws XmlPullParserException, IOException {
String title = null;
String link = null;
String description = null;
String imageLink = null;
boolean isItem = false;
List<RssFeedModel> items = new ArrayList<>();
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(inputStream, "UTF-8");
while (xmlPullParser.next() != XmlPullParser.END_DOCUMENT) {
int eventType = xmlPullParser.getEventType();
String name = xmlPullParser.getName();
if (name == null)
continue;
if (eventType == XmlPullParser.END_TAG) {
if (name.equalsIgnoreCase("item")) {
isItem = false;
}
continue;
}
if (eventType == XmlPullParser.START_TAG) {
if (name.equalsIgnoreCase("item")) {
isItem = true;
continue;
}
}
Log.d("MyXmlParser", "Parsing name ==> " + name);
String result = "";
if (xmlPullParser.next() == XmlPullParser.TEXT) {
result = xmlPullParser.getText();
xmlPullParser.nextTag();
}
if (name.equalsIgnoreCase("title")) {
title = result;
} else if (name.equalsIgnoreCase("link")) {
link = result;
} else if (name.equalsIgnoreCase("description")) {
description = result;
} else if (name.equalsIgnoreCase("thumbnail")) {//for image
imageLink = xmlPullParser.getAttributeValue(null, "url");
}
if (title != null && link != null && description != null) {
if (isItem) {
RssFeedModel item = new RssFeedModel(title, link, description, imageLink);
items.add(item);
} else {
mFeedTitle = title;
mFeedLink = link;
mFeedDescription = description;
}
}
}
return items;
} finally {
inputStream.close();
}
}
Just answering my own question for future reference:
public List<RssFeedModel> parseFeed(InputStream inputStream) throws XmlPullParserException, IOException {
/**
* Declares the variables to hold article title, link and description
* Declares List to hold all items once parsed
*/
String title = null;
String link = null;
String description = null;
String imageLink = null;
List<RssFeedModel> items = new ArrayList<>();
/**Logic for parsing through the XML*/
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput(inputStream, null);
/**Retrieves eventType (START_DOCUMENT, START_TAG, END_DOCUMENT)*/
int eventType = xpp.getEventType();
/**Only checks items that are within the <item></item> tags*/
boolean insideItem = false;
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("item")) {
insideItem = true;
} else if (xpp.getName().equalsIgnoreCase("title")) {
if (insideItem) {
title = xpp.nextText();
}
} else if (xpp.getName().equalsIgnoreCase("description")) {
if (insideItem) {
description = xpp.nextText();
}
} else if (xpp.getName().equalsIgnoreCase("link")) {
if (insideItem) {
link = xpp.nextText();
}
} else if (xpp.getName().equalsIgnoreCase("thumbnail")) {
if (insideItem) {
imageLink = xpp.getAttributeValue(null, "url");
}
}
/**Once cursor arrives at the END_TAG, save parsed items into List*/
} else if (eventType == XmlPullParser.END_TAG) {
if(xpp.getName().equalsIgnoreCase("item")){
if (insideItem) {
RssFeedModel item = new RssFeedModel(title, link, description, imageLink);
items.add(item);
}else if (xpp.getName().equalsIgnoreCase("channel")){
insideItem = false;
}
}
}
eventType = xpp.next();
}
return items;
} finally {
inputStream.close();
}
}
What is the best way to use one instance of a Class in multiple Fragments? (or just a method of it)
In my Application I have a Class RangeFetcher which downloads an XML-file and parses it into a model class Item. RangeFetcher holds an ArrayList<Item> items containing the whole XML-file parsed into instances of the model.
I'm creating the Fragment ShopFragment from my starting activity ShopMainViewScreen. In this Fragment I instantiate a RangeFetcher. Since this is a pretty expensive operation in terms of data volume and computing, I would like to pass this instance of RangeFetcher to the other three Fragments I have.
This method getItems is basically all I need in the fragments.
public class RangeFetcher extends AsyncTask {
ArrayList <Item> items = new ArrayList();
//filling the List...
public ArrayList <Item> getItems() {
return items;
}
}
What i took into consideration up to now:
I thought about using a bundle and passing the ArrayList in the arguments to pass the instance from one Fragment to another, but that feels kinda clunky. Could this cause trouble regarding the back-stack?
making the method getItems static? I think it would be feasible since the RangeFetcher will always produce exactly the same output for all the fragments. (am I missing something here?)
Do you have any other suggestions on how to access this instance from multiple fragments?
RangeFetcher Class
package com.XXX;
import android.os.AsyncTask;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
//I know using a switch statement would probably be more fitting here.
//Furthermore I didn't know how to wait for the background task to
//finish so i just introduced a boolean "processing". When this boolean
//is set to false, i continue with my tasks on the fragments.
//So feel free to suggest some improvements to my code ^^
public class RangeFetcher extends AsyncTask {
URL url;
ArrayList < Item > items = new ArrayList();
boolean processing = false;
Item i;
protected Object doInBackground(Object[] objects) {
processing = true;
try {
url = new URL("XXXX");
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(false);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput(getInputStream(url), "UTF_8");
boolean insideItem = false;
boolean priceSet = false;
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("item")) {
insideItem = true;
i = new Item();
} else if (xpp.getName().equalsIgnoreCase("g:id")) {
if (insideItem)
i.setmID(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("title")) {
if (insideItem)
i.setmTitle(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("description")) {
if (insideItem)
i.setmDescription(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:product_type")) {
if (insideItem) {
String fullCategory = xpp.nextText();
if (fullCategory.contains(" ")) {
fullCategory = fullCategory.substring(0, fullCategory.indexOf(" "));
}
i.setmProductType(fullCategory);
}
} else if (xpp.getName().equalsIgnoreCase("g:image_link")) {
if (insideItem)
i.setmPictureLink(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:condition")) {
if (insideItem)
i.setmCondition(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:availability")) {
if (insideItem)
i.setmAvailability(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:price") && priceSet == false) {
if (insideItem)
i.setmPrice(xpp.nextText());
priceSet = true;
} else if (xpp.getName().equalsIgnoreCase("g:brand")) {
if (insideItem)
i.setmBrand(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:mpn")) {
if (insideItem)
i.setmMpn(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:country")) {
if (insideItem)
i.setmShippingCountry(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:service")) {
if (insideItem)
i.setmService(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:price")) {
if (insideItem)
i.setmShippingCosts(xpp.nextText());
} else if (xpp.getName().equalsIgnoreCase("g:pubDate")) {
if (insideItem)
i.setMpubDate(xpp.nextText());
}
} else if (eventType == XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")) {
insideItem = false;
items.add(i);
priceSet = false;
}
eventType = xpp.next(); //move to next element
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
processing = false;
return items;
}
private InputStream getInputStream(URL url) {
try {
return url.openConnection().getInputStream();
} catch (IOException e) {
return null;
}
}
public ArrayList < Item > getItems() {
while (processing == true) {}
return items;
}
public Boolean processing() {
return processing;
}
}
I have found several questions of the name question, but can't get any of them to work. What I want is to get the url of the media:thumbnail tag:
<media:thumbnail width="144" height="81" url="http://c.files.bbci.co.uk/6013/production/_88159542_3e6f2bc3-16a3-407d-9e07-62bae1fa755e.jpg"/>
Above the example of such tag
private void handleText(String text) {
String xmlText = text;
if (currentEntry != null && currentTag != null) {
if (currentTag.equals(TITLE)) {
currentEntry.setTitle(xmlText);
} else if (currentTag.equals(DESC)) {
currentEntry.setDescription(xmlText);
} else if (currentTag.equals(LINK)) {
currentEntry.setLink(xmlText);
} else if (currentTag.equals(IMAGE)) {
currentEntry.setImage("test");
}
}
}
I tried several things as:
xpp.getAttributeValue(null, "url"); and set the image as that. However I noticed that I am not even getting in that else if clause. I tried several values on the IMAGE variable like:
media:thumbnail
media
thumbnail
I have also set namespace aware:
factory.setNamespaceAware(true);
What am I doing wrong?
parser:
XmlPullParser xpp;
int eventType;
protected List<Entry> doInBackground(String... string) {
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
xpp = factory.newPullParser();
xpp.setInput(getInputStream(new URL("http://feeds.bbci.co.uk/news/technology/rss.xml?edition=uk")), "UTF_8");
eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
handleStartTag(xpp.getName());
} else if (eventType == XmlPullParser.END_TAG) {
currentTag = null;
} else if (eventType == XmlPullParser.TEXT) {
handleText(xpp.getText());
}
eventType = xpp.next();
}
} catch (Resources.NotFoundException e) {
Log.d(LOGTAG, e.getMessage());
} catch (XmlPullParserException e) {
Log.d(LOGTAG, e.getMessage());
} catch (IOException e) {
Log.d(LOGTAG, e.getMessage());
}
return entries;
}
I fixed it. I systemed out the start tags it was parsing and it showed up as: thumbnail. So I changed my IMAGE constant to have the value of "thumbnail". It never came in the thumbnail clause since the handleText method only handles found text in a tag. Since media:thumbnail has no text only attributes with values I needed to handle it in the handleStartTag method. There I could say if the current tag name equals "thumbnail" get the attribute value of url and setImage as that value.
I have been creating an app recently that I am working on. The app simply shows the contents of a RSS feed in a ListView where the list items is clickable. It worked on Android 3.0 and below, but once i upgraded my app and device to 4.0 and above, it failed. I can see that I need to use a custom thread because of the NetworkOnMainThreadException, so I have chosen AsyncTask. I simply have a listview in my XML with id android:id/list and the code I use to load the feed looks like this:
//Load RSS news feed
// Initializing instance variables
headlines = new ArrayList();
links = new ArrayList();
try {
URL url = new URL("http://www.vg.no/rss/create.php?categories=25,10,49,26,23,20,21,12,32,30,34&keywords=85,98,1724,84,476,821,820,512,8317,343&limit=15");
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(false);
XmlPullParser xpp = factory.newPullParser();
// We will get the XML from an input stream
xpp.setInput(getInputStream(url), "UTF_8");
/* We will parse the XML content looking for the "<title>" tag which appears inside the "<item>" tag.
* However, we should take in consideration that the rss feed name also is enclosed in a "<title>" tag.
* As we know, every feed begins with these lines: "<channel><title>Feed_Name</title>...."
* so we should skip the "<title>" tag which is a child of "<channel>" tag,
* and take in consideration only "<title>" tag which is a child of "<item>"
*
* In order to achieve this, we will make use of a boolean variable.
*/
boolean insideItem = false;
// Returns the type of current event: START_TAG, END_TAG, etc..
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("item")) {
insideItem = true;
} else if (xpp.getName().equalsIgnoreCase("title")) {
if (insideItem)
headlines.add(xpp.nextText()); //extract the headline
} else if (xpp.getName().equalsIgnoreCase("link")) {
if (insideItem)
links.add(xpp.nextText()); //extract the link of article
}
}else if(eventType==XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")){
insideItem=false;
}
eventType = xpp.next(); //move to next element
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// Binding data
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_1, headlines);
setListAdapter(adapter);
} catch (RuntimeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(getApplicationContext(), "Cannot load news, please check internett connection", Toast.LENGTH_LONG).show();
}
I have been trying to understand it; however, I cannot get it to work. If someone could please help me put all of this in AsyncTask and call the RSS refresh on onCreate() and on onClick() and explain how, it would be great.
Thanks!
this is a boilerplate inline AsyncTask method
new AsyncTask<Void, Void, Void>({
//initialize headlines here or outside of here or before this Asynctask
headlines = new ArrayList();
links = new ArrayList();
#Override
protected Void doInBackground(Void... params)
try {
URL url = new URL("http://www.vg.no/rss/create.php?categories=25,10,49,26,23,20,21,12,32,30,34&keywords=85,98,1724,84,476,821,820,512,8317,343&limit=15");
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(false);
XmlPullParser xpp = factory.newPullParser();
// We will get the XML from an input stream
xpp.setInput(getInputStream(url), "UTF_8");
/* We will parse the XML content looking for the "<title>" tag which appears inside the "<item>" tag.
* However, we should take in consideration that the rss feed name also is enclosed in a "<title>" tag.
* As we know, every feed begins with these lines: "<channel><title>Feed_Name</title>...."
* so we should skip the "<title>" tag which is a child of "<channel>" tag,
* and take in consideration only "<title>" tag which is a child of "<item>"
*
* In order to achieve this, we will make use of a boolean variable.
*/
boolean insideItem = false;
// Returns the type of current event: START_TAG, END_TAG, etc..
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("item")) {
insideItem = true;
} else if (xpp.getName().equalsIgnoreCase("title")) {
if (insideItem)
headlines.add(xpp.nextText()); //extract the headline
} else if (xpp.getName().equalsIgnoreCase("link")) {
if (insideItem)
links.add(xpp.nextText()); //extract the link of article
}
}else if(eventType==XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")){
insideItem=false;
}
eventType = xpp.next(); //move to next element
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected Void onPostExecute() //UI THREAD STUFF , onPostExecute may need an argument, not sure yet though
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_1, headlines);
setListAdapter(adapter);
}
}).execute();
good luck, you can put a loading progress bar in onPreExecute()
I'm trying to extract data from an Xml file, I followed this tutorial:
XmlPullParser tutorial
And now have the following code:
public void parse(InputStream is) {
// create new Study object to hold data
try {
// get a new XmlPullParser object from Factory
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
// set input source
parser.setInput(is, null);
// get event type
int eventType = parser.getEventType();
// process tag while not reaching the end of document
while(eventType != XmlPullParser.END_DOCUMENT) {
switch(eventType) {
// at start of document: START_DOCUMENT
case XmlPullParser.START_DOCUMENT:
break;
// at start of a tag: START_TAG
case XmlPullParser.START_TAG:
// get tag name
String tagName = parser.getName();
Log.i("AT START TAG","AT START TAG..."+tagName);
// if <study>, get attribute: 'id'
if(tagName.equalsIgnoreCase("Date")) {
Log.i("****PARSER INFO","TAG NAME="+tagName+"...."+parser.nextText());
eventDates.add(parser.nextText());
//study.mId = Integer.parseInt(parser.getAttributeValue(null, Study.ID));
}
// if <content>
else if(tagName.equalsIgnoreCase("Name")) {
Log.i("****PARSER INFO","TAG NAME="+tagName+"...."+parser.nextText());
performanceNames.add(parser.nextText());
//study.mContent = parser.nextText();
}
// if <topic>
else if(tagName.equalsIgnoreCase("RequestURL")) {
Log.i("****PARSER INFO","TAG NAME="+tagName+"...."+parser.nextText());
eventsURLS.add(parser.nextText());
//study.mTopic = parser.nextText();
}
break;
}
// jump to next event
eventType = parser.next();
}
// exception stuffs
} catch (XmlPullParserException e) {
//study = null;
} catch (IOException e) {
//study = null;
}
// return Study object
}
For some reason, the code within the IF statements is not running even though I have made sure the tag names do equal the strings above.
What am I doing wrong?