I am trying to parse an XML file using a DOM parser. The XML file contains a list of airports by name, FAA identifier, latitude/longitude, and URL. Here is a fragment of it:
<station>
<station_id>NFNA</station_id>
<state>FJ</state>
<station_name>Nausori</station_name>
<latitude>-18.05</latitude>
<longitude>178.567</longitude>
<html_url>http://weather.noaa.gov/weather/current/NFNA.html</html_url>
<rss_url>http://weather.gov/xml/current_obs/NFNA.rss</rss_url>
<xml_url>http://weather.gov/xml/current_obs/NFNA.xml</xml_url>
</station>
<station>
<station_id>KCEW</station_id>
<state>FL</state>
<station_name>Crestview, Sikes Airport</station_name>
<latitude>30.79</latitude>
<longitude>-86.52</longitude>
<html_url>http://weather.noaa.gov/weather/current/KCEW.html</html_url>
<rss_url>http://weather.gov/xml/current_obs/KCEW.rss</rss_url>
<xml_url>http://weather.gov/xml/current_obs/KCEW.xml</xml_url>
</station>
I am attempting to create objects (one for each airport) containing the information of each parsed airport, so that I can display each airport by name only. The rest of the airport info will be used later on in the project.
Can anyone tell me how I can create and instantiate objects from the information provided by this DOM parser, so that I can display only the name of each airport?
Here is my code:
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class Test {
//initialize variables
String station_id;
String state;
String station_name;
double latitude;
double longitude;
String html_url;
//Here is my constructor, I wish to instantiate these values with
//those of obtained by the DOM parser
Test(){
station_id = this.station_id;
state = this.state;
station_name = this.station_name;
latitude = this.latitude;
longitude = this.longitude;
html_url = this.html_url;
}
//Method for DOM Parser
public void readXML(){
try {
//new xml file and Read
File file1 = new File("src/flwxindex3.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(file1);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getElementsByTagName("station");
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if(node.getNodeType() == Node.ELEMENT_NODE){
Element first = (Element) node;
station_id = first.getElementsByTagName("station_id").item(0).getTextContent();
state = first.getElementsByTagName("state").item(0).getTextContent();
station_name = first.getElementsByTagName("station_name").item(0).getTextContent();
latitude = Double.parseDouble(first.getElementsByTagName("latitude").item(0).getTextContent());
longitude = Double.parseDouble(first.getElementsByTagName("longitude").item(0).getTextContent());
html_url = first.getElementsByTagName("station_id").item(0).getTextContent();
}
/*These are just test to see if the actually worked
*
System.out.println(station_id);
System.out.println(state);
System.out.println(station_name);
System.out.println(latitude);
System.out.println(longitude);
System.out.println(html_url);
*/
}
} catch (Exception e) {
System.out.println("XML Pasing Excpetion = " + e);
}
}
public static void main(String[] args) {
//Create new object and call the DOM Parser method
Test test1 = new Test();
test1.readXML();
//System.out.println(test1.station_name);
}
}
You currently have variables that are global to your class. You need an Object
to contain your variables:
public class Airport {
String stationId;
String state;
String stationName;
double latitude;
double longitude;
String url;
//getters/setters etc
}
Now create a List
of your airports at the top of your class
final List<Airport> airports = new LinkedList<Airport>();
Finally create and add instances of airport to the List
in your loop
if(node.getNodeType() == Node.ELEMENT_NODE){
final Element first = (Element) node;
final Airport airport = new Airport();
airport.setStationId(first.getElementsByTagName("station_id").item(0).getTextContent());
airport.setState(first.getElementsByTagName("state").item(0).getTextContent());
//etc
airports.add(airport);
}
And to loop over the airports and show names, simply
for(final Airport airport : airports) {
System.out.println(airport.getStationName());
}
To be honest this is all a little messy. I would really recommend using JAXB if you want to unmarshall your XML into java POJOs.
Here is quick example using an XML schema, maven-jaxb2-plugin and JAXB.
With a little preparation you can create your list of Airport
using only the following code (Airport
has become Station
as that is your xml element name).
public static void main(String[] args) throws InterruptedException, JAXBException {
final JAXBContext jaxbc = JAXBContext.newInstance(Stations.class);
final Unmarshaller unmarshaller = jaxbc.createUnmarshaller();
final Stations stations = (Stations) unmarshaller.unmarshal(Thread.currentThread().getContextClassLoader().getResource("airports.xml"));
for (final Station station : stations.getStation()) {
System.out.println(station.getStationName());
}
}
Output:
Nausori
Crestview, Sikes Airport
In order for this to work I created an xml schema to define your xml file format
<xs:element name="stations">
<xs:complexType>
<xs:sequence>
<xs:element name="station" minOccurs="1" maxOccurs="unbounded">
<xs:complexType>
<xs:all>
<xs:element name="station_id" type="xs:string"/>
<xs:element name="state" type="xs:string"/>
<xs:element name="station_name" type="xs:string"/>
<xs:element name="latitude" type="xs:decimal"/>
<xs:element name="longitude" type="xs:decimal"/>
<xs:element name="html_url" type="xs:anyURI"/>
<xs:element name="rss_url" type="xs:anyURI"/>
<xs:element name="xml_url" type="xs:anyURI"/>
</xs:all>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
The defines the format of your xml file. Note it has a stations
tag that wraps your two station tags. Here is an example xml
<stations>
<station>
<station_id>NFNA</station_id>
<state>FJ</state>
<station_name>Nausori</station_name>
<latitude>-18.05</latitude>
<longitude>178.567</longitude>
<html_url>http://weather.noaa.gov/weather/current/NFNA.html</html_url>
<rss_url>http://weather.gov/xml/current_obs/NFNA.rss</rss_url>
<xml_url>http://weather.gov/xml/current_obs/NFNA.xml</xml_url>
</station>
<station>
<station_id>KCEW</station_id>
<state>FL</state>
<station_name>Crestview, Sikes Airport</station_name>
<latitude>30.79</latitude>
<longitude>-86.52</longitude>
<html_url>http://weather.noaa.gov/weather/current/KCEW.html</html_url>
<rss_url>http://weather.gov/xml/current_obs/KCEW.rss</rss_url>
<xml_url>http://weather.gov/xml/current_obs/KCEW.xml</xml_url>
</station>
</stations>
I compiled the schema to a java class using the following in my pom.xml - using maven. If not using maven you can call xjc from the command line.
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.8.0</version>
<configuration>
<schemaDirectory>src/main/resources/</schemaDirectory>
<generatePackage>com.boris.airport</generatePackage>
</configuration>
<executions>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>