47

When processing XML by means of standard DOM, attribute order is not guaranteed after you serialize back. At last that is what I just realized when using standard java XML Transform API to serialize the output.

However I do need to keep an order. I would like to know if there is any posibility on Java to keep the original order of attributes of an XML file processed by means of DOM API, or any way to force the order (maybe by using an alternative serialization API that lets you set this kind of property). In my case processing reduces to alter the value of some attributes (not all) of a sequence of the same elements with a bunch of attributes, and maybe insert a few more elements.

Is there any "easy" way or do I have to define my own XSLT transformation stylesheet to specify the output and altering the whole input XML file?

Update I must thank all your answers. The answer seems now more obvious than I expected. I never paid any attention to attribute order, since I had never needed it before.

The main reason to require an attribute order is that the resulting XML file just looks different. The target is a configuration file that holds hundreds of alarms (every alarm is defined by a set of attributes). This file usually has little modifications over time, but it is convenient to keep it ordered, since when we need to modify something it is edited by hand. Now and then some projects need light modifications of this file, such as setting one of the attributes to a customer specific code.

I just developed a little application to merge original file (common to all projects) with specific parts of each project (modify the value of some attributes), so project-specific file gets the updates of the base one (new alarm definitions or some attribute values bugfixes). My main motivation to require ordered attributes is to be able to check the output of the application againts the original file by means of a text comparation tool (such as Winmerge). If the format (mainly attribute order) remains the same, the differences can be easily spotted.

I really thought this was possible, since XML handling programs, such as XML Spy, lets you edit XML files and apply some ordering (grid mode). Maybe my only choice is to use one of these programs to manually modify the output file.

Fernando Miguélez
  • 11,196
  • 6
  • 36
  • 54
  • *Why* do you need to keep an order? The request implies that you are processing the XML text with tools that have not been made for XML. Is that the case? – Tomalak Apr 07 '09 at 15:46
  • The solution to your stated problem is to write a program that preprocesses the files to compare before comparing them. Such a program would put the attributes into a canonical order. – John Saunders Aug 10 '11 at 13:24
  • 7
    Commander @Tomalak, i am processing XML text with tools that have not been made for XML: my eyes. Xml is also a human-readable format. – Ian Boyd Apr 25 '12 at 14:53
  • @IanBoyd: do your eyes a favor, then, and process the XML into something that's easier to read. If your eyes find the order to be important, then your conversion tool should output to a specific order. – John Saunders Sep 11 '14 at 00:48
  • 2
    @JohnSaunders I'm trying to; which is why i need the original order! – Ian Boyd Sep 11 '14 at 15:00
  • @IanBoyd: the _original_ order, or a specific order? Recall that the original order was indeterminate and unimportant. Choose an order and stick to it. I always prefer alphabetical, since XSLT can do that fairly easily. – John Saunders Sep 11 '14 at 16:00
  • I have another option to change to order using DOM apache parser: https://stackoverflow.com/a/55598817/1118996 – IvanNik Apr 09 '19 at 20:52
  • Underscore-java library preserves attribute order while loading xml. – Valentyn Kolesnikov Mar 15 '20 at 04:48

12 Answers12

31

Sorry to say, but the answer is more subtle than "No you can't" or "Why do you need to do this in the first place ?".

The short answer is "DOM will not allow you to do that, but SAX will".

This is because DOM does not care about the attribute order, since it's meaningless as far as the standard is concerned, and by the time the XSL gets hold of the input stream, the info is already lost. Most XSL engine will actually gracefully preserve the input stream attribute order (e.g. Xalan-C (except in one case) or Xalan-J (always)). Especially if you use <xsl:copy*>.

