0

I'm getting hard time in figuring how to write a loop to retrieve values from a given XML document for simplicity I'll give you a sample of the xml file

</solution>
          <solution>
               <cost>505.9208295302417</cost>
               <routes>
                    <route>
                         <driverId>noDriver</driverId>
                         <vehicleId>1_1</vehicleId>
                         <start>0.0</start>
                         <act type="service">
                              <serviceId>5  </serviceId>
                              <arrTime>109.9819741964403</arrTime>
                              <endTime>119.9819741964403</endTime>
                         </act>
                         <end>229.9639483928806</end>
                    </route>
                    <route>
                         <driverId>noDriver</driverId>
                         <vehicleId>3_1</vehicleId>
                         <start>0.0</start>
                         <act type="service">
                              <serviceId>4  </serviceId>
                              <arrTime>109.98190391287031</arrTime>
                              <endTime>119.98190391287031</endTime>
                         </act>
                         <act type="service">
                              <serviceId>2 </serviceId>
                              <arrTime>119.98282618841856</arrTime>
                              <endTime>129.98282618841856</endTime>
                         </act>
                         <act type="service">
                              <serviceId>1 </serviceId>
                              <arrTime>129.98372097890456</arrTime>
                              <endTime>139.98372097890456</endTime>
                         </act>
                         <act type="service">
                              <serviceId>3 </serviceId>
                              <arrTime>139.9846432544528</arrTime>
                              <endTime>149.9846432544528</endTime>
                         </act>
                         <end>259.9668316441239</end>
                    </route>
               </routes>
          </solution>
     </solutions>

so basically what I've in the code that I'll be showing you is to obtain value from only the node where there is cost = 505.9208295302417, please don't take into account this part of the code, so the next step was to retrieve driverid``, vehicleid and act , I know that there is a missing for loop but I don't know where to put it!! please can someone help. The desired output that i need to have is like for every vehicleid obtain list act values associated with it

