1

I want to consume a REST interface that gives the following output (to be precise, I want to retrieve the list of TAF elements):

<?xml version="1.0" encoding="UTF-8"?>
<response xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XML-Schema-instance" version="1.2" xsi:noNamespaceSchemaLocation="http://aviationweather.gov/adds/schema/taf1_2.xsd">
  <request_index>32084191</request_index>
  <data_source name="tafs" />
  <request type="retrieve" />
  <errors />
  <warnings />
  <time_taken_ms>13</time_taken_ms>
  <data num_results="4">
    <TAF>
      <raw_text>TAF EPGD 171130Z 1712/1812 25008KT CAVOK PROB40 1718/1806 1500 BR BKN005</raw_text>
      <station_id>EPGD</station_id>
      <issue_time>2017-10-17T11:30:00Z</issue_time>
      <bulletin_time>2017-10-17T11:00:00Z</bulletin_time>
      <valid_time_from>2017-10-17T12:00:00Z</valid_time_from>
      <valid_time_to>2017-10-18T12:00:00Z</valid_time_to>
      <latitude>54.37</latitude>
      <longitude>18.47</longitude>
      <elevation_m>138.0</elevation_m>
      <forecast>
        <fcst_time_from>2017-10-17T12:00:00Z</fcst_time_from>
        <fcst_time_to>2017-10-18T12:00:00Z</fcst_time_to>
        <wind_dir_degrees>250</wind_dir_degrees>
        <wind_speed_kt>8</wind_speed_kt>
        <visibility_statute_mi>6.21</visibility_statute_mi>
        <wx_string>NSW</wx_string>
        <sky_condition sky_cover="NSC" />
      </forecast>
      <forecast>
        <fcst_time_from>2017-10-17T18:00:00Z</fcst_time_from>
        <fcst_time_to>2017-10-18T06:00:00Z</fcst_time_to>
        <change_indicator>PROB</change_indicator>
        <probability>40</probability>
        <visibility_statute_mi>0.93</visibility_statute_mi>
        <wx_string>BR</wx_string>
        <sky_condition sky_cover="BKN" cloud_base_ft_agl="500" />
      </forecast>
    </TAF>
    <TAF>
      <raw_text>TAF EPGD 170530Z 1706/1806 22010KT 0300 FG BKN002 BECMG 1707/1709 CAVOK</raw_text>
      <station_id>EPGD</station_id>
      <issue_time>2017-10-17T05:30:00Z</issue_time>
      <bulletin_time>2017-10-17T05:00:00Z</bulletin_time>
      <valid_time_from>2017-10-17T06:00:00Z</valid_time_from>
      <valid_time_to>2017-10-18T06:00:00Z</valid_time_to>
      <latitude>54.37</latitude>
      <longitude>18.47</longitude>
      <elevation_m>138.0</elevation_m>
      <forecast>
        <fcst_time_from>2017-10-17T06:00:00Z</fcst_time_from>
        <fcst_time_to>2017-10-17T07:00:00Z</fcst_time_to>
        <wind_dir_degrees>220</wind_dir_degrees>
        <wind_speed_kt>10</wind_speed_kt>
        <visibility_statute_mi>0.19</visibility_statute_mi>
        <wx_string>FG</wx_string>
        <sky_condition sky_cover="BKN" cloud_base_ft_agl="200" />
      </forecast>
      <forecast>
        <fcst_time_from>2017-10-17T07:00:00Z</fcst_time_from>
        <fcst_time_to>2017-10-18T06:00:00Z</fcst_time_to>
        <change_indicator>BECMG</change_indicator>
        <time_becoming>2017-10-17T09:00:00Z</time_becoming>
        <wind_dir_degrees>220</wind_dir_degrees>
        <wind_speed_kt>10</wind_speed_kt>
        <visibility_statute_mi>6.21</visibility_statute_mi>
        <wx_string>NSW</wx_string>
        <sky_condition sky_cover="NSC" />
      </forecast>
    </TAF>
    <TAF>
      <raw_text>TAF EPGD 162330Z 1700/1724 24006KT 7000 SCT012 TEMPO 1700/1708 BKN005 PROB40 1702/1707 1000 BR OVC002</raw_text>
      <station_id>EPGD</station_id>
      <issue_time>2017-10-16T23:30:00Z</issue_time>
      <bulletin_time>2017-10-16T23:00:00Z</bulletin_time>
      <valid_time_from>2017-10-17T00:00:00Z</valid_time_from>
      <valid_time_to>2017-10-18T00:00:00Z</valid_time_to>
      <latitude>54.37</latitude>
      <longitude>18.47</longitude>
      <elevation_m>138.0</elevation_m>
      <forecast>
        <fcst_time_from>2017-10-17T00:00:00Z</fcst_time_from>
        <fcst_time_to>2017-10-17T08:00:00Z</fcst_time_to>
        <change_indicator>TEMPO</change_indicator>
        <sky_condition sky_cover="BKN" cloud_base_ft_agl="500" />
      </forecast>
      <forecast>
        <fcst_time_from>2017-10-17T00:00:00Z</fcst_time_from>
        <fcst_time_to>2017-10-18T00:00:00Z</fcst_time_to>
        <wind_dir_degrees>240</wind_dir_degrees>
        <wind_speed_kt>6</wind_speed_kt>
        <visibility_statute_mi>4.35</visibility_statute_mi>
        <sky_condition sky_cover="SCT" cloud_base_ft_agl="1200" />
      </forecast>
      <forecast>
        <fcst_time_from>2017-10-17T02:00:00Z</fcst_time_from>
        <fcst_time_to>2017-10-17T07:00:00Z</fcst_time_to>
        <change_indicator>PROB</change_indicator>
        <probability>40</probability>
        <visibility_statute_mi>0.62</visibility_statute_mi>
        <wx_string>BR</wx_string>
        <sky_condition sky_cover="OVC" cloud_base_ft_agl="200" />
      </forecast>
    </TAF>
    <TAF>
      <raw_text>TAF EPGD 161730Z 1618/1718 24006KT CAVOK BECMG 1618/1620 BKN013 TEMPO 1621/1708 BKN005 PROB40 1700/1707 2000 BR OVC002 BECMG 1709/1711 SCT030</raw_text>
      <station_id>EPGD</station_id>
      <issue_time>2017-10-16T17:30:00Z</issue_time>
      <bulletin_time>2017-10-16T17:00:00Z</bulletin_time>
      <valid_time_from>2017-10-16T18:00:00Z</valid_time_from>
      <valid_time_to>2017-10-17T18:00:00Z</valid_time_to>
      <latitude>54.37</latitude>
      <longitude>18.47</longitude>
      <elevation_m>138.0</elevation_m>
      <forecast>
        <fcst_time_from>2017-10-16T18:00:00Z</fcst_time_from>
        <fcst_time_to>2017-10-17T18:00:00Z</fcst_time_to>
        <wind_dir_degrees>240</wind_dir_degrees>
        <wind_speed_kt>6</wind_speed_kt>
        <visibility_statute_mi>6.21</visibility_statute_mi>
        <wx_string>NSW</wx_string>
        <sky_condition sky_cover="NSC" />
      </forecast>
      <forecast>
        <fcst_time_from>2017-10-16T18:00:00Z</fcst_time_from>
        <fcst_time_to>2017-10-17T09:00:00Z</fcst_time_to>
        <change_indicator>BECMG</change_indicator>
        <time_becoming>2017-10-16T20:00:00Z</time_becoming>
        <wind_dir_degrees>240</wind_dir_degrees>
        <wind_speed_kt>6</wind_speed_kt>
        <visibility_statute_mi>6.21</visibility_statute_mi>
        <wx_string>NSW</wx_string>
        <sky_condition sky_cover="BKN" cloud_base_ft_agl="1300" />
      </forecast>
      <forecast>
        <fcst_time_from>2017-10-16T21:00:00Z</fcst_time_from>
        <fcst_time_to>2017-10-17T08:00:00Z</fcst_time_to>
        <change_indicator>TEMPO</change_indicator>
        <sky_condition sky_cover="BKN" cloud_base_ft_agl="500" />
      </forecast>
      <forecast>
        <fcst_time_from>2017-10-17T00:00:00Z</fcst_time_from>
        <fcst_time_to>2017-10-17T07:00:00Z</fcst_time_to>
        <change_indicator>PROB</change_indicator>
        <probability>40</probability>
        <visibility_statute_mi>1.24</visibility_statute_mi>
        <wx_string>BR</wx_string>
        <sky_condition sky_cover="OVC" cloud_base_ft_agl="200" />
      </forecast>
      <forecast>
        <fcst_time_from>2017-10-17T09:00:00Z</fcst_time_from>
        <fcst_time_to>2017-10-17T18:00:00Z</fcst_time_to>
        <change_indicator>BECMG</change_indicator>
        <time_becoming>2017-10-17T11:00:00Z</time_becoming>
        <wind_dir_degrees>240</wind_dir_degrees>
        <wind_speed_kt>6</wind_speed_kt>
        <visibility_statute_mi>6.21</visibility_statute_mi>
        <wx_string>NSW</wx_string>
        <sky_condition sky_cover="SCT" cloud_base_ft_agl="3000" />
      </forecast>
    </TAF>
  </data>
