带有自定义类的XmlPullParser不会显示所有RSS源

时间:2017-07-26 06:09:17

标签: java android xml class xml-parsing

我有以下自定义类来从纽约时报,BBC等的一些RSS提要中获取新闻:

class News{
   String title;
   String link;
   String imageURL;
}

这是我用来解析XML数据的代码:

 void getRSSList() {
    newsArray = new ArrayList<News>();

    // Load each RSS feed URL in a for loop
    for (int i = 0; i < catURLsList.size(); i++) {
         String feedURL = catURLsList.get(i);
         try {
             StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
             StrictMode.setThreadPolicy(policy);

             URL aUrl = new URL(feedURL);
             InputStream is = getInputStream(aUrl);
             parseRssFeeds(is);
          } catch (MalformedURLException e) { 
                 e.printStackTrace(); 
          }
     }
}




void parseRssFeeds(InputStream is)  {

        News n = new News();

        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            factory.setNamespaceAware(false);
            XmlPullParser xpp = factory.newPullParser();
            xpp.setInput(is, "UTF_8");

            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;

                    // Get LINK
                    } else if (xpp.getName().equalsIgnoreCase("link")) {
                        if (insideItem) {
                            n.link = xpp.nextText();
                             Log.i("log-", "LINK: " + n.link);
                        }

                    // Get TITLE
                    } else if (xpp.getName().equalsIgnoreCase("title")) {
                        if (insideItem) {
                            n.title = xpp.nextText();
                             Log.i("log-", "TITLE: " + n.title);
                        }

                    // Get MEDIA URL
                    } else if (xpp.getName().equalsIgnoreCase("media:content")) {
                        if (insideItem)
                            n.imageURL = xpp.getAttributeValue(null, "url");
                             Log.i("log-", "MEDIA URL: " + n.imageURL);
                    }

                } else if (eventType == XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")) {
                    insideItem = false;
                }

                // Add news objects to the newsArray
                newsArray.add(n);

                eventType = xpp.next(); // move to next element

            } // end WHILE loop

        } catch(Exception e) { e.printStackTrace(); }


        setNewsGridView();
    }

setNewsGridView()调用一个GridAdapter的方法,里面显示了RSSfeeds的标题和图片,问题是我在Logcat中获得所有标题,链接和媒体URL,但我只得到我的GridView的每个单元格中重复的一个新闻源,可能与newsArray的大小一样。

这是我的GridAdapter:

 // MARK: - SET NEWS GRID VIEW ---------------------------------------------
    void setNewsGridView() {

        class GridAdapter extends BaseAdapter {
            private Context context;
            public GridAdapter(Context context, List<News> objects) {
                super();
                this.context = context;
            }


            // CONFIGURE CELL
            @Override
            public View getView(int position, View cell, ViewGroup parent) {
                if (cell == null) {
                    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    cell = inflater.inflate(R.layout.cell_news, null);
                }

                // Get News object
                News n = newsArray.get(position);


                // Get Title
                TextView titleTxt = (TextView) cell.findViewById(R.id.cnTitleTxt);
                titleTxt.setText(n.title);


                // Get Image
                ImageView newsImg = (ImageView)cell.findViewById(R.id.cnImage);
                if (n.imageURL != null) {
                    Picasso.with(context).load(n.imageURL).into(newsImg);
                } else { newsImg.setImageResource(R.drawable.logo); }



                return cell;
            }

            @Override public int getCount() { return newsArray.size(); }
            @Override public Object getItem(int position) { return newsArray.get(position); }
            @Override public long getItemId(int position) { return position; }
        }


        // Init GridView and set its adapter
        GridView aGrid = (GridView) findViewById(R.id.hNewsGridView);
        aGrid.setAdapter(new GridAdapter(Home.this, newsArray));

        // Set number of Columns accordingly to the device used
        float scalefactor = getResources().getDisplayMetrics().density * 150; // 150 is the cell's width
        int number = getWindowManager().getDefaultDisplay().getWidth();
        int columns = (int) ((float) number / (float) scalefactor);
        aGrid.setNumColumns(columns);


    }

运行应用时的我的Logcat:

    I/log-: TITLE: Senate Votes Down Broad Obamacare Repeal
    I/log-: LINK: https://www.nytimes.com/2017/07/25/us/politics/senate-health-care.html?partner=rss&emc=rss
    I/log-: MEDIA URL: https://static01.nyt.com/images/2017/07/26/us/26dc-health-sub1/26dc-health-sub1-moth.jpg
    I/log-: TITLE: John McCain to Senate: ‘We’re Getting Nothing Done’
    I/log-: LINK: https://www.nytimes.com/video/us/politics/100000005305566/john-mccain-health-bill-vote.html?partner=rss&emc=rss
    I/log-: TITLE: McCain Returns to Cast Vote to Help the President Who Derided Him
    I/log-: LINK: https://www.nytimes.com/2017/07/25/us/politics/mccain-health-care-brain-cancer

etc...
...

这是我在设备上获得的输出:

enter image description here

我做错了什么?

非常感谢!

3 个答案:

答案 0 :(得分:2)

parseRssFeeds内部,您在进入主循环之前实例化News对象,然后当您从XML中提取新闻块时,您可以在此设置值并将其添加到newsArray。然后,当您继续迭代时,每次都会覆盖News对象n中的值。从Java passes objects as references开始,您最终会得到一个包含单个实例n的多个副本的列表,其值将设置为XML中的最后一个条目。

