package fr.tvbarthel.apps.simplethermometer.openweathermap; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; /** * A simple parser used to retrieve data of * an xml flux from the OpenWeatherMap Api. */ public class OpenWeatherMapParser { /* * A typical xml response from http://api.openweathermap.org/data/2.5/weather?lat=35&lon=139&mode=xml&units=metric * * <current> * <city id="1851632" name="Shuzenji"> * <coord lon="139" lat="35"/> * <country>JP</country> * <sun rise="2013-09-11T20:24:53" set="2013-09-12T08:55:26"/> * </city> * <temperature value="21.47" min="21.47" max="21.47" unit="celsius"/> * <humidity value="100" unit="%"/> * <pressure value="1005.09" unit="hPa"/> * <wind> * <speed value="1.96" name="Light breeze"/> * <direction value="42.0003" code="NE" name="NorthEast"/> * </wind> * <clouds value="8" name="sky is clear"/> * <precipitation mode="no"/> * <weather number="800" value="sky is clear" icon="02n"/> * <lastupdate value="2013-09-12T17:32:19"/> * </current> */ //Names and Attributes used in the openWeatherMap XML public static final String NAME_ROOT = "current"; public static final String NAME_CITY = "city"; public static final String ATTRIBUTE_CITY_ID = "id"; public static final String ATTRIBUTE_CITY_NAME = "name"; public static final String NAME_COORDINATE = "coord"; public static final String ATTRIBUTE_LONGITUDE = "lon"; public static final String ATTRIBUTE_LATITUDE = "lat"; public static final String NAME_COUNTRY = "country"; public static final String NAME_SUN = "sun"; public static final String ATTRIBUTE_SUN_RISE = "rise"; public static final String ATTRIBUTE_SUN_SET = "set"; public static final String NAME_TEMPERATURE = "temperature"; public static final String ATTRIBUTE_TEMPERATURE_VALUE = "value"; public static final String ATTRIBUTE_TEMPERATURE_MIN = "min"; public static final String ATTRIBUTE_TEMPERATURE_MAX = "max"; public static final String ATTRIBUTE_TEMPERATURE_UNIT = "unit"; public static final String NAME_HUMDITY = "humidity"; public static final String ATTRIBUTE_HUMIDITY_VALUE = "value"; public static final String ATTRIBUTE_HUMIDITY_UNIT = "unit"; public static final String NAME_PRESSURE = "pressure"; public static final String ATTRIBUTE_PRESSURE_VALUE = "value"; public static final String ATTRIBUTE_PRESSURE_UNIT = "unit"; public static final String NAME_WIND = "wind"; public static final String NAME_WIND_SPEED = "speed"; public static final String ATTRIBUTE_WIND_SPEED_VALUE = "value"; public static final String ATTRIBUTE_WIND_SPEED_NAME = "name"; public static final String NAME_WIND_DIRECTION = "direction"; public static final String ATTRIBUTE_WIND_DIRECTION_VALUE = "value"; public static final String ATTRIBUTE_WIND_DIRECTION_CODE = "code"; public static final String ATTRIBUTE_WIND_DIRECTION_NAME = "name"; public static final String NAME_CLOUD = "clouds"; public static final String ATTRIBUTE_CLOUD_VALUE = "value"; public static final String ATTRIBUTE_CLOUD_NAME = "name"; public static final String NAME_PRECIPITATION = "precipitation"; public static final String ATTRIBUTE_PRECIPITATION_MODE = "mode"; public static final String NAME_WEATHER = "weather"; public static final String ATTRIBUTE_WEATHER_NUMBER = "number"; public static final String ATTRIBUTE_WEATHER_VALUE = "value"; public static final String ATTRIBUTE_WEATHER_ICON = "icon"; public static final String NAME_LAST_UPDATE = "lastupdate"; public static final String ATTIBUTE_LAST_UPDATE_VALUE = "value"; private static final String NAME_SPACE = null; /** * Parse {@code in} a {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * * @param in {@link java.io.InputStream} * @return {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * @throws XmlPullParserException * @throws IOException */ public OpenWeatherMapParserResult parse(InputStream in) throws XmlPullParserException, IOException { try { XmlPullParser parser = Xml.newPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); parser.setInput(in, null); parser.nextTag(); return readOpenWeatherMap(parser); } finally { in.close(); } } /** * Read an {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * from {@code parser} * * @param parser {@link org.xmlpull.v1.XmlPullParser} * @return {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * @throws XmlPullParserException * @throws IOException */ private OpenWeatherMapParserResult readOpenWeatherMap(XmlPullParser parser) throws XmlPullParserException, IOException { OpenWeatherMapParserResult result = new OpenWeatherMapParserResult(); parser.require(XmlPullParser.START_TAG, NAME_SPACE, NAME_ROOT); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) { continue; } final String name = parser.getName(); if (name.equals(NAME_CITY)) { readCity(parser, result); } else if (name.equals(NAME_TEMPERATURE)) { readTemperature(parser, result); } else if (name.equals(NAME_HUMDITY)) { readHumidity(parser, result); } else if (name.equals(NAME_PRESSURE)) { readPressure(parser, result); } else if (name.equals(NAME_WIND)) { readWind(parser, result); } else if (name.equals(NAME_CLOUD)) { readCloud(parser, result); } else if (name.equals(NAME_PRECIPITATION)) { readPrecipitation(parser, result); } else if (name.equals(NAME_WEATHER)) { readWeather(parser, result); } else if (name.equals(NAME_LAST_UPDATE)) { readLastUpdate(parser, result); } else { skip(parser); } } return result; } /** * Read the precipitation information from {@code parser} and set them in {@code result} * * @param parser {@link org.xmlpull.v1.XmlPullParser} * @param result {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * @throws XmlPullParserException * @throws IOException */ private void readPrecipitation(XmlPullParser parser, OpenWeatherMapParserResult result) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, NAME_SPACE, NAME_PRECIPITATION); result.setPrecipitationMode(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_PRECIPITATION_MODE)); parser.nextTag(); parser.require(XmlPullParser.END_TAG, NAME_SPACE, NAME_PRECIPITATION); } /** * Read the cloud information from {@code parser} and set them in {@code result} * * @param parser {@link org.xmlpull.v1.XmlPullParser} * @param result {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * @throws XmlPullParserException * @throws IOException */ private void readCloud(XmlPullParser parser, OpenWeatherMapParserResult result) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, NAME_SPACE, NAME_CLOUD); result.setCloudValue(Float.valueOf(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_CLOUD_VALUE))); result.setCloudName(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_CLOUD_NAME)); parser.nextTag(); parser.require(XmlPullParser.END_TAG, NAME_SPACE, NAME_CLOUD); } /** * Read the pressure information from {@code parser} and set them in {@code result} * * @param parser {@link org.xmlpull.v1.XmlPullParser} * @param result {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * @throws XmlPullParserException * @throws IOException */ private void readPressure(XmlPullParser parser, OpenWeatherMapParserResult result) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, NAME_SPACE, NAME_PRESSURE); result.setPressureValue(Float.valueOf(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_PRESSURE_VALUE))); result.setPressureUnit(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_PRESSURE_UNIT)); parser.nextTag(); parser.require(XmlPullParser.END_TAG, NAME_SPACE, NAME_PRESSURE); } /** * Read the humidity information from {@code parser} and set them in {@code result} * * @param parser {@link org.xmlpull.v1.XmlPullParser} * @param result {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * @throws XmlPullParserException * @throws IOException */ private void readHumidity(XmlPullParser parser, OpenWeatherMapParserResult result) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, NAME_SPACE, NAME_HUMDITY); result.setHumidityValue(Float.valueOf(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_HUMIDITY_VALUE))); result.setHumidityUnit(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_HUMIDITY_UNIT)); parser.nextTag(); parser.require(XmlPullParser.END_TAG, NAME_SPACE, NAME_HUMDITY); } /** * Read the weather information from {@code parser} and set them in {@code result} * * @param parser {@link org.xmlpull.v1.XmlPullParser} * @param result {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * @throws XmlPullParserException * @throws IOException */ private void readWeather(XmlPullParser parser, OpenWeatherMapParserResult result) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, NAME_SPACE, NAME_WEATHER); result.setWeatherValue(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_WEATHER_VALUE)); result.setWeatherNumber(Integer.valueOf(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_WEATHER_NUMBER))); result.setWeatherIcon(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_WEATHER_ICON)); parser.nextTag(); parser.require(XmlPullParser.END_TAG, NAME_SPACE, NAME_WEATHER); } /** * Read the last update information from {@code parser} and set them in {@code result} * * @param parser {@link org.xmlpull.v1.XmlPullParser} * @param result {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * @throws XmlPullParserException * @throws IOException */ private void readLastUpdate(XmlPullParser parser, OpenWeatherMapParserResult result) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, NAME_SPACE, NAME_LAST_UPDATE); result.setLastUpdate(parser.getAttributeValue(NAME_SPACE, ATTIBUTE_LAST_UPDATE_VALUE)); parser.nextTag(); parser.require(XmlPullParser.END_TAG, NAME_SPACE, NAME_LAST_UPDATE); } /** * Read the temperature information from {@code parser} and set them in {@code result} * * @param parser {@link org.xmlpull.v1.XmlPullParser} * @param result {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * @throws XmlPullParserException * @throws IOException */ private void readTemperature(XmlPullParser parser, OpenWeatherMapParserResult result) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, NAME_SPACE, NAME_TEMPERATURE); result.setTemperatureValue(Float.valueOf(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_TEMPERATURE_VALUE))); result.setTemperatureMax(Float.valueOf(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_TEMPERATURE_MAX))); result.setTemperatureMin(Float.valueOf(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_TEMPERATURE_MIN))); result.setTemperatureUnit(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_TEMPERATURE_UNIT)); parser.nextTag(); parser.require(XmlPullParser.END_TAG, NAME_SPACE, NAME_TEMPERATURE); } /** * Read the wind information from {@code parser} and set them in {@code result} * * @param parser {@link org.xmlpull.v1.XmlPullParser} * @param result {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * @throws XmlPullParserException * @throws IOException */ private void readWind(XmlPullParser parser, OpenWeatherMapParserResult result) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, NAME_SPACE, NAME_WIND); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) { continue; } final String name = parser.getName(); if (name.equals(NAME_WIND_SPEED)) { result.setWindSpeedValue(Float.valueOf(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_WIND_SPEED_VALUE))); result.setWindSpeedName(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_WIND_SPEED_NAME)); parser.nextTag(); } else if (name.equals(NAME_WIND_DIRECTION)) { result.setWindDirectionValue(Float.valueOf(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_WIND_DIRECTION_VALUE))); result.setWindDirectionCode(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_WIND_DIRECTION_CODE)); result.setWindDirectionName(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_WIND_DIRECTION_NAME)); parser.nextTag(); } else { skip(parser); } } } /** * Read the city information from {@code parser} and set them in {@code result} * * @param parser {@link org.xmlpull.v1.XmlPullParser} * @param result {@link fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult} * @throws XmlPullParserException * @throws IOException */ private void readCity(XmlPullParser parser, OpenWeatherMapParserResult result) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, NAME_SPACE, NAME_CITY); result.setCityId(Integer.valueOf(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_CITY_ID))); result.setCityName(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_CITY_NAME)); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) { continue; } final String name = parser.getName(); if (name.equals(NAME_COORDINATE)) { result.setLongitude(Float.valueOf(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_LONGITUDE))); result.setLatitude(Float.valueOf(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_LATITUDE))); parser.nextTag(); } else if (name.equals(NAME_COUNTRY)) { parser.require(XmlPullParser.START_TAG, NAME_SPACE, NAME_COUNTRY); result.setCountry(readText(parser)); parser.require(XmlPullParser.END_TAG, NAME_SPACE, NAME_COUNTRY); } else if (name.equals(NAME_SUN)) { result.setSunRise(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_SUN_RISE)); result.setSunSet(parser.getAttributeValue(NAME_SPACE, ATTRIBUTE_SUN_SET)); parser.nextTag(); } else { skip(parser); } } } private String readText(XmlPullParser parser) throws IOException, XmlPullParserException { String result = ""; if (parser.next() == XmlPullParser.TEXT) { result = parser.getText(); parser.nextTag(); } return result; } private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { if (parser.getEventType() != XmlPullParser.START_TAG) { throw new IllegalStateException(); } int depth = 1; while (depth != 0) { switch (parser.next()) { case XmlPullParser.END_TAG: depth--; break; case XmlPullParser.START_TAG: depth++; break; } } } }