</response>

My POJO Classes look like this:

@XmlRootElement(name = "response")
public class Response {
    @XmlElement private List<Taf> data; // I don't need any other contents of this element

    public List<Taf> getData() {
        return data;
    }

    public void setData(List<Taf> data) {
        this.data = data;
    }
}

@XmlRootElement(name = "TAF")
public class Taf {
    @XmlElement(name = "raw_text") private String raw_text;
    @XmlElement(name = "station_id") private String station_id; // ICAO
    @XmlElement(name = "issue_time") private Date issue_time;
    @XmlElement(name = "bulletin_time") private Date bulletin_time;
    @XmlElement(name = "valid_time_from") private Date valid_time_from;
    @XmlElement(name = "valid_time_to") private Date valid_time_to;
    @XmlElement(name = "remarks") private String remarks;
    @XmlElement(name = "latitude") private Double latitude;
    @XmlElement(name = "longitude") private Double longitude;
    @XmlElement(name = "elevation_m") private Double elevation_m;
    @XmlElement(name = "forecast", type = Forecast.class) private List<Forecast> forecast;
    // getters and setters
}

@XmlRootElement(name = "forecast")
public class Forecast {
    @XmlElement(name = "fcst_time_from") private Date fcst_time_from;
    @XmlElement(name = "fcst_time_to") private Date fcst_time_to;
    @XmlElement(name = "wind_dir_degrees") private Integer wind_dir_degrees;
    @XmlElement(name = "wind_speed_kt") private Integer wind_speed_kt;
    @XmlElement(name = "visibility_statute_mi") private Double visibility_statute_mi;
    @XmlElement(name = "wx_string") private String wx_string;
    @XmlElement(name = "sky_condition", type = SkyCondition.class) private List<SkyCondition> sky_condition;
    // getters and setters
}

