Android org.xmlpull.v1.XmlPullParserException при анализе XML
У меня есть ситуация, когда я вызываю веб-сервис, и он возвращает мне немного HTML в конверте XML. лайк:
<xml version="1.0" cache="false">
<head/>
<body>
<table>
<tr>
<td>
<a href="link-to-prev-post">
<text color="red"><< Prev</text>
</a>
</td>
<td>
<a href="link-to-next-post">
<text color="red">| Next >></text>
</a>
</td>
</tr>
</table>
</body>
</xml>
Мне нужно получить ссылки "ссылка на предыдущий пост" и " ссылка на следующий пост"... чтобы я мог получить больше данных по этим ссылкам.
Я использую XmlPullParser для анализа предоставленного выше XML/HTML. Чтобы получить ссылки для следующих / предыдущих элементов, я делаю следующее:
if (xmlNodeName.equalsIgnoreCase("a")) {
link = parser.getAttributeValue(null, "href");
} else if (xmlNodeName.equalsIgnoreCase("text")) {
color = parser.getAttributeValue(null, "color");
if (color.equalsIgnoreCase("red") && parser.getEventType() == XmlPullParser.START_TAG) {
// check for next/prev blog entries links
// but this parser.nextText() throws XmlPullParserException
// i think because the nextText() returns << Prev which the parser considers to be wrong
String innerText = parser.nextText();
if (innerText.contains("<< Prev")) {
blog.setPrevBlogItemsUrl(link);
} else if (innerText.contains("Next >>")) {
blog.setNextBlogItemsUrl(link);
}
}
link = null;
}
}
Он генерирует исключение XmlPullParserException при выполнении parser.nextText ()... и значение текстового элемента в это время равно << Prev.. я думаю, что оно неправильно понимает это значение с начальным тегом из-за присутствия << в тексте..
LogCat подробно это:
04-08 18:32:09.827: W/System.err(688): org.xmlpull.v1.XmlPullParserException: precondition: START_TAG (position:END_TAG </text>@9:2535 in java.io.InputStreamReader@44c6d0d8)
04-08 18:32:09.827: W/System.err(688): at org.kxml2.io.KXmlParser.exception(KXmlParser.java:245)
04-08 18:32:09.827: W/System.err(688): at org.kxml2.io.KXmlParser.nextText(KXmlParser.java:1382)
04-08 18:32:09.827: W/System.err(688): at utilities.XMLParserHelper.parseBlogEntries(XMLParserHelper.java:139)
04-08 18:32:09.827: W/System.err(688): at serviceclients.PlayerSummaryAsyncTask.doInBackground(PlayerSummaryAsyncTask.java:68)
04-08 18:32:09.827: W/System.err(688): at serviceclients.PlayerSummaryAsyncTask.doInBackground(PlayerSummaryAsyncTask.java:1)
04-08 18:32:09.836: W/System.err(688): at android.os.AsyncTask$2.call(AsyncTask.java:185)
04-08 18:32:09.836: W/System.err(688): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
04-08 18:32:09.836: W/System.err(688): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
04-08 18:32:09.836: W/System.err(688): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
04-08 18:32:09.836: W/System.err(688): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
04-08 18:32:09.836: W/System.err(688): at java.lang.Thread.run(Thread.java:1096)
Я надеюсь, что прояснил свою проблему.
Решение
Вдохновленный подходом Martin Nordholts преобразованию полученных данных сначала в строку, я решил свою проблему в виде смешанного подхода.
Преобразуйте полученное значение InputStream в строку и замените ошибочные символы на * (или все, что пожелаете):
InputStreamReader isr = new InputStreamReader(serviceReturnedStream); BufferedReader br = new BufferedReader(isr); StringBuilder xmlAsString = new StringBuilder(512); String line; try { while ((line = br.readLine()) != null) { xmlAsString.append(line.replace("<<", "*").replace(">>", "*")); } } catch (IOException e) { e.printStackTrace(); }
Теперь у меня есть строка, которая содержит правильные данные XML (для моего случая), так что просто используйте обычный XmlPullParser для его анализа вместо того, чтобы анализировать его вручную:
XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); factory.setNamespaceAware(false); XmlPullParser parser = factory.newPullParser(); parser.setInput(new StringReader(xmlAsString.toString()));
Надеюсь, это поможет кому-то!
1 ответ
Да, исключение, вероятно, выдается, потому что это недопустимый XML согласно разделу 2.4 Символьные данные и разметка в спецификации XML 1.0:
[...] левая угловая скобка (<) НЕ ДОЛЖНА появляться в [своей] буквальной форме, [...]
Если вы поместите этот XML в Eclipse, Eclipse будет жаловаться на то, что XML недействителен. Если вы можете исправить веб-службу, вы должны исправить сгенерированный XML, либо используя ссылки на сущности, такие как <
или с помощью CDATA.
Если вы не обладаете властью над веб-службой, я думаю, что проще всего будет проанализировать это вручную с помощью некоторого пользовательского кода, возможно, с использованием регулярных выражений, в зависимости от того, насколько непринужденными являются требования общности.
Пример кода
Вот как вы можете разобрать файл XML выше. Обратите внимание, что вы, вероятно, хотите улучшить этот код, чтобы сделать его более общим, но у вас должно быть что-то для начала, по крайней мере:
// Read the XML into a StringBuilder so we can get get a Matcher for the
// whole XML
InputStream xmlResponseInputStream = // Get InputStream to XML somehow
InputStreamReader isr = new InputStreamReader(xmlResponseInputStream);
BufferedReader br = new BufferedReader(isr);
StringBuilder xmlAsString = new StringBuilder(512);
String line;
try {
while ((line = br.readLine()) != null) {
xmlAsString.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
// Look for links using a regex. Assume the first link is "Prev" and the
// next link is "Next"
Pattern hrefRegex = Pattern.compile("<a href=\"([^\"]*)\">");
Matcher m = hrefRegex.matcher(xmlAsString);
String linkToPrevPost = null;
String linkToNextPost = null;
while (m.find()) {
String hrefValue = m.group(1);
if (linkToPrevPost == null) {
linkToPrevPost = hrefValue;
} else {
linkToNextPost = hrefValue;
}
}
Log.i("Example", "'Prev' link = " + linkToPrevPost +
" 'Next' link = " + linkToNextPost);
С вашим XML-файлом вывод в logcat будет
I/Example (12399): 'Prev' link = link-to-prev-post 'Next' link = link-to-next-post