2012-05-08 5 views
2

Я пытаюсь вытащить XHTML из RSS-канала, поэтому я могу разместить его в WebView. В RSS-канале есть тег под названием <content>, а символы внутри содержимого - XHTML. (Сайт, который я обрабатываю, является блогером блогера) Каков наилучший способ попытаться извлечь этот контент? Символы < путают мой парсер. Я пробовал как DOM, так и SAX, но никто не может справиться с этим очень хорошо.Как вы вытаскиваете XHTML из фида ATOM с помощью Java?

Here is a sample of the XML as requested. В этом случае я хочу, чтобы XHTML внутри тега контента являлся строкой. <content> XHTML </content>

Редактировать: по предложению ignyhere Я пробовал XPath, но у меня все еще такая же проблема. Here is a pastebin sample of my tests.

+0

Можете ли вы включить образец xhtml? – waqaslam

+0

@Waqas Я обновил свой вопрос с помощью образца. – schwiz

+0

http://www.xml.com/pub/a/2006/02/22/rome-parse-publish-rss-atom-feeds-java.html или другая библиотека http://www.giantflyingsaucer.com/blog /? p = 3308 – ant

ответ

3

Это не очень, но это (сущность), что я использую для анализа фида ATOM из Blogger с использованием XmlPullParser. Код довольно нехороший, но он из реального приложения. В любом случае, вы можете получить общий вкус.

final String TAG_FEED = "feed"; 

