sidlejinks
sidlejinks

Reputation: 709

Parsing XML into HashMap using DOM

I thoroughly search answer to my question but didn't get it. My task is something like that. I have an XML:

<?xml version="1.0" encoding="utf-8" ?>
<users>
    <user id="1">
        <login>james.bond</login>
        <password>12345</password>
        <enabled>true</enabled>
    </user>
    <user id="2">
        <login>john.doe</login>
        <password>67890</password>
        <enabled>false</enabled>
    </user>
</users>

I want to parse it to HashMap where keys and values should be something like that (node names through a period as a keys and values of these nodes as a values in map):

K: users.user.1.login V: james.bond

K: users.user.1.password V: 12345

etc...

On the very moment I have:

public Map<String, String> parse(File file) {
        Document doc = null;

        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();

            dbf.setIgnoringComments(true);

            doc = db.parse(file);
        } catch (ParserConfigurationException pce) {
            System.out.println("Parser was not configured properly [LOG IT!]");
        } catch (IOException io) {
            System.out.println("Cannot read input file [LOG IT!]");
        } catch (SAXException se) {
            System.out.println("Problem parsing the file [LOG I!]");
        } catch (IllegalArgumentException ae) {
            System.out.println("Please specify an XML source [LOG IT!]");
        }

        Element root = doc.getDocumentElement();

        return stepThrough(root);
    }

private Map<String, String> stepThrough(Node node) {
        String nodeName =  node.getNodeName();

        if (!nodeName.equals("#text")) {
            buildedKey.append(nodeName).append(".");
        }

        if (node.getNodeValue() != null && !node.getNodeValue().matches("^\\W*$")) {
            parsedMap.put(buildedKey.toString(),node.getNodeValue());
        }

        if (node.getNodeType() == Node.ELEMENT_NODE) {
            if (node.hasAttributes()) {
                NamedNodeMap startAttr = node.getAttributes();
                for (int i = 0; i < startAttr.getLength(); i++) {
                    Node attr = startAttr.item(i);
                    buildedKey.append(attr.getNodeValue()).append(".");
                }
            }
        }

        for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
            stepThrough(child);
        }

        return parsedMap;
    }

And I have last key in my map for example: users.user.1.login.password.enabled.user.2.login.password.enabled

In other words all node names are concatenating.

Would you help me? Thanks!

P.S.: I have one restriction and I can not use libs like XStream...

Upvotes: 0

Views: 3918

Answers (1)

Mikita Belahlazau
Mikita Belahlazau

Reputation: 15434

The problem is that you're using same global buildedKey for all traversing and you don't remove added part when you're coming back to from stepThrough. Simple solution would be to pass buildedKey as parameter instead of using global variable:

private Map<String, String> stepThrough(Node node, String buildedKey) {
        String nodeName =  node.getNodeName();

        if (!nodeName.equals("#text")) {
            buildedKey += nodeName + ".";
        }

        if (node.getNodeValue() != null && !node.getNodeValue().matches("^\\W*$")) {
            parsedMap.put(buildedKey,node.getNodeValue());
        }

        if (node.getNodeType() == Node.ELEMENT_NODE) {
            if (node.hasAttributes()) {
                NamedNodeMap startAttr = node.getAttributes();
                for (int i = 0; i < startAttr.getLength(); i++) {
                    Node attr = startAttr.item(i);
                    buildedKey += attr.getNodeValue() + ".";
                }
            }
        }

        for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
            stepThrough(child, buildedKey);
        }

        return parsedMap;
    }

Also you better use String for buildedKey instead of StringBuilder because String is immutable and you don't want to spoil buildedKey in one node and use same spoiled key in another.

Upvotes: 2

Related Questions