Cases where the attribute order is not kept, best of my knowledge, are. - If the input stream is a DOM - Xalan-C: if you insert your result-tree tags literally (e.g. <elem att1={@att1} .../>

Here is one example with SAX, for the record (inhibiting DTD nagging as well).

SAXParserFactory spf = SAXParserFactoryImpl.newInstance();
spf.setNamespaceAware(true);
spf.setValidating(false);
spf.setFeature("http://xml.org/sax/features/validation", false);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
SAXParser sp = spf.newSAXParser() ;
Source src = new SAXSource ( sp.getXMLReader(), new InputSource( input.getAbsolutePath() ) ) ;
String resultFileName = input.getAbsolutePath().replaceAll(".xml$", ".cooked.xml" ) ;
Result result = new StreamResult( new File (resultFileName) ) ;
TransformerFactory tf = TransformerFactory.newInstance();
Source xsltSource = new StreamSource( new File ( COOKER_XSL ) );
xsl = tf.newTransformer( xsltSource ) ;
xsl.setParameter( "srcDocumentName", input.getName() ) ;
xsl.setParameter( "srcDocumentPath", input.getAbsolutePath() ) ;

xsl.transform(src, result );

I'd also like to point out, at the intention of many naysayers that there are cases where attribute order does matter.

Regression testing is an obvious case. Whoever has been called to optimise not-so-well written XSL knows that you usually want to make sure that "new" result trees are similar or identical to the "old" ones. And when the result tree are around one million lines, XML diff tools prove too unwieldy... In these cases, preserving attribute order is of great help.

Hope this helps ;-)

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Alain Pannetier
  • 342
  • 3
  • 2
  • 1
    +1 but your example of regression testing is a "red herring". The solution is to have the regression testing tool first convert the XML file to a canonical order before comparing. – John Saunders Sep 11 '14 at 00:47
  • 8
    @JohnSaunders On the other hand, one of the beauty things about XML is that humans can read it, and at least in LtoR countries, we tend to look for important things towards the L, with less important things to the R. So, ideally it would be nice to preserve the order when the XML is created, as the creator may have considered attribute ordering to be significant to humans. At least, this should be an option on the writer or document object. – MushyMiddle Jan 13 '15 at 19:06
  • But since XML does not consider the order of the attributes, that wouldn't make much sense. – John Saunders Jan 13 '15 at 19:12
  • 21
    clean diffs in version control is another reason to keep the order of a file the same. – SketchBookGames Mar 06 '15 at 21:11
  • @sketch the solution would be to use a compare tool that understands XML. Such a tool would show as identical two XML documents which different only in attribute order. – John Saunders Jan 19 '17 at 23:52
  • 2
    Allow me to add a comment on an old answer. It may well be that certain SAX parsers preserve attribute order, or that they did so back in 2010. But it's not part of the specification, and relying on an accidental unspecified property of a particular implementation is not good engineering. – Michael Kay Jan 03 '19 at 16:18
  • I just suggest Open Source implementation use ordered map / set other than hash map/set. with Hash Map, order of 3-attributes is possibly different from that of 5-attributes, so that after I added few information and save back the file change is bigger than expected, if I commit change to SVN, system thinks there is a big change. – Daniel Yang Jun 18 '20 at 08:11
24

Look at section 3.1 of the XML recommendation. It says, "Note that the order of attribute specifications in a start-tag or empty-element tag is not significant."

If a piece of software requires attributes on an XML element to appear in a specific order, that software is not processing XML, it's processing text that looks superficially like XML. It needs to be fixed.

If it can't be fixed, and you have to produce files that conform to its requirements, you can't reliably use standard XML tools to produce those files. For instance, you might try (as you suggest) to use XSLT to produce attributes in a defined order, e.g.:

<test>
   <xsl:attribute name="foo"/>
   <xsl:attribute name="bar"/>
   <xsl:attribute name="baz"/>
</test>

only to find that the XSLT processor emits this:

<test bar="" baz="" foo=""/>