要解决此问题,您需要在循环内移动声明。 (编辑)我在这里第一次尝试的问题是我假设在单循环迭代期间将读取每个News对象,但情况并非如此。每次遇到新的新闻时,都需要实例化一个新的News对象,这应该会给你以下内容:

            // Returns the type of current event: START_TAG, END_TAG, etc..
            News currentNewsItem = null;
            int eventType = xpp.getEventType();
            while (eventType != XmlPullParser.END_DOCUMENT) {

                if (eventType == XmlPullParser.START_TAG) {
                    if (xpp.getName().equalsIgnoreCase("item")) {
                        insideItem = true;

                    // Get LINK
                    } else if (xpp.getName().equalsIgnoreCase("link")) {
                        if (insideItem) {
                            // If no item is currently in progress, start one
                            currentNewsItem = startNewItemIfRequired(currentNewsItem, newsArray);

                            currentNewsItem.link = xpp.nextText();
                             Log.i("log-", "LINK: " + currentNewsItem.link);
                        }

                    // Get TITLE
                    } else if (xpp.getName().equalsIgnoreCase("title")) {
                        if (insideItem) {
                            // Start a new news item, even if one is already in progress 
                            currentNewsItem = startNewItemIfRequired(null, newsArray);

                            currentNewsItem.title = xpp.nextText();
                             Log.i("log-", "TITLE: " + currentNewsItem.title);
                        }

                    // Get MEDIA URL
                    } else if (xpp.getName().equalsIgnoreCase("media:content")) {
                        if (insideItem)
                            // If no item is currently in progress, start one
                            currentNewsItem = startNewItemIfRequired(currentNewsItem, newsArray);

                            currentNewsItem.imageURL = xpp.getAttributeValue(null, "url");
                             Log.i("log-", "MEDIA URL: " + currentNewsItem.imageURL);
                    }

                } else if (eventType == XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")) {
                    insideItem = false;
                }

                eventType = xpp.next(); // move to next element

            } // end WHILE loop

        } catch(Exception e) { e.printStackTrace(); }

使用此附加功能:

private News startNewItemIfRequired(News currentNewsItem, List<News> newsArray) {
    if (currentNewsItem==null){
        currentNewsItem = new News();
        newsArray.add(currentNewsItem);
    }
    return currentNewsItem;
}

只要&#34;标题&#34;这将创建一个新的新闻项目。遇到启动标记,并将其添加到面板,然后继续填充此标记,直到开始新标记。为了安全起见,我已经添加了一些检查,如果没有&#34;标题&#34;,它将防止空指针。也就是说,这确实增加了一个约束&#34; title&#34;必须首先出现在您收到的XML中 - 这似乎是您的日志中的情况,但如果您希望这会有所不同,则需要更复杂的内容。

我补充一点,这种流媒体方法是启动这种方法的最简单方法,但如果你想在未来使其更加强大(特别是防止XML结构和缺少字段的更改),你可能会首先将XML作为文档阅读,然后删除您感兴趣的结构,以获得更好的服务。

我担心我的问题还没有得到足够的代码让我站起来而不花一些时间分开你的依赖关系,所以我还没有能够实际上我自己测试了这段代码,但希望这会给你足够的答案来解决你的问题!

答案 1 :(得分:2)

问题是您创建了News n = new News();对象一次,但每次都将该对象添加到ArrayList中,因此它总是显示所有项目中的最后一条新闻内容。每次获得名为item的新标记时都应创建对象,并在标记item的末尾添加int,应插入ArrayList。

你应该这样做,

void parseRssFeeds(InputStream is) {

    // News n = new News(); your are creating object only here.
    News n = null;

    try {
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        factory.setNamespaceAware(false);
        XmlPullParser xpp = factory.newPullParser();
        xpp.setInput(is, "UTF_8");

        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;
                    n = new News(); // you should intialize the object every time here

                    // Get LINK
                } else if (xpp.getName().equalsIgnoreCase("link")) {
                    if (insideItem) {
                        n.link = xpp.nextText();
                        Log.i("log-", "LINK: " + n.link);
                    }

                    // Get TITLE
                } else if (xpp.getName().equalsIgnoreCase("title")) {
                    if (insideItem) {
                        n.title = xpp.nextText();
                        Log.i("log-", "TITLE: " + n.title);
                    }

                    // Get MEDIA URL
                } else if (xpp.getName().equalsIgnoreCase("media:content")) {
                    if (insideItem)
                        n.imageURL = xpp.getAttributeValue(null, "url");
                    Log.i("log-", "MEDIA URL: " + n.imageURL);
                }

            } else if (eventType == XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")) {
                insideItem = false;
                // End of the item tag, we should add it into the list
                // Add news objects to the newsArray
                newsArray.add(n);
            }


            eventType = xpp.next(); // move to next element

        } // end WHILE loop

    } catch (Exception e) {
        e.printStackTrace();
    }


    setNewsGridView();
}

答案 2 :(得分:0)

使用SimpleXmlConverter进行改造

          OkHttpClient okHttpClient = new 
          OkHttpClient.Builder().readTimeout(60, 
          TimeUnit.SECONDS).connectTimeout(60, TimeUnit.SECONDS)
            .addInterceptor(new Interceptor() {
        @Override
        public Response intercept(Interceptor.Chain chain) throws IOException 
         {
            Request original = chain.request();

            Request.Builder requestBuilder = original.newBuilder()
                    .header("Accept", "application/xml");

            Request request = requestBuilder.build();
            return chain.proceed(request);
        }
    }).build();

    retrofitClient = new Retrofit.Builder()
            .baseUrl(RestAPI.baseURL)
            .client(okHttpClient)
            .addConverterFactory(SimpleXmlConverterFactory.create())
            .build();

    restAPI = retrofitClient.create(RestAPI.class);