@XmlRootElement(name = "sky_condition")
public class SkyCondition {
    @XmlAttribute(name = "sky_cover") private String sky_cover;
    @XmlAttribute(name = "cloud_base_ft_agl") private Integer cloud_base_ft_agl;
    // getters and setters
}

Now my service has a simple method:

public List<Taf> getTafsForICAO(String icao) {
    RestTemplate restTemplate = new RestTemplate();

    Response resp = restTemplate.getForObject(tafUrl+icao, Response.class);
    return resp.getData();
}

If I ignore the nested forecast elements everything parses correctly. But I need those nested elements, and If I run everything as it is, I get the following exception:

Failed to read HTTP message:
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error:
Can not construct instance of airports.models.taf.Forecast:
no String-argument constructor/factory method to deserialize from String value ('2017-10-17T12:00:00Z'); nested exception is 
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of airports.models.taf.Forecast: no String-argument constructor/factory method to deserialize from String value ('2017-10-17T12:00:00Z')
at [Source: java.io.PushbackInputStream@4c723aba; line: 21, column: 45] (through reference chain: airports.models.taf.Response["data"]->java.util.ArrayList[0]->airports.models.taf.Taf["forecast"]->java.util.ArrayList[0])

It looks like the parser saw the first subelement of forecast, which is fcst_time_from took its contents and tried to deserialize the date into Forecast object. I don't know why. Maybe I'm missing something simple? Thanks in advance for any help.