because the DOM that the processor is using orders attributes alphabetically by tag name. (That's common but not universal behavior among XML DOMs.)

But I want to emphasize something. If a piece of software violates the XML recommendation in one respect, it probably violates it in other respects. If it breaks when you feed it attributes in the wrong order, it probably also breaks if you delimit attributes with single quotes, or if the attribute values contain character entities, or any of a dozen other things that the XML recommendation says that an XML document can do that the author of this software probably didn't think about.

Robert Rossney
  • 94,622
  • 24
  • 146
  • 218
8

XML Canonicalisation results in a consistent attribute ordering, primarily to allow one to check a signature over some or all of the XML, though there are other potential uses. This may suit your purposes.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
  • Though the problem no longer applies to my current situation I do thank your answer. In a near future it could be useful – Fernando Miguélez Sep 19 '10 at 04:13
  • This is the answer for people who are looking to have XML be comparable in automated tests and diffs. That's why canonicalization was developed. It does make a lot of potential [changes](https://www.ibm.com/developerworks/library/x-c14n/index.html) to the document, but they didn't affect me much and I kept them. I did my c14n with python `lxml`, full working snippet at https://stackoverflow.com/questions/22959577/python-exclusive-xml-canonicalization-xml-exc-c14n/22960033#22960033. – Noumenon Dec 15 '17 at 02:06
7

It's not possible to over-emphasize what Robert Rossney just said, but I'll try. ;-)

The benefit of International Standards is that, when everybody follows them, life is good. All our software gets along peacefully.

XML has to be one of the most important standards we have. It's the basis of "old web" stuff like SOAP, and still 'web 2.0' stuff like RSS and Atom. It's because of clear standards that XML is able to interoperate between different platforms.

If we give up on XML, little by little, we'll get into a situation where a producer of XML will not be able to assume that a consumer of XML will be able to consumer their content. This would have a disasterous affect on the industry.

We should push back very forcefully, on anyone who writes code that does not process XML according to the standard. I understand that, in these economic times, there is a reluctance to offend customers and business partners by saying "no". But in this case, I think it's worth it. We would be in much worse financial shape if we had to hand-craft XML for each business partner.

So, don't "enable" companies who do not understand XML. Send them the standard, with the appropriate lines highlighted. They need to stop thinking that XML is just text with angle brackets in it. It simply does not behave like text with angle brackets in it.

It's not like there's an excuse for this. Even the smallest embedded devices can have full-featured XML parser implementations in them. I have not yet heard a good reason for not being able to parse standard XML, even if one can't afford a fully-featured DOM implementation.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
3

I think I can find some valid justifications for caring about attribute order:

  • You may be expecting humans to have to manually read, diagnose or edit the XML data one time or another; readability would be important in that instance, and a consistent and logical ordering of the attributes helps with that;
  • You may have to communicate with some tool or service that (admitedly erroneously) cares about the order; asking the provider to correct its code may not be an option: try to ask that from a government agency while your user's deadline for electronically delivering a bunch of fiscal documents looms closer and closer!

It seems like Alain Pannetier's solution is the way to go.

Also, you may want to take a look at DecentXML; it gives you full control of how the XML is formatted, even though it's not DOM-compatible. Specially useful if you want to modify some hand-edited XML without losing the formatting.

Community
  • 1
  • 1
Haroldo_OK
  • 6,612
  • 3
  • 43
  • 80
  • Your humans should be taught that the order doesn't matter. And, yes, if your government is the kind which takes dissident software engineers out back and shoots them, then, don't say no. But do try to find a way to tell us which government it is, so we know for the future. – John Saunders Sep 11 '14 at 00:43
  • 6
    Sorry, @John Saunders. People doesn't need to be "taught" by software, software need to fulfill people needs. If you have users that can find useful to review attributes on specific order (maybe to not to do a 15min job in 2h...), you need to do it or you are an incompetent engineer. People comes first. – Renascienza Jan 19 '17 at 23:26
  • @ren I didn't say they needed to be taught by software. I said they need to be taught _about_ software. XML works how it works, not as uninformed people imagine it does. A compliant XML implementation can present the attributes in any order and still be correct. In this case, the OP confused the UI of a tool for the behavior of the standard. He needed a compare tool that understood XML. – John Saunders Jan 19 '17 at 23:49
  • 3
    I think users really can be taught about XML constraints and how about the format work. But I doubt that this fact change their requirements. If users need ordered attributes to accomplish some job, so they need. If they want to use a simple webpage or a basic text viewer to do it, that's exactly how this will work. Isn't developer call to decide that they can't have it. The developer job is build what people ask for. – Renascienza Jan 20 '17 at 00:17
  • @Renascienza if your users require ordered attributes in order to do the job, then they need elements instead. Attributes are not ordered. – John Saunders Jun 03 '20 at 10:18
