JamieOliverQA
JamieOliverQA

Reputation: 45

Sort XML lines in a java list, based on tag values

I have a list loaded with XML lines, which can come in any order as per the following ...

<MAIN_TAG>
    <TAG_ONE>22</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>8888</TAG_FOUR>
    <TAG_FIVE>arcdf</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>3</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>522</TAG_FOUR>
    <TAG_FIVE>00561</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>10</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>200</TAG_FOUR>
    <TAG_FIVE>ggggg</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>1</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>951</TAG_FOUR>
    <TAG_FIVE>56756</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>35</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>071</TAG_FOUR>
    <TAG_FIVE>ds15s</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>2</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>071</TAG_FOUR>
    <TAG_FIVE>34534</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>40</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>45</TAG_FOUR>
    <TAG_FIVE>4rsss</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>42</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>35</TAG_FOUR>
    <TAG_FIVE>cdsss</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>20</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>10</TAG_FOUR>
    <TAG_FIVE>dssss</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>30</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>1</TAG_FOUR>
    <TAG_FIVE>dsdfe</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>

I would like to be able to sort this Java list based on a tag passed in by the user. So the following would have been sorted by passing in to sort by 'TAG_ONE'...

<MAIN_TAG>
    <TAG_ONE>1</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>951</TAG_FOUR>
    <TAG_FIVE>56756</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>2</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>071</TAG_FOUR>
    <TAG_FIVE>34534</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>3</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>522</TAG_FOUR>
    <TAG_FIVE>00561</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>10</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>200</TAG_FOUR>
    <TAG_FIVE>ggggg</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>20</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>10</TAG_FOUR>
    <TAG_FIVE>dssss</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>22</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>8888</TAG_FOUR>
    <TAG_FIVE>arcdf</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>30</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>1</TAG_FOUR>
    <TAG_FIVE>dsdfe</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>35</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>071</TAG_FOUR>
    <TAG_FIVE>ds15s</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>40</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>45</TAG_FOUR>
    <TAG_FIVE>4rsss</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>
<MAIN_TAG>
    <TAG_ONE>42</TAG_ONE>
    <TAG_TWO>2</TAG_TWO>
    <TAG_THREE>junk</TAG_THREE>
    <TAG_FOUR>35</TAG_FOUR>
    <TAG_FIVE>cdsss</TAG_FIVE>
    <TAG_SIX>more junk</TAG_SIX>
</MAIN_TAG>

But would be good to have the option to sort by 'TAG_FOUR' for example.

Thank you in advance for any help.

Stacksamus

Upvotes: 2

Views: 1251

Answers (2)

Syam S
Syam S

Reputation: 8509

Alternatively you can use xslt for this. You could parameterize the tag to sort, the sort order, type etc.. Howvever you need to have a root element. I gave Example

import java.io.File;

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class Test {

    public static void main(String[] args) throws Exception {
        TransformerFactory factory = TransformerFactory.newInstance();
        Source xslt = new StreamSource(new File("sort.xslt"));
        Transformer transformer = factory.newTransformer(xslt);
        Source text = new StreamSource(new File("data.xml"));
        transformer.setParameter("tagName", "TAG_ONE");
        transformer.transform(text, new StreamResult(new File("output.xml")));
        System.out.println("Done");
    }

}

sort.xslt

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes" />
    <xsl:strip-space elements="*" />
    <xsl:param name="tagName" />
    <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates>
                <xsl:sort data-type="number" select="*[name() = $tagName]" />
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates />
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Upvotes: 0

icza
icza

Reputation: 418655

Here is a complete solution.

First I define 2 very simple classes to represent the data in the XML: Wrapper and MainTag. The wrapper just wraps the MAIN_TAG elements in a list, required by JAXB. The MainTag class just holds public properties, exactly as appearing in the input.

Once the input is read, I sort it by an arbitrarily chosen TAG (identified by an int index, 1 for TAG_ONE, 2 for TAG_TWO etc.). The sorting is implemented in the Wrapper.sort() method which takes the tag as parameter to sort by.

Lastly I simply print out the result to the standard outpout, also using JAXB.

Input is slightly modified (wrapped in a wrapper tag):

<wrapper>
    <MAIN_TAG>
        <TAG_ONE>22</TAG_ONE>
        <TAG_TWO>2</TAG_TWO>
        <TAG_THREE>junk</TAG_THREE>
        <TAG_FOUR>8888</TAG_FOUR>
        <TAG_FIVE>arcdf</TAG_FIVE>
        <TAG_SIX>more junk</TAG_SIX>
    </MAIN_TAG>
    <MAIN_TAG>
        <TAG_ONE>3</TAG_ONE>
        <TAG_TWO>2</TAG_TWO>
        <TAG_THREE>junk</TAG_THREE>
        <TAG_FOUR>522</TAG_FOUR>
        <TAG_FIVE>00561</TAG_FIVE>
        <TAG_SIX>more junk</TAG_SIX>
    </MAIN_TAG>
    <!-- ...and the rest which I omit here... -->
</wrapper>

The Wrapper and MainTag classes:

class Wrapper {
    public List< MainTag > MAIN_TAG = new ArrayList<>();

    public void sort(final int byTag) {
        Collections.sort(MAIN_TAG, new Comparator< MainTag >() {
            @Override
            public int compare(MainTag m1, MainTag m2) {
                switch (byTag) {
                    case 1: return m1.TAG_ONE.compareTo(m2.TAG_ONE);
                    case 2: return m1.TAG_TWO.compareTo(m2.TAG_TWO);
                    case 3: return m1.TAG_THREE.compareTo(m2.TAG_THREE);
                    case 4: return m1.TAG_FOUR.compareTo(m2.TAG_FOUR);
                    case 5: return m1.TAG_FIVE.compareTo(m2.TAG_FIVE);
                    case 6: return m1.TAG_SIX.compareTo(m2.TAG_SIX);
                }
                return 0;
            }
        });
    }
}

class MainTag {
    public Integer TAG_ONE;
    public Integer TAG_TWO;
    public String TAG_THREE;
    public Integer TAG_FOUR;
    public String TAG_FIVE;
    public String TAG_SIX;
}

And finally how to use it:

Wrapper w = JAXB.unmarshal(new File("input.xml"), Wrapper.class);
w.sort(1);
JAXB.marshal(w, System.out);

Upvotes: 1

Related Questions