Create objects from DOM parser in Java?

David picture David · Apr 14, 2013 · Viewed 7.9k times · Source

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


}

}

Answer

Boris the Spider picture Boris the Spider · Apr 14, 2013

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>