Android解析OpenWeatherMap XML数据时触发XmlPullParserException崩溃
问题
调用OpenWeatherMap API获取天气数据,使用XmlPullParser循环解析XML结果时,程序在即将处理timezone标签时崩溃。日志显示已成功解析current、city、coord、country标签,随后报错:
Caused by: java.lang.RuntimeException: org.xmlpull.v1.XmlPullParserException: unexpected type (position:TEXT CA@2:99 in java.io.InputStreamReader@59ac190)
已排除UTF-8 BOM导致问题的可能,求故障原因及解决办法。
相关代码及返回XML结构如下:
原代码
URL url = new URL("https://api.openweathermap.org/data/2.5/weather?q=toronto,ca&APPID=***&mode=xml&units=metric"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(10000 /* milliseconds */); conn.setConnectTimeout(15000 /* milliseconds */); conn.setRequestMethod("GET"); conn.setDoInput(true); // Starts the query. conn.connect(); InputStream is = conn.getInputStream(); XmlPullParser parser = Xml.newPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); parser.setInput(is, null); while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { if(parser.getEventType() == XmlPullParser.START_TAG) { String loopTag = parser.getName(); Log.i("weather", loopTag); if (loopTag.equals("temperature")) { min = parser.getAttributeValue(null, "min"); max = parser.getAttributeValue(null, "max"); curr = parser.getAttributeValue(null, "value"); } else if (loopTag.equals("weather")) { icon = parser.getAttributeValue(null, "icon"); } } parser.nextTag(); }
返回的XML数据
<current> <city id="6167865" name="Toronto"> <coord lon="-79.4163" lat="43.7001"/> <country>CA</country> <timezone>-14400</timezone> <sun rise="2023-07-12T09:46:54" set="2023-07-13T00:59:13"/> </city> <temperature value="20.57" min="19.15" max="21.94" unit="celsius"/> <feels_like value="20.28" unit="celsius"/> <humidity value="61" unit="%"/> <pressure value="1013" unit="hPa"/> <wind> <speed value="1.79" unit="m/s" name="Light breeze"/> <gusts value="4.47"/> <direction value="314" code="NW" name="Northwest"/> </wind> <clouds value="98" name="overcast clouds"/> <visibility value="10000"/> <precipitation mode="no"/> <weather number="804" value="overcast clouds" icon="04d"/> <lastupdate value="2023-07-12T15:07:14"/> </current>
故障原因与解决方案
核心原因
代码中使用的parser.nextTag()方法只会跳过到下一个标签(START_TAG或END_TAG),但会忽略标签之间的文本节点(比如country标签内的CA文本)。当解析完country的START_TAG后,调用nextTag()会尝试跳过CA文本直接查找下一个标签,而nextTag()在遇到TEXT类型节点时会直接抛出异常,这就是报错的根源。
解决办法
将循环里的parser.nextTag()替换为parser.next(),该方法会处理所有XML事件类型(包括TEXT、END_TAG等),确保不会因跳过非标签节点导致异常。
修改后的代码示例
URL url = new URL("https://api.openweathermap.org/data/2.5/weather?q=toronto,ca&APPID=***&mode=xml&units=metric"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(10000 /* milliseconds */); conn.setConnectTimeout(15000 /* milliseconds */); conn.setRequestMethod("GET"); conn.setDoInput(true); conn.connect(); InputStream is = conn.getInputStream(); XmlPullParser parser = Xml.newPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); parser.setInput(is, null); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if(eventType == XmlPullParser.START_TAG) { String loopTag = parser.getName(); Log.i("weather", loopTag); if (loopTag.equals("temperature")) { min = parser.getAttributeValue(null, "min"); max = parser.getAttributeValue(null, "max"); curr = parser.getAttributeValue(null, "value"); } else if (loopTag.equals("weather")) { icon = parser.getAttributeValue(null, "icon"); } } // 使用next()替代nextTag(),处理所有事件类型 eventType = parser.next(); }
额外说明
如果需要读取标签内的文本内容(比如timezone的-14400),可以在遇到对应START_TAG后,调用parser.nextText()获取文本,该方法会自动跳过到对应的END_TAG,示例如下:
if (loopTag.equals("timezone")) { String timezoneValue = parser.nextText(); Log.i("weather", "timezone: " + timezoneValue); }
内容的提问来源于stack exchange,提问作者1011 1110