UPDATE

I am a bit closer to the problem. The nested TAF elements are parsed properly inside response element because they are wrapped in data element. Unfortunately, there is no wrapping element for list of forecast tags inside TAF. I created a constructor with String argument for Forecast class and now it parses the embedded elements (fcst_time_from, fcst_time_to and so on) as Forcast objects. Is there a way to declare that this inner list has no wrapping element?

UPDATE 2

After some tests I figured that the problewm is strictly related to parsing unwrapped lists of elements. I don't know why, but if I declare my Result class like this, instead of the declaration shown higher:

@XmlRootElement(name = "response")
public class Response {
    @XmlElementWrapper(name = "data")
    @XmlElement(name = "TAF")
    private List<Taf> tafs;

    public List<Taf> getTafs() {
        return tafs;
    }

    public void setTafs(List<Taf> tafs) {
        this.tafs = tafs;
    }
}

It does not work. No TAF elements are parsed. It seems like the parser is forcing all the lists to be wrapped. Unfortunately I get unwrapped forecast lists in the REST output. How can I deal with this? I saw a parameter for @Element(inline = true) annotation, but @XmlElement doesn't have such a thing. Can I somehow force unwrapped lists in the RestTemplate itself?

Yaerius
  • 231
  • 2
  • 7
  • 1
    Please fix the primary problem first, then we can see... The primary problem is: Your RestTemplate cannot parse/deserialize '2017-10-17T12:00:00Z' as a 'java.utill.Date'! For the solution refer to: https://stackoverflow.com/q/21355450/592355 (`ObjectMapper.setDateFormat()`!) – xerx593 Oct 17 '17 at 16:23
  • @xerx593 No, it's not that. It parses '2017-10-17T12:00:00Z' as a 'java.utill.Date' with no problem. When I comment out the List forecast the Taf objects are parsed properly (and they contain Date fields). This error says that it tries do deserialize this date string into Forecast object. – Yaerius Oct 18 '17 at 08:01

1 Answers1

2

OMG

Two days of struggle to find out I need one line to solve this. So... it seems all those @XmlElement annotations are useless from the parser's perspective. They are used only when I want to output the objects back as a REST service. In order to tell anything to parser I needed to use @JacksonXmlElementWrapper annotation. The exact solution is:

@XmlRootElement(name = "TAF")
public class Taf {
    @XmlElement(name = "raw_text") private String raw_text;
    @XmlElement(name = "station_id") private String station_id; // ICAO
    @XmlElement(name = "issue_time") private Date issue_time;
    @XmlElement(name = "bulletin_time") private Date bulletin_time;
    @XmlElement(name = "valid_time_from") private Date valid_time_from;
    @XmlElement(name = "valid_time_to") private Date valid_time_to;
    @XmlElement(name = "remarks") private String remarks;
    @XmlElement(name = "latitude") private Double latitude;
    @XmlElement(name = "longitude") private Double longitude;
    @XmlElement(name = "elevation_m") private Double elevation_m;
    @JacksonXmlElementWrapper(useWrapping = false)
    @XmlElement(name = "forecast") private List<Forecast> forecast;
    // getters and setters
}

and

@XmlRootElement(name = "forecast")
public class Forecast {
    @XmlElement(name = "fcst_time_from") private Date fcst_time_from;
    @XmlElement(name = "fcst_time_to") private Date fcst_time_to;
    @XmlElement(name = "wind_dir_degrees") private Integer wind_dir_degrees;
    @XmlElement(name = "wind_speed_kt") private Integer wind_speed_kt;
    @XmlElement(name = "visibility_statute_mi") private Double visibility_statute_mi;
    @XmlElement(name = "ex_string") private String wx_string;
    @JacksonXmlElementWrapper(useWrapping = false)
    @XmlElement(name = "sky_condition") private List<SkyCondition> sky_condition;
    // getters and setters
}

That's it.

Yaerius
  • 231
  • 2
  • 7