public int parseXml(Reader reader) { 
    XmlPullParserFactory factory = null; 
    StringBuilder out = new StringBuilder(); 
    int entries = 0; 

    try { 
     factory = XmlPullParserFactory.newInstance(); 
     factory.setNamespaceAware(true); 
     XmlPullParser xpp = factory.newPullParser(); 
     xpp.setInput(reader); 

     while (true) { 
      int eventType = xpp.next(); 
      if (eventType == XmlPullParser.END_DOCUMENT) { 
       break; 
      } else if (eventType == XmlPullParser.START_DOCUMENT) { 
       out.append("Start document\n"); 
      } else if (eventType == XmlPullParser.START_TAG) { 
       String tag = xpp.getName(); 
       // out.append("Start tag " + tag + "\n"); 
       if (TAG_FEED.equalsIgnoreCase(tag)) { 
        entries = parseFeed(xpp); 
       } 
      } else if (eventType == XmlPullParser.END_TAG) { 
       // out.append("End tag " + xpp.getName() + "\n"); 
      } else if (eventType == XmlPullParser.TEXT) { 
       // out.append("Text " + xpp.getText() + "\n"); 
      } 
     } 
     out.append("End document\n"); 

    } catch (XmlPullParserException e) { 
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    //  return out.toString(); 
    return entries; 

} 

private int parseFeed(XmlPullParser xpp) throws XmlPullParserException, IOException { 
    int depth = xpp.getDepth(); 
    assert (depth == 1); 
    int eventType; 
    int entries = 0; 
    xpp.require(XmlPullParser.START_TAG, null, TAG_FEED); 
    while (((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) && (xpp.getDepth() > depth)) { 
     // loop invariant: At this point, the parser is not sitting on 
     // end-of-document, and is at a level deeper than where it started. 

     if (eventType == XmlPullParser.START_TAG) { 
      String tag = xpp.getName(); 
      // Log.d("parseFeed", "Start tag: " + tag); // Uncomment to debug 
      if (FeedEntry.TAG_ENTRY.equalsIgnoreCase(tag)) { 
       FeedEntry feedEntry = new FeedEntry(xpp); 
       feedEntry.persist(this); 
       entries++; 
       // Log.d("FeedEntry", feedEntry.title); // Uncomment to debug 
       // xpp.require(XmlPullParser.END_TAG, null, tag); 
      } 
     } 
    } 
    assert (depth == 1); 
    return entries; 
} 

class FeedEntry { 
    String id; 
    String published; 
    String updated; 
    // Timestamp lastRead; 
    String title; 
    String subtitle; 
    String authorName; 
    int contentType; 
    String content; 
    String preview; 
    String origLink; 
    String thumbnailUri; 
    // Media media; 

    static final String TAG_ENTRY = "entry"; 
    static final String TAG_ENTRY_ID = "id"; 
    static final String TAG_TITLE = "title"; 
    static final String TAG_SUBTITLE = "subtitle"; 
    static final String TAG_UPDATED = "updated"; 
    static final String TAG_PUBLISHED = "published"; 
    static final String TAG_AUTHOR = "author"; 
    static final String TAG_CONTENT = "content"; 
    static final String TAG_TYPE = "type"; 
    static final String TAG_ORIG_LINK = "origLink"; 
    static final String TAG_THUMBNAIL = "thumbnail"; 
    static final String ATTRIBUTE_URL = "url"; 

    /** 
    * Create a FeedEntry by pulling its bits out of an XML Pull Parser. Side effect: Advances 
    * XmlPullParser. 
    * 
    * @param xpp 
    */ 
public FeedEntry(XmlPullParser xpp) { 
    int eventType; 
    int depth = xpp.getDepth(); 
    assert (depth == 2); 
    try { 
     xpp.require(XmlPullParser.START_TAG, null, TAG_ENTRY); 
     while (((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) 
     && (xpp.getDepth() > depth)) { 

      if (eventType == XmlPullParser.START_TAG) { 
       String tag = xpp.getName(); 
       if (TAG_ENTRY_ID.equalsIgnoreCase(tag)) { 
        id = Util.XmlPullTag(xpp, TAG_ENTRY_ID); 
       } else if (TAG_TITLE.equalsIgnoreCase(tag)) { 
        title = Util.XmlPullTag(xpp, TAG_TITLE); 
       } else if (TAG_SUBTITLE.equalsIgnoreCase(tag)) { 
        subtitle = Util.XmlPullTag(xpp, TAG_SUBTITLE); 
       } else if (TAG_UPDATED.equalsIgnoreCase(tag)) { 
        updated = Util.XmlPullTag(xpp, TAG_UPDATED); 
       } else if (TAG_PUBLISHED.equalsIgnoreCase(tag)) { 
        published = Util.XmlPullTag(xpp, TAG_PUBLISHED); 
       } else if (TAG_CONTENT.equalsIgnoreCase(tag)) { 
        int attributeCount = xpp.getAttributeCount(); 
        for (int i = 0; i < attributeCount; i++) { 
         String attributeName = xpp.getAttributeName(i); 
         if (attributeName.equalsIgnoreCase(TAG_TYPE)) { 
          String attributeValue = xpp.getAttributeValue(i); 
          if (attributeValue 
          .equalsIgnoreCase(FeedReaderContract.FeedEntry.ATTRIBUTE_NAME_HTML)) { 
           contentType = FeedReaderContract.FeedEntry.CONTENT_TYPE_HTML; 
           } else if (attributeValue 
           .equalsIgnoreCase(FeedReaderContract.FeedEntry.ATTRIBUTE_NAME_XHTML)) { 
            contentType = FeedReaderContract.FeedEntry.CONTENT_TYPE_XHTML; 
           } else { 
            contentType = FeedReaderContract.FeedEntry.CONTENT_TYPE_TEXT; 
           } 
           break; 
          } 
         } 
         content = Util.XmlPullTag(xpp, TAG_CONTENT); 
         extractPreview(); 
        } else if (TAG_AUTHOR.equalsIgnoreCase(tag)) { 
         // Skip author for now -- it is complicated 
         int authorDepth = xpp.getDepth(); 
         assert (authorDepth == 3); 
         xpp.require(XmlPullParser.START_TAG, null, TAG_AUTHOR); 
         while (((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) 
         && (xpp.getDepth() > authorDepth)) { 
         } 
         assert (xpp.getDepth() == 3); 
         xpp.require(XmlPullParser.END_TAG, null, TAG_AUTHOR); 

        } else if (TAG_ORIG_LINK.equalsIgnoreCase(tag)) { 
         origLink = Util.XmlPullTag(xpp, TAG_ORIG_LINK); 
        } else if (TAG_THUMBNAIL.equalsIgnoreCase(tag)) { 
         thumbnailUri = Util.XmlPullAttribute(xpp, tag, null, ATTRIBUTE_URL); 
        } else { 
         @SuppressWarnings("unused") 
          String throwAway = Util.XmlPullTag(xpp, tag); 
        } 
       } 
      } // while 
     } catch (XmlPullParserException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
     assert (xpp.getDepth() == 2); 
    } 
} 

public static String XmlPullTag(XmlPullParser xpp, String tag) 
    throws XmlPullParserException, IOException { 
    xpp.require(XmlPullParser.START_TAG, null, tag); 
    String itemText = xpp.nextText(); 
    if (xpp.getEventType() != XmlPullParser.END_TAG) { 
     xpp.nextTag(); 
    } 
    xpp.require(XmlPullParser.END_TAG, null, tag); 
    return itemText; 
} 

public static String XmlPullAttribute(XmlPullParser xpp, 
    String tag, String namespace, String name) 
throws XmlPullParserException, IOException { 
    assert (!TextUtils.isEmpty(tag)); 
    assert (!TextUtils.isEmpty(name)); 
    xpp.require(XmlPullParser.START_TAG, null, tag); 
    String itemText = xpp.getAttributeValue(namespace, name); 
    if (xpp.getEventType() != XmlPullParser.END_TAG) { 
     xpp.nextTag(); 
    } 
    xpp.require(XmlPullParser.END_TAG, null, tag); 
    return itemText; 
} 

Я дам вам подсказку: ничто из возвращаемых значений не имеет значения. Данные сохраняются в базе данных по методу (не показан), который вызывается в этой строке:

feedEntry.persist(this); 
+0

Спасибо за отличный ответ! Много, чтобы впитаться здесь, это выглядит намного лучше и надежнее, чем то, что я закончил, поэтому я не могу дождаться, когда попытаюсь это сделать, как только я получу некоторое время простоя. – schwiz

3

Я бы попытался атаковать его с помощью XPath. Будет что-то вроде этой работы?

public static String parseAtom (InputStream atomIS) 
    throws Exception { 

    // Below should yield the second content block 
    String xpathString = "(//*[starts-with(name(),"content")])[2]"; 
    // or, String xpathString = "//*[name() = 'content'][2]"; 
    // remove the '[2]' to get all content tags or get the count, 
    // if needed, and then target specific blocks 
    //String xpathString = "count(//*[starts-with(name(),"content")])"; 
    // note the evaluate expression below returns a glob and not a node set 

    XPathFactory xpf    = XPathFactory.newInstance(); 
    XPath xpath     = xpf.newXPath(); 
    XPathExpression xpathCompiled = xpath.compile (xpathString); 

    // use the first to recast and evaluate as NodeList 
    //Object atomOut = xpathCompiled.evaluate ( 
    // new InputSource (atomIS), XPathConstants.NODESET); 
    String atomOut = xpathCompiled.evaluate ( 
     new InputSource (atomIS), XPathConstants.STRING); 

    System.out.println (atomOut); 

    return atomOut; 

} 
+0

Не совсем работает, я не думаю, что вы можете дать более надежный образец? Я не очень хорошо знаком с Xpath, поэтому, возможно, я делаю глупую ошибку. Я обновлю свой вопрос с помощью пастеина моего теста, если вы хотите посмотреть. – schwiz

+0

Извините, я изменил выражение XPath для работы в данном сценарии. – ingyhere

+0

Спасибо за обновление, когда у меня есть время простоя, мне придется протестировать ваш xpath. Однако мне удалось получить дескриптор узла контента, на котором я был (проблема была в пространстве имен), но он все еще не вытащил xhtml полностью. Все еще повышается за вашу помощь :) – schwiz

1

Я вижу вашу проблему здесь, причина, почему эти парсеры не производя правильный результат, потому что содержимое вашей <content> тега не заворачивают в <![CDATA[ ]]>, что бы я делал, пока я не найду более адекватное решение I» d использовать быстрый и грязный трюк:

private void parseFile(String fileName) throws IOException { 
     String line; 
     BufferedReader br = new BufferedReader(new FileReader(new File(fileName))); 
     StringBuilder sb = new StringBuilder(); 
     boolean match = false; 

     while ((line = br.readLine()) != null) { 
      if(line.contains("<content")){ 
       sb.append(line); 
       sb.append("\n"); 
       match = true; 
       continue; 
      } 

      if(match){ 
       sb.append(line); 
       sb.append("\n"); 
       match = false; 
      } 

      if(line.contains("</content")){ 
       sb.append(line); 
       sb.append("\n"); 
      } 
     } 

     System.out.println(sb.toString()); 
    } 

Это даст вам весь контент в String. Вы можете опционально разделить их, слегка модифицируя этот метод, или если вам не нужен фактический <content>, вы также можете отфильтровать его.

+0

Спасибо за ваше понимание о CDATA, я закончил тем, что просто заменил строку, чтобы добавить тег CDATA после содержимого. SAX по-прежнему не мог справиться с этим, но DOM читает его так, как я хотел. Наверное, это не лучшее решение, но оно отвечает моим требованиям. – schwiz

+0

Неверно. В xml нет требования, чтобы содержимое элемента было обернуто в CDATA. Это необходимо только в том случае, если данные не кодируются надлежащим образом для XML. Он работает с тем, что предоставляет сервер, и не обязательно обязательно менять набор результатов. – ingyhere

+0

@ingyhere да Я не говорю, что я доволен решением, я просто говорю, по крайней мере, это работает :-) – schwiz

Смежные вопросы