1

I have a XML string which I need to sort it by the attributes. Here is the input XML string:

<graph>
  <root root_id="1"/>
  <type name="A">
    <extends name="Sphere"/>
  </type>
  <node id="3" name="" type="A">
    <property name="radius" value="0.1"/>
  </node>
  <edge id="2" src_id="5" dest_id="3" type="successor"/>
  <node id="4" name="" type="L">
    <property name="length" value="3.0"/>
  </node>
  <edge id="3" src_id="1" dest_id="4" type="successor"/>
  <node id="5" name="" type="P">
    <property name="color" value="14"/>
  </node>
  <edge id="4" src_id="4" dest_id="5" type="successor"/>
</graph>

Here is the expecting result:

<graph>
  <root root_id="1"/>
  <type name="A">
    <extends name="Sphere"/>
  </type>
  <edge id="3" src_id="1" dest_id="4" type="successor"/>
  <node id="4" name="" type="L">
    <property name="length" value="3.0"/>
  </node>
  <edge id="4" src_id="4" dest_id="5" type="successor"/>
  <node id="5" name="" type="P">
    <property name="color" value="14"/>
  </node>
  <edge id="2" src_id="5" dest_id="3" type="successor"/>
  <node id="3" name="" type="A">
    <property name="radius" value="0.1"/>
  </node>
</graph>

Basically, the nodes have to been linked with the edges one after another, with the help of edge's attribute src_id and dest_id. Any idea will be very appreciated! Thank you very much!

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
Alex
  • 21
  • 1
  • 7

1 Answers1

4

This Class sorts NodeList by attribute's value given the type of data (String, Integer, Double etc).

Here is the solution in full:

package mynewpackage;

// for the method
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

// for the test example
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
import org.w3c.dom.Document;
import java.math.BigDecimal;

public class NodeTools {
    /**
     * Method sorts any NodeList by provided attribute.
     * @param nl NodeList to sort
     * @param attributeName attribute name to use
     * @param asc true - ascending, false - descending
     * @param B class must implement Comparable and have Constructor(String) - e.g. Integer.class , BigDecimal.class etc
     * @return Array of Nodes in required order
     */
    public static Node[] sortNodes(NodeList nl, String attributeName, boolean asc, Class<? extends Comparable> B)
    {        
        class NodeComparator<T> implements Comparator<T>
        {
            @Override
            public int compare(T a, T b)
            {
                int ret;
                Comparable bda = null, bdb = null;
                try{
                    Constructor bc = B.getDeclaredConstructor(String.class);
                    bda = (Comparable)bc.newInstance(((Element)a).getAttribute(attributeName));
                    bdb = (Comparable)bc.newInstance(((Element)b).getAttribute(attributeName));
                }
                catch(Exception e)
                {
                    return 0; // yes, ugly, i know :)
                }
                ret = bda.compareTo(bdb);
                return asc ? ret : -ret; 
            }
        }

        List<Node> x = new ArrayList<>();
        for(int i = 0; i < nl.getLength(); i++)
        {
            x.add(nl.item(i));
        }
        Node[] ret = new Node[x.size()];
        ret = x.toArray(ret);
        Arrays.sort(ret, new NodeComparator<Node>());
        return ret;
    }   

// Test with simple example 
    public static void main(String... args)
    {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  
        DocumentBuilder builder;
        String s = "<xml><item id=\"1\" price=\"100.00\" /><item id=\"3\" price=\"29.99\" /><item id=\"2\" price=\"5.10\" /></xml>";
        Document doc = null;
        try 
        {  
            builder = factory.newDocumentBuilder();  
            doc = builder.parse(new InputSource(new StringReader(s)));
        }
        catch(Exception e) { System.out.println("Alarm "+e); return; }

        System.out.println("*** Sort by id ***");
        Node[] ret = NodeTools.sortNodes(doc.getElementsByTagName("item"), "id", true, Integer.class);

        for(Node n: ret)
        {
            System.out.println(((Element)n).getAttribute("id")+" : "+((Element)n).getAttribute("price"));
        }

        System.out.println("*** Sort by price ***");
        ret = NodeTools.sortNodes(doc.getElementsByTagName("item"), "price", true, BigDecimal.class);
        for(Node n: ret)
        {
            System.out.println(((Element)n).getAttribute("id")+" : "+((Element)n).getAttribute("price"));
        }
    }
}

In my simple test it prints:

*** Sort by id ***
1 : 100.00
2 : 5.10
3 : 29.99
*** Sort by price ***
2 : 5.10
3 : 29.99
1 : 100.00
Community
  • 1
  • 1
Andrey Lebedenko
  • 1,850
  • 17
  • 24