2

I had the same exact problem. I wanted to modify XML attributes but wanted to keep the order because of diff. I used StAX to achieve this. You have to use XMLStreamReader and XMLStreamWriter (the Cursor based solution). When you get a START_ELEMENT event type, the cursor keeps the index of the attributes. Hence, you can make appropriate modifications and write them to the output file "in order".

Look at this article/discussion. You can see how to read the attributes of the start elements in order.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Bashir
  • 266
  • 3
  • 10
1

You can still do this using the standard DOM and Transformation API by using a quick and dirty solution like the one I am describing:

We know that the transformation API solution orders the attributes alphabetically. You can prefix the attributes names with some easy-to-strip-later strings so that they will be output in the order you want. Simple prefixes as "a_" "b_" etc should suffice in most situations and can be easily stripped from the output xml using a one liner regex.

If you are loading an xml and resave and want to preserve attributes order, you can use the same principle, by first modifying the attribute names in the input xml text and then parsing it into a Document object. Again, make this modification based on a textual processing of the xml. This can be tricky but can be done by detecting elements and their attributes strings, again, using regex. Note that this is a dirty solution. There are many pitfalls when parsing XML on your own, even for something as simple as this, so be careful if you decide to implement this.

Radu Simionescu
  • 4,518
  • 1
  • 35
  • 34
0

Kind of works...

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 
     */
    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;
    }    

    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
Andrey Lebedenko
  • 1,850
  • 17
  • 24
  • 1
    Not really related with this question. The guy doesn't need to order elements, but attributes inside elements. – Renascienza Jan 19 '17 at 23:23
0

You really shouldn't need to keep any sort of order. As far as I know, no schema takes attribute order into account when validating an XML document either. It sounds like whatever is processing XML on the other end isn't using a proper DOM to parse the results.

I suppose one option would be to manually build up the document using string building, but I strongly recommend against that.

Soviut
  • 88,194
  • 49
  • 192
  • 260
0

Robert Rossney said it well: if you're relying on the ordering of attributes, you're not really processing XML, but rather, something that looks like XML.

I can think of at least two reasons why you might care about attribute ordering. There may be others, but at least for these two I can suggest alternatives:

  1. You're using multiple instances of attributes with the same name:

    <foo myAttribute="a" myAttribute="b" myAttribute="c"/>
    

    This is just plain invalid XML; a DOM processor will probably drop all but one of these values – if it processes the document at all. Instead of this, you want to use child elements:

    <foo>
        <myChild="a"/>
        <myChild="b"/>
        <myChild="c"/>
    </foo>
    
  2. You're assuming that some sort of distinction applies to the attribute(s) that come first. Make this explicit, either through other attributes or through child elements. For example:

    <foo attr1="a" attr2="b" attr3="c" theMostImportantAttribute="attr1" />
    
svick
  • 236,525
  • 50
  • 385
  • 514
Dan Breslau
  • 11,472
  • 2
  • 35
  • 44
  • 5
    In my case I am writing a migration script manipulating some XML configuration, which is stored in the VCS. The VCS diff is showing meaningless changes (modification of attribute order) as well as meaningful changes (what the program has modified). It would be nice to show only meaningful changes. Also (although this is not a problem I have) spurious merge conflicts will result if multiple people did this sort of thing and their XML serializers wrote non-modified attributes in various orders. – Adrian Smith Dec 05 '12 at 10:54
  • 1
    Sometimes one is writing a script for third party software/configuration files. XML is forced upon you. – kierans Dec 03 '13 at 07:03
  • 1
    @AdrianSmith: your scenario can be handled by processing both sides of the comparison ahead of time with a script or stylesheet that outputs the XML in a canonical order. I have done this in order to compare SQL Server Integration Services .dtsx files, which have far worse problems that attribute order. Among other things, these files change simply by opening them in the design tool. – John Saunders Sep 11 '14 at 00:41
  • @kierans: no, it is not forced on you. Just say "no" to garbage, or you will find yourself writing custom code every time you have to process XML. – John Saunders Sep 11 '14 at 00:41
