I would like to parse XML file to the object. I read some articles, tutorials and I thought that the best option in that case is JAXB.
XML example:
<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/metar1_2.xsd">
<request_index>136291043</request_index>
<data_source name="metars"/>
<request type="retrieve"/>
<errors/>
<warnings/>
<time_taken_ms>5</time_taken_ms>
<data num_results="2">
<METAR>
<raw_text>
PHTO 071453Z AUTO 23005KT 10SM FEW060 19/17 A3004 RMK AO2 SLP171 T01940172 55001 TSNO
</raw_text>
<station_id>PHTO</station_id>
<observation_time>2017-07-07T14:53:00Z</observation_time>
<latitude>19.72</latitude>
<longitude>-155.05</longitude>
<temp_c>19.4</temp_c>
<dewpoint_c>17.2</dewpoint_c>
<wind_dir_degrees>230</wind_dir_degrees>
<wind_speed_kt>5</wind_speed_kt>
<visibility_statute_mi>10.0</visibility_statute_mi>
<altim_in_hg>30.041338</altim_in_hg>
<sea_level_pressure_mb>1017.1</sea_level_pressure_mb>
<quality_control_flags>
<auto>TRUE</auto>
<auto_station>TRUE</auto_station>
<lightning_sensor_off>TRUE</lightning_sensor_off>
</quality_control_flags>
<sky_condition sky_cover="FEW" cloud_base_ft_agl="6000"/>
<flight_category>VFR</flight_category>
<three_hr_pressure_tendency_mb>-0.1</three_hr_pressure_tendency_mb>
<metar_type>METAR</metar_type>
<elevation_m>12.0</elevation_m>
</METAR>
<METAR>...</METAR>
</data>
</response>
I created DTO classes:
1) Data class:
@XmlRootElement(name = "data")
@XmlAccessorType(XmlAccessType.FIELD)
public class Data {
public Data(){}
public Data(int numResult, List<Metar> metars) {
this.numResult = numResult;
this.metars = metars;
}
public int getNumResult() {
return numResult;
}
public void setNumResult(int numResult) {
this.numResult = numResult;
}
public List<Metar> getMetars() {
return metars;
}
public void setMetars(List<Metar> metars) {
this.metars = metars;
}
@XmlAttribute(name="num_results")
private int numResult;
@XmlElement(name = "METAR")
private List<Metar> metars ;
}
}
2) Metar class:
@Data
@Builder
@XmlRootElement(name ="METAR")
@XmlAccessorType(XmlAccessType.FIELD)
public class Metar {
@Tolerate
public Metar(){
}
@XmlElement(name = "raw_text")
private String rawText;
@XmlElement(name = "station_id")
private String stationId;
@XmlElement(name = "observation_time")
private String observationTime;
@XmlElement(name = "latitude")
private float latitude;
@XmlElement(name = "longitude")
private float longitude;
@XmlElement(name = "temp_c")
private float tempC; // in decimal Celcius degrees
@XmlElement(name = "dewpoint_c")
private float dewpointC; // in decimal Celcius degrees
@XmlElement(name = "wind_dir_degrees")
private int windDirection; // Degrees
@XmlElement(name = "wind_speed_kt")
private int windSpeed; // Knots
@XmlElement(name = "wind_gust_kt")
private int windGusts; // Knots
@XmlElement(name = "visibility_statute_mi")
private float visibility; // StatuteMiles
@XmlElement(name = "altim_in_hg")
private float altimeter; // inches of Hg
@XmlElement(name = "sea_level_pressure_mb")
private float seaLvlPressure; // mb
@XmlElement(name = "quality_control_flags")
private String qualityControlFlags;
@XmlElement(name = "wx_string")
private String wxString;
And so on.. over 30 fields.
And The last one class with JAXB:
String urlForMetar = StaticValues.MAIN_URL_ADDRESS_FOR_METAR;
private StringBuilder xmlFile;
@Override
public String takeXmlFromApi() throws MalformedURLException, FileNotFoundException {
String line = null;
URL url = new URL(urlForMetar);
HttpURLConnection connection = null;
try {
xmlFile = null;
connection = (HttpURLConnection) url.openConnection();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
xmlFile = new StringBuilder();
while ((line = bufferedReader.readLine()) != null) {
xmlFile.append(line).append("\n");
}
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
return xmlFile.toString();
}
@Override
public void unmarshalFileFromXmlToObject(String xmlFile) throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(Data.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(xmlFile);
Data metarData = (Data) unmarshaller.unmarshal(reader);
System.out.println(metarData.getMetars().get(0).getStationId());
}
But I have got that kind of errors:
Exception in thread "main" javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"response"). Expected elements are <{}METAR>,<{}data> at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:726) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:247) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:242) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:109) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1131) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:556) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:538) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:153) at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:509) at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:374) at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl$NSContentDriver.scanRootElementHook(XMLNSDocumentScannerImpl.java:613) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:3132) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:852) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:602) at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:505) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:841) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:770) at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213) at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:643) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:243) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:214) at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:157) at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:214) at com.przemyslawostrouch.parsers.XmlParser.unmarshalFileFromXmlToObject(XmlParser.java:59) at com.przemyslawostrouch.main.WeatherAppApplication.main(WeatherAppApplication.java:24)
What is wrong? I read some articles about object factory class, should it be created? And second question quality control flags field have to be an Array instead of String ? I would like to add that I will not use this data for sure in the future.
<quality_control_flags>
<auto>TRUE</auto>
<auto_station>TRUE</auto_station>
<lightning_sensor_off>TRUE</lightning_sensor_off>
</quality_control_flags>