0

NOTE: I really have no idea how to ask my question so my title probably didn't do any justice.

I'm beginning to write an RSS parser. I want the user to be able to specify the tag they want information on and my parseInnerXmlByTag method will return an ArrayList of that type. Problem is that different tags have different properties, which means I will have to create classes for each type of tag ("item", "channel", "entry"...). So I decided to create an empty RSSElement class. Then I created an RSSItem class that extends it. So my parseInnerXmlByTag now returns an ArrayList < RSSElements >. My goal is for the user to only call one method specifying the tag they want info about. Say they specify "item", the method should return ArrayList < RSSItem >.

But a line like this won't work because those two types don't techically have a relationship:

ArrayList<RSSElement> data = (method returning ArrayList<RSSItem>) 

I'm having trouble wrapping my head around wildcards and subtypes so I was wondering if someone could check out what I have so far and give me your thoughts. Here's my code

public class RSSParser {
public static ArrayList<RSSElement> parseInnerXmlByTag(String in, String tag)
        throws XmlPullParserException, IOException{
    ArrayList<RSSElement> data = new ArrayList<>();
    StringReader input = new StringReader(in);
    try{
        XmlPullParser parser = Xml.newPullParser();
        parser.setInput(input);
        parser.nextTag();
        data = readFeed(parser, tag);
    }
    finally {
        input.close();
        return data;
    }
}

/*
Returns an ArrayList of parsed RSSElements specified by "tag"
 */
private static ArrayList<RSSElement> readFeed(XmlPullParser parser, String tag)
        throws  XmlPullParserException, IOException{
    ArrayList<RSSElement> data = new ArrayList<>();
    int event = parser.getEventType();

    //Parse until input is consumed
    while(event != XmlPullParser.END_DOCUMENT){
        switch (event){
            case XmlPullParser.START_TAG:
                if(parser.getName().equalsIgnoreCase(tag)){
                    RSSItem item = RSSItem.processItem(parser);
                    data.add(RSSItem.processItem(parser));
                }
                break;
        }
        event = parser.next();
    }
    return data;
}

public static abstract class RSSElement{
    //Private constructor prevents instantiation
    private RSSElement(){}
}

public static class RSSItem extends RSSElement{
    String title = "";
    String description = "";
    public RSSItem(String t, String d){
        title = t;
        description = d;
    }

    @Override
    public String toString() {...}

    /*
    Parses an RSS item tag into a container RSSItem (ONLY GRABS TITLE RIGHT NOW)
    */
    protected static RSSItem processItem(XmlPullParser parser)
            throws XmlPullParserException, IOException{
        String currentTag = "";
        String title = "";
        /*
            while end tag hasn't been reached yet:
            reaching the end tag means parser.getEventType == END_TAG && parser.getName = tag
        */
        while(!(parser.next() == XmlPullParser.END_TAG && parser.getName().equalsIgnoreCase("item"))){
            Log.i("rssparser", "process entry");
            if(parser.getEventType() == XmlPullParser.START_TAG){
                currentTag = parser.getName();
            }
            else if(parser.getEventType() == XmlPullParser.TEXT && currentTag.equalsIgnoreCase("title")){
                title = parser.getText();
                break;
            }
        }
        return new RSSItem(title, description);
    }
}
}

Now this code completes and returns the ArrayList. Also I know it's parsing RSSItems correctly based on my logs. But here is where I use it (aka the reason I'm posting here)

                @Override
                public void onResponse(String response){
                    ArrayList<? extends RSSParser.RSSElement> items = null;
                    try {
                        items = RSSParser.parseInnerXmlByTag(response, "entry");
                    }...

                    if(items != null) {
                        for(int i = 0; i < items.size(); i++){
                           //****DOESNT WORK****
                            redditOutputTextView.append(items.get(i).title + "\n"); 
                        }
                    }
                    else redditOutputTextView.setText("Items null");
                }

That line there obviously can't work because it doesn't know it has an array of RSSItems. So hopefully someone can figure out what I'm trying to do and make some suggestions.

sadelbrid
  • 411
  • 1
  • 4
  • 16

3 Answers3

2

Could you please replace your following snippet:

//****DOESNT WORK****
redditOutputTextView.append(items.get(i).title + "\n");

by following statements:

//****WORKS****
Object obj=items.get(i);
if(obj instanceof RSSItem){
    RSSItem rssItem = (RSSItem)obj;
    redditOutputTextView.append(rssItem .title + "\n");
}

and see the result?

Sanjeev Saha
  • 2,632
  • 1
  • 12
  • 19
  • Fixed it! Well a combination of that and the fact I noticed I was calling the processItem method twice in a row which might have been creating empty objects. Thanks mate. – sadelbrid May 28 '16 at 03:06
0

Change

ArrayList<? extends RSSParser.RSSElement> items = null;

To

ArrayList<RSSParser.RSSElement> items = null;

Basically, you now have a list that can take items of any subclass of RSSElement

The old one was a list that could only take items of some class which derived from RSSElement, and since you used a wildcard, that type could be anything, so the compiler doesn't know. You can call get and use it however, because you know the superclass of the elements: RSSElement.

For more information, see this answer.

Community
  • 1
  • 1
k_g
  • 4,333
  • 2
  • 25
  • 40
  • But say my method returns ArrayList and puts it in items, how would I access the RSSItem properties? – sadelbrid May 28 '16 at 02:46
  • You're going to eventually need to check the types of things, so either do an instanceof tree or make the methods of the abstract class – k_g May 28 '16 at 02:47
0

You can ues the return fun code like this:

public static List<? extends Parent> test2(){
        List<Parent> parents = new ArrayList<Parent>();
        parents.add(new Son1());
        return parents;
}

and get the list like this:

List<? extends Parent> parents = test2();

and obv, Clss Son1() extend Parent.

vipkk
  • 24
  • 4