Java -Android. Parser problem
I am making a very simple app with an RSS reader. The reader works great, but it's only giving me the title, and i want the description too. I'am very new to android, and I have tried a lot of things, but I can't get it to work. I've found a lot of parsers but they are to complicated for me to understand, so I was hoping to find a simple solution, since it's only title and description i want. Can anyone help me?
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class NyhedActivity extends Activity {
String streamTitle = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.nyheder);
TextView result = (TextView)findViewById(R.id.result);
try {
URL rssUrl = new URL("http://tv2sport.dk/rss/*/*/*/248/*/*");
SAXParserFactory mySAXParserFactory = SAXParserFactory.newInstance();
SAXParser mySAXParser = mySAXParserFactory.newSAXParser();
XMLReader myXMLReader = mySAXParser.getXMLReader();
RSSHandler myRSSHandler = new RSSHandler();
myXMLReader.setContentHandler(myRSSHandler);
InputSource myInputSource = new InputSource(rssUrl.openStream());
myXMLReader.parse(myInputSource);
result.setText(streamTitle);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
result.setText("Cannot connect RSS!");
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
result.setText("Cannot connect RSS!");
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
result.setText("Cannot connect RSS!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
result.setText("Cannot connect RSS!");
}
}
private class RSSHandler extends DefaultHandler
{
final int stateUnknown = 0;
final int stateTitle = 1;
int state = stateUnknown;
int numberOfTitle = 0;
String strTitle = "";
String strElement = "";
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
strTitle = "Nyheder fra ";
}
@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub
strTitle += "";
streamTitle = "" + strTitle;
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// TODO Auto-generated method stub
if (localName.equalsIgnoreCase("title"))
{
state = stateTitle;
strElement = "";
numberOfTitle++;
}
else
{
state = stateUnknown;
}
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
if (localName.equalsIgnoreCase("title"))
{
strTitle += strElement + "\n"+"\n";
}
state = stateUnknown;
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
String strCharacters = new String(ch, start, length);
if (state == stateTitle)
{
strElement += strC开发者_如何学Characters;
}
}
}
}
I've never really used SAX, when it comes to parsing XML in Java. I allways use JDOM. It's simple and really easy to use.
To read a an XML-file with JDOM, you create a document and fill it using an InputStream and a SAXBuilder:
SAXBuilder builder = new SAXBuilder();
Document document = builder.builder( myInputStream );
In your posted case: myInputStream = url.openStream();
Then you need to fetch the root of the XML-document:
Element root = document.getRootElement();
Now it's very simple. Since I don't know the structure of the XML you're getting I'll just assume that it looks something like:
<rssfeed>
<news>
<title> Title </title>
<description> Description </description>
</news>
<news>
<title> ... </title>
<description> ... </description>
</news>
<news>
<title> ... </title>
<description> ... </description>
</news>
<rssfeed>
You can then list all elements of like this:
List<Element> news = root.getChildren( "news" );
Then you run through the list in a for-each loop, getting the title and description (Having a data-class to hold these information would be a help e.g. a News-class):
ArrayList<News> newsList = new ArrayList<News>();
for( Element child : news ) {
News news = new News();
news.setTitle( child.getChildText( "title" );
news.setDescription( child.getChildText( "description" );
newsList.add( news );
}
Now you have a list of news that you can play around with.
Kano,
You can simplify your life and get top-notch performance by utilizing SJXP to write this RSS feed parser with (disclaimer: I am the author).
SJXP is a very-very thin abstraction layer that sits on top of the XML Pull Parsing API (Android provides its own so you only have the sjxp.JAR dependency, XPP3 for every other platform) and allows you to use XPath-like parsing rules for matching simple rules against certain places of a document and then telling the parsing what information you want from those locations.
I wrote an example Eclipse project for you that parses that TV2 Sports feed for you in 6 minutes (I'll link it at the bottom).
The main method looks like this so you get an idea of the flow:
public static void main(String[] args) throws IllegalArgumentException,
XMLParserException, IOException {
// Location we want to parse.
URL feedURL = new URL("http://tv2sport.dk/rss/*/*/*/248/*/*");
// List we will hold all parsed stories in.
List<Item> itemList = new ArrayList<Item>();
// Get all the rules we will use to parse this file
IRule[] rules = createRules();
// Create the parser and populate it with the rules.
XMLParser<List<Item>> parser = new XMLParser<List<Item>>(rules);
// Parse the RSS feed.
parser.parse(feedURL.openStream(), itemList);
// Print the results.
System.out.println("Parsed " + itemList.size() + " RSS items.");
for (Item i : itemList)
System.out.println("\t" + i);
}
You see the flow starts with creating our List to hold our Items in as we parse them from the doc. Then we get a set of IRule instances to give the parser, then create the parser and give it the rules to use while working.
We then invoke the parse method on the contents of the feed and pass it what is called a "user object", more specifically, just an instance of anything that we would like it to pass-through to the rules when they execute.
In this case, we want access to our List so we can add items to it, so we just pass that in and the parser passes it right through to our IRule logic when it executes so we can use it.
The Item class utilizes is just a simple POJO to hold the data and make printing look nice:
public class Item {
public String title;
public String description;
@Override
public String toString() {
return "Item [title='" + title + "', description='" + description + "']";
}
}
All the interesting stuff happens in your IRule where you define what kind of element you are targeting (character data, attribute data or just tag open/close events) and then override the appropriate method from the IRule interface to provide a handler that does something.
For example, here is the handler that parses the titles:
IRule<List<Item>> itemDescRule = new DefaultRule<List<Item>>(Type.CHARACTER, "/rss/channel/item/description") {
@Override
public void handleParsedCharacters(XMLParser<List<Item>> parser, String text, List<Item> userObject) {
Item item = userObject.get(userObject.size() - 1);
item.description = text;
}
};
You see that you get given the parser instance itself (so you can trigger the 'stop' method if you want to end parsing early), you get the text that was the character data and you get that 'user object' which happened to be our list passed through to you.
We grab the item we are populating out of the list, give it the description and that's it. 2 lines of code.
There is another IRule that adds a new Item to the list every time an open-tag is encountered, that is what allows our subsequent rules like this one to just pop the end element off the list and populate it.
When you run the project, the output looks like this:
Parsed 50 RSS items.
Item [title='Barcas bøddel beæret over Barca-føler', description='Tirsdag snød Thiago Silva Barcelona for tre point, da han headede AC Milans udligning i kassen i Champions League-kampens overtid.']
Item [title='Guardiola: Pato hurtigere end Usain Bolt', description='FC Barcelona-træner, Josep Guardiola, er dybt imponeret af Milan-målscoreren Alexandre Patos hurtighed.']
Item [title='Milan-profil: Vi kan nå semifinalen', description='Clarence Seedorf mener, at AC Milan kan nå semifinalerne i Champions League efter 2-2 i Barcelona.']
<SNIP...>
You can download the entire Eclipse project I created for you here.
Hope that helps.
I hope I can help you:
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
if (localName.equalsIgnoreCase("title"))
{
strTitle += strElement + "\n"+"\n";
}
else if (localName.equalsIgnoreCase("lead"))
{
lead += strElement + "\n"+"\n";
}
}
精彩评论