0

Inspired by the answer of Andrey Lebedenko.
Capable of sorting by a Nodes attribute or by a Nodes text content.
Ready to be used in Your XML utility class.

public static Collection<Node> nodeListCollection(final NodeList nodeList) {
  if (nodeList == null) {
    return Collections.emptyList();
  }
  final int length = nodeList.getLength();
  if (length == 0) {
    return Collections.emptyList();
  }
  return IntStream.range(0, length)
      .mapToObj(nodeList::item)
      .collect(Collectors.toList());
}

private static int compareString(final String str1, final String str2, final boolean nullIsLess) {
  if (Objects.equals(str1, str2)) { 
    return 0;
  }
  if (str1 == null) {
    return nullIsLess ? -1 : 1;
  }
  if (str2 == null) {
    return nullIsLess ? 1 : -1;
  }
  return str1.compareTo(str2);
}

private static final Function<Boolean, Comparator<Node>> StringNodeValueComparatorSupplier = (asc) ->
    (Node a, Node b) -> {
      final String va = a == null ? null : a.getTextContent();
      final String vb = b == null ? null : b.getTextContent();
      return (asc ? 1 : -1) * compareString(va, vb,asc);
    };

private static final BiFunction<Boolean, String, Comparator<Node>> StringNodeAttributeComparatorSupplier = (asc, attrName) ->
    (Node a, Node b) -> {
      final String va = a == null ? null : a.hasAttributes() ?
          ((Element) a).getAttribute(attrName) : null;
      final String vb = b == null ? null : b.hasAttributes() ?
          ((Element) b).getAttribute(attrName) : null;
      return (asc ? 1 : -1) * compareString(va, vb,asc);
    };

private static <T extends Comparable<T>> Comparator<Node> nodeComparator(
    final boolean asc,
    final boolean useAttr,
    final String attribute,
    final Constructor<T> constructor
) {
  return (Node a, Node b) -> {
    if (a == null && b == null) {
      return 0;
    } else if (a == null) {
      return (asc ? -1 : 1);
    } else if (b == null) {
      return (asc ? 1 : -1);
    }

    T aV;
    try {
      final String aStr;
      if (useAttr) {
        aStr = a.hasAttributes() ? ((Element) a).getAttribute(attribute) : null;
      } else {
        aStr = a.getTextContent();
      }
      aV = aStr == null || aStr.matches("\\s+") ? null : constructor.newInstance(aStr);
    } catch (Exception ignored) {
      aV = null;
    }

    T bV;
    try {
      final String bStr;
      if (useAttr) {
        bStr = b.hasAttributes() ? ((Element) b).getAttribute(attribute) : null;
      } else {
        bStr = b.getTextContent();
      }
      bV = bStr == null || bStr.matches("\\s+") ? null : constructor.newInstance(bStr);
    } catch (Exception ignored) {
      bV = null;
    }

    final int ret;
    if (aV == null && bV == null) {
      ret = 0;
    } else if (aV == null) {
      ret = -1;
    } else if (bV == null) {
      ret = 1;
    } else {
      ret = aV.compareTo(bV);
    }
    return (asc ? 1 : -1) * ret;
  };
}


