【问题标题】:Parsing nested elements with Spring Boot RestTemplate gives JSON parse error使用 Spring Boot RestTemplate 解析嵌套元素会导致 JSON 解析错误
【发布时间】:2017-10-17 15:46:05
【问题描述】:

我想使用一个提供以下输出的 REST 接口(准确地说,我想检索 TAF 元素列表):

<?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>

我的 POJO 类如下所示:

@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
}

现在我的服务有一个简单的方法:

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

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

如果我忽略嵌套的 forecast 元素,一切都会正确解析。但我需要那些嵌套元素,如果我按原样运行所有内容,我会得到以下异常:

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])

解析器似乎看到了forecast 的第一个子元素,即fcst_time_from 获取其内容并尝试将日期反序列化为Forecast 对象。我不知道为什么。也许我错过了一些简单的东西?提前感谢您的帮助。

更新

我离问题更近了一点。嵌套的TAF 元素在response 元素中被正确解析,因为它们被包裹在data 元素中。不幸的是,TAF 内的forecast 标签列表没有包装元素。我为Forecast 类创建了一个带有String 参数的构造函数,现在它将嵌入的元素(fcst_time_from、fcst_time_to 等)解析为Forcast 对象。有没有办法声明这个内部列表没有包装元素?

更新 2

经过一些测试,我发现问题与解析未包装的元素列表密切相关。我不知道为什么,但是如果我像这样声明我的 Result 类,而不是上面显示的声明:

@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;
    }
}

它不起作用。不解析任何 TAF 元素。似乎解析器正在强制包装所有列表。不幸的是,我在 REST 输出中打开了 forecast 列表。我该如何处理?我看到@Element(inline = true)注解的参数,但是@XmlElement没有这样的东西。我可以以某种方式强制 RestTemplate 本身中的展开列表吗?

【问题讨论】:

  • 请先修复主要问题,然后我们可以看到...主要问题是:您的 RestTemplate 无法将 '2017-10-17T12:00:00Z' 解析/反序列化为 'java.utill 。日期'!解决方法参考:stackoverflow.com/q/21355450/592355(ObjectMapper.setDateFormat()!)
  • @xerx593 不,不是这样。它将 '2017-10-17T12:00:00Z' 解析为 'java.utill.Date' 没有问题。当我注释掉 List 预测时,Taf 对象被正确解析(并且它们包含日期字段)。此错误表示它尝试将此日期字符串反序列化为 Forecast 对象。

标签: java xml spring rest


【解决方案1】:

天哪

两天的努力发现我需要一条线来解决这个问题。所以......从解析器的角度来看,所有这些 @XmlElement 注释似乎都是无用的。仅当我想将对象作为 REST 服务输出时才使用它们。为了告诉解析器我需要使用 @JacksonXmlElementWrapper 注释。确切的解决方案是:

@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
}

@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
}

就是这样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-09
    • 1970-01-01
    • 2017-11-18
    • 2011-10-30
    • 2016-09-02
    • 2019-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多