javaandroidsaxnutiteq

Empty list in SAX parser endDocument after successfully parsing elements to the list


I'm trying to parse an XML containing geographic nodes and ways connecting the nodes using SAX parser. I store the parsed nodes in an ArrayMap<Long, MapPos> and the ways in an ArrayList<ArrayList<MapPos>>. When parsing a way, I create an ArrayList<MapPos> of the referenced nodes and add this to the ArrayList of ways.

After debugging the application, I see that startElement() and endElement() successfully adds the ways to the ArrayList, but in the endDocument() method the ways ArrayList contains nothing but a bunch of empty ArrayList.

Here is the java class:

public class ParkingDataExtractor {
    private static List<ArrayList<MapPos>> roads = new ArrayList<ArrayList<MapPos>>();

    public static List<ArrayList<MapPos>> getWaysFromXML()
            throws ParserConfigurationException, SAXException, IOException{

        SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
        DefaultHandler handler = new DefaultHandler() {
            ArrayMap<Long, MapPos> nodes = new ArrayMap<Long, MapPos>();
            ArrayList<MapPos> nodeBuffer = new ArrayList<MapPos>();
            List<ArrayList<MapPos>> ways = new ArrayList<ArrayList<MapPos>>();
            // private int i; // for debug purposes

            @Override
            public void startElement(String uri, String localName,
                    String qName, Attributes attributes)
                    throws SAXException {
                if (qName.equalsIgnoreCase("node")) {
                    Long id = Long.parseLong(attributes.getValue("id"));
                    Float lat = Float
                            .parseFloat(attributes.getValue("lat"));
                    Float lon = Float
                            .parseFloat(attributes.getValue("lon"));
                    nodes.put(id, new MapPos(lat, lon));
                } else if (qName.equalsIgnoreCase("nd")) {
                    Long ref = Long.parseLong(attributes.getValue("ref"));
                    nodeBuffer.add(nodes.get(ref));
                }
            }

            @Override
            public void endElement(String uri, String localName,
                    String qName) throws SAXException {
                if (qName.equalsIgnoreCase("way")) {
                    ways.add(nodeBuffer);
                    // i++;
                    // if(i==1590) // last element
                    //     ArrayList<MapPos> test = ways.get(i-1); // test = [MapPos [x=..., y=..., z=0.0], MapPos [x=..., y=..., z=0.0],...]
                    nodeBuffer.clear();
                }
            }

            @Override
            public void endDocument() throws SAXException {
                // ArrayList<MapPos> test = ways.get(i-1); // test = []
                roads = ways;
            }
        };

        saxParser.parse("file://" + Environment.getExternalStorageDirectory() 
                + "/roadmap.xml", handler);
        return roads;
    }
}

Solution

  • When you call nodeBuffer.clear() you empty the list that you have just passed on to ways. You basically use the same nodeBuffer object over and over, and fill the ways list with a lot of references to the same object - which you empty each time.

    The way you should do it is create a new ArrayList object using new and assign it to nodeBuffer every time. You will then have separate objects, each containing the list of nodes parsed in the latest round.