/**
 * Method to sort any NodeList by an attribute all nodes must have. <br>If the attribute is absent for a signle
 * {@link Node} or the {@link NodeList} does contain elements without Attributes, null is used instead. <br>If
 * <code>asc</code> is
 * <code>true</code>, nulls first, else nulls last.
 *
 * @param nodeList The {@link NodeList} containing all {@link Node} to sort.
 * @param attribute Name of the attribute to extract and compare
 * @param asc <code>true</code>: ascending, <code>false</code>: descending
 * @param compareType Optional class to use for comparison. Must implement {@link Comparable} and have Constructor
 * that takes a single {@link String} argument. If <code>null</code> is supplied, {@link String} is used.
 * @return A collection of the {@link Node}s passed as {@link NodeList}
 * @throws RuntimeException If <code>compareType</code> does not have a constructor taking a single {@link String}
 * argument. Also, if the comparator created does violate the {@link Comparator} contract, an
 * {@link IllegalArgumentException} is raised.
 * @implNote Exceptions during calls of the single String argument constructor of <code>compareType</code> are
 * ignored. Values are substituted by <code>null</code>
 */
public static <T extends Comparable<T>> Collection<Node> sortNodesByAttribute(
    final NodeList nodeList,
    String attribute,
    boolean asc,
    Class<T> compareType) {

  final Comparator<Node> nodeComparator;
  if (compareType == null) {
    nodeComparator = StringNodeAttributeComparatorSupplier.apply(asc, attribute);
  } else {
    final Constructor<T> constructor;
    try {
      constructor = compareType.getDeclaredConstructor(String.class);
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(
          "Cannot compare Node Attribute '" + attribute + "' using the Type '" + compareType.getName()
              + "': No Constructor available that takes a single String argument.", e);
    }
    nodeComparator = nodeComparator(asc, true, attribute, constructor);
  }
  final List<Node> nodes = new ArrayList<>(nodeListCollection(nodeList));
  nodes.sort(nodeComparator);
  return nodes;
}

/**
 * Method to sort any NodeList by their text content using an optional type. <br>If
 * <code>asc</code> is
 * <code>true</code>, nulls first, else nulls last.
 *
 * @param nodeList The {@link NodeList} containing all {@link Node}s to sort.
 * @param asc <code>true</code>: ascending, <code>false</code>: descending
 * @param compareType Optional class to use for comparison. Must implement {@link Comparable} and have Constructor
 * that takes a single {@link String} argument. If <code>null</code> is supplied, {@link String} is used.
 * @return A collection of the {@link Node}s passed as {@link NodeList}
 * @throws RuntimeException If <code>compareType</code> does not have a constructor taking a single {@link String}
 * argument. Also, if the comparator created does violate the {@link Comparator} contract, an
 * {@link IllegalArgumentException} is raised.
 * @implNote Exceptions during calls of the single String argument constructor of <code>compareType</code> are
 * ignored. Values are substituted by <code>null</code>
 */
public static <T extends Comparable<T>> Collection<Node> sortNodes(
    final NodeList nodeList,
    boolean asc,
    Class<T> compareType) {

  final Comparator<Node> nodeComparator;
  if (compareType == null) {
    nodeComparator = StringNodeValueComparatorSupplier.apply(asc);
  } else {
    final Constructor<T> constructor;
    try {
      constructor = compareType.getDeclaredConstructor(String.class);
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(
          "Cannot compare Nodes using the Type '" + compareType.getName()
              + "': No Constructor available that takes a single String argument.", e);
    }
    nodeComparator = nodeComparator(asc, false, null, constructor);
  }
  final List<Node> nodes = new ArrayList<>(nodeListCollection(nodeList));
  nodes.sort(nodeComparator);
  return nodes;
}
trh0
  • 1
  • 1
-1

I have a quite similar problem. I need to have always the same attribute for first. Example :

<h50row a="1" xidx="1" c="1"></h50row>
<h50row a="2" b="2" xidx="2"></h50row>

must become

<h50row xidx="1" a="1" c="1"></h50row>
<h50row xidx="2" a="2" b="2"></h50row>

I found a solution with a regex:

test = "<h50row a=\"1\" xidx=\"1\" c=\"1\"></h50row>";
test = test.replaceAll("(<h5.*row)(.*)(.xidx=\"\\w*\")([^>]*)(>)", "$1$3$2$4$5");

Hope you find this usefull