java code

        public static void main(String[] args) {
        try {
            int totalVehicle;
            totalVehicle = 2;
            File fXmlFile = new File("C:/Users/HP/Desktop/solution.xml");
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            Document doc = dBuilder.parse(fXmlFile);
            doc.getDocumentElement().normalize();
            Double requiredCost = 505.9208295302417;
            System.out.println("Root element :" + doc.getDocumentElement().getNodeName());
            // NodeList nList = doc.getElementsByTagName("route");
            System.out.println("----------------------------");
            NodeList nodeList = doc.getElementsByTagName("solution");
            for (int i = 0; i < nodeList.getLength(); i++) {

                Node solutionNode = nodeList.item(i);

                if (solutionNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element solutionElement = (Element) solutionNode;
                    Node costNode = solutionElement.getElementsByTagName("cost").item(0);
                    Node route = solutionElement.getElementsByTagName("routes").item(0);
                    // if correct cost, proceed to parse further
                    Double costValue = Double.valueOf(costNode.getTextContent());
                    if (Double.compare(requiredCost, costValue) == 0) {
                        System.out.println("working");
                        // there you go, found the node with the cost 505.9208295302417
                        // now just parse all the node elements you need here

                        System.out.println("cost : "
                                + solutionElement.getElementsByTagName("cost")
                                        .item(0).getTextContent());
                        for (int h = 0; h < totalVehicle; h++) {
                            System.out.println("DriverId : "
                                    + solutionElement.getElementsByTagName("driverId")
                                            .item(h).getTextContent().toString());
                            System.out.println("vehicleId : "
                                    + solutionElement.getElementsByTagName("vehicleId")
                                            .item(h).getTextContent());
NodeList optionList = solutionElement.getElementsByTagName("act");
                            System.out.println(optionList.getLength());

        for (int j = 0; j < optionList.getLength(); ++j)
        {

            for(int k =0;k<1;++k){
                Element option = (Element) optionList.item(j);
            String optionText = option.getTextContent();
           //address.add(optionText.replaceAll("[^A-Za-z]"," "));
            System.out.println("Citizen :"+optionText.replaceAll("[^A-Za-z]"," "));}
            ;


        }
Aftab H.
  • 1,517
  • 4
  • 13
  • 25
Mattieu Kevin
  • 23
  • 1
  • 8
  • This is too much code to dig through. My approach for this king of tasks is to use [tag:jaxb] to parse the XML into an object structure first. Things get easier then. – lexicore Mar 04 '18 at 09:27
  • :( jaxb is not an option i need to get it through domparser it is the requirement wait , i understand that is too much to look in code, just forget about the java code if you could help me to get the code to retrieve all data in the "act" tag i will be able to make a way – Mattieu Kevin Mar 04 '18 at 09:38
  • DOM parser a requirement? How very interesting. The easiest would be probably using XPath. See [this question](https://stackoverflow.com/questions/2811001/how-to-read-xml-using-xpath-in-java) on how to do it. The XPath you'll need is like `/solutions/solution/routes/route/act`. – lexicore Mar 04 '18 at 09:41
  • I've tried xpath also i find it hard because in my xml document there are namespaces(not shown in the file) it is getting more complex than DOM parser believe me. I just the code to get all the data element present in the "act" tags the code above gave me just the one data per tag. I know that it is just a for loop but i'm not finding the logic :( please help me if you can @lexicore – Mattieu Kevin Mar 04 '18 at 09:50
  • 1
    I'm sorry, I will not write the code for you. I think getting XPath to work (even with namespaces) is easier than all these for-loops over a DOM tree. – lexicore Mar 04 '18 at 09:52
  • never mind thank you @lexicore – Mattieu Kevin Mar 04 '18 at 09:56

1 Answers1

0

As others have suggested, using xpath would be much easier but if it's an absolute requirement that you loop over all this stuff, break the problem into smaller, more manageable pieces. I thought I'd give it a try and I have a complete, working solution to your problem.

The idea is to break xml up into Java objects and use those objects to do the work you need.

public static void main(String... args) throws SAXException, IOException, ParserConfigurationException {
    Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
            .parse(new File("/home/william/Documents/test.xml"));
    doc.getDocumentElement().normalize();

    NodeList nodeList = doc.getElementsByTagName("solution");
    for (int i = 0; i < nodeList.getLength(); i++) {
        Node solutionNode = nodeList.item(i);

        try {
            System.out.println(Solution.newInstance(solutionNode).toString());
        } catch (Exception e) {
            // do something
            e.printStackTrace();
        }   
    }
}

Here, your original document parsed the same way you did it but Solution is its own class you can put the logic in:

public class Solution {

    private final double cost;

    private final Collection<Route> routes;

    public static final Solution newInstance(Node solution) throws Exception {
        return new Solution(solution);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("solution:");
        sb.append(System.lineSeparator());
        sb.append("--------");
        sb.append(System.lineSeparator());
        sb.append("cost:");
        sb.append(this.cost);

        for (Route route : this.routes) {
            sb.append(route.toString());
        }

        return sb.toString();
    }

    private Solution(Node solution) throws Exception {
        if (solution.getNodeType() == Node.ELEMENT_NODE) {
            Element solutionElement = (Element) solution;
            this.cost = Double.valueOf(solutionElement.getElementsByTagName("cost").item(0).getTextContent());
            this.routes = Routes.get(solutionElement.getElementsByTagName("routes").item(0));
        } else {
            // TODO something?
            this.cost = 0D;
            this.routes = Collections.emptyList();
        }
    }
}

Note that in your main method you have a check on the cost. I've left all class variable getter methods out, add the ones you need if you need to read them from outside the object. So for example in your Solution class add a getCost() method and then in the main method you can check as appropriate: if (solution.getCost() == requiredCost) { ...

Here, based on your requirements, you could add getters to the class members if needed. I've just overridden the toString() method in order to print something you can read.

Routes is a util class to create a collection of Route objects out of the child nodes of the <routes> node.

public class Routes {

    private final Collection<Route> items;

    public static Collection<Route> get(Node routes) throws Exception {
        return new Routes(routes).items;
    }

    private Routes() {
        this.items = new ArrayList<>();
    }

    private Routes(Node routes) throws Exception {
        this.items = new ArrayList<>();
        NodeList routesList = routes.getChildNodes();
        for (int i = 0; i < routesList.getLength(); i++) {
            Node route = routesList.item(i);
            if (Node.ELEMENT_NODE == route.getNodeType()) {
                items.add(Route.newInstance(route));
            } else {
                // TODO something?
            }
        }
    }   
}

The Acts class has similar logic to the Routes class (same thing but for acts):

public class Acts {

    private Collection<Act> items;

    public static Collection<Act> get(NodeList acts) throws Exception {
        return new Acts(acts).items;
    }

    private Acts() {
        this.items = new ArrayList<>();
    }

    private Acts(NodeList acts) throws Exception {
        this.items = new ArrayList<>();
        for (int i = 0; i < acts.getLength(); i++) {
            Node act = acts.item(i);
            if (Node.ELEMENT_NODE == act.getNodeType()) {
                this.items.add(Act.newInstance(act));
            } else {
                // TODO something?
            }
        }
    }
}

and finally, each individual Act:

public class Act {

    private final String service;

    private final double arrTime;

    private final double endTime;

    public static Act newInstance(Node act) throws Exception {
        return new Act(act);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();

        sb.append("Act");
        sb.append(System.lineSeparator());
        sb.append("arr time");
        sb.append(System.lineSeparator());
        sb.append(this.arrTime);
        sb.append(System.lineSeparator());
        sb.append("end time:");
        sb.append(System.lineSeparator());
        sb.append(this.endTime);
        sb.append(System.lineSeparator());
        sb.append("service:");
        sb.append(System.lineSeparator());
        sb.append(this.service);

        return sb.toString();
    }

    private Act(Node act) throws Exception {
        Element actElement = (Element) act;
        this.service = actElement.getAttribute("service");
        this.arrTime = Double.valueOf(actElement.getElementsByTagName("arrTime").item(0).getTextContent());
        this.endTime = Double.valueOf(actElement.getElementsByTagName("endTime").item(0).getTextContent());
    }
}
geco17
  • 5,152
  • 3
  • 21
  • 38
  • 1
    wow was not expecting that, i'll give it a try and let you know if it worked :) thank you for being a good human being. – Mattieu Kevin Mar 04 '18 at 10:55
  • I added a bit more information for you, specifically, my example just reads tho whole document and prints it. No checks on anything (whereas in yours you've got a cost requirement). Put getter methods in the classes as you need them for checking values as required. – geco17 Mar 04 '18 at 11:01