Java Doc Transformer re-arranging xml [duplicate] - java
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.
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 ;-)
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.
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.
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.
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.
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.
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.
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.
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:
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>
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" />
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
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;
}
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
Related
How to compare GraphQL query tree in java
My spring-boot application is generating GraphQL queries, however I want to compare that query in my test. So basically I have two strings where the first one is containing the actual value and the latter one the expected value. I want to parse that in a class or tree node so I can compare them if both of them are equal. So even if the order of the fields are different, I need to know if it's the same query. So for example we have these two queries: Actual: query Query { car { brand color year } person { name age } } Expected query Query { person { age name } car { brand color year } } I expect that these queries both are semantically the same. I tried Parser parser = new Parser(); Document expectedDocument = parser.parseDocument(expectedValue); Document actualDocument = parser.parseDocument(actualValue); if (expectedDocument.isEqualTo(actualDocument)) { return MatchResult.exactMatch(); } But found out that it does nothing since the isEqualTo is doing this: public boolean isEqualTo(Node o) { if (this == o) { return true; } else { return o != null && this.getClass() == o.getClass(); } } I know with JSON I can use Jackson for this purpose and compare treenodes, or parsing it into a Java object and have my own equals() implementation, but I don't know how to do that for GraphQL Java. How can I parse my GraphQL query string into an object so that I can compare it?
I have recently solved this problem myself. You can reduce the query to a hash and compare the values. You can account for varied query order by utilizing a tree structure. You can take advantage of the QueryTraverser and QueryReducer to accomplish this. First you can create the QueryTraverser, the exact method for creating this will depend on your execution point. Assuming you are doing it in the AsyncExecutor with access to the ExecutionContext the below code snippet will suffice. But you can do this in instrumentation or the data fetcher itself if you so choose; val queryTraverser = QueryTraverser.newQueryTraverser() .schema(context.graphQLSchema) .document(context.document .operationName(context.operationDefinition.name) .variables(context.executionInput?.variables ?: emptyMap()) .build() Next you will need to provide an implementation of the reducer, and some accumulation object that can add each field to a tree structure. Here is a simplified version of an accumulation object class MyAccumulation { /** * A sorted map of the field node of the query and its arguments */ private val fieldPaths = TreeMap<String, String>() /** * Add a given field and arguments to the sorted map. */ fun addFieldPath(path: String, arguments: String) { fields[path] = arguments } /** * Function to generate the query hash */ fun toHash(): String { val joinedFields = fieldPaths.entries .joinToString("") { "${it.key}[${it.value}]" } return HashingLibrary.hashingfunction(joinedFields) } A sample reducer implementation would look like the below; class MyReducer : QueryReducer<MyAccumulation> { override fun reduceField( fieldEnvironment: QueryVisitorFieldEnvironment, acc: MyAccumulation ): MyAccumulation { if (fieldEnvironment.isTypeNameIntrospectionField) { return acc } // Get your field path, this should account for // the same node with different parents, and you should recursively // traverse the parent environment to construct this val fieldPath = getFieldPath(fieldEnvironment) // Provide a reproduceable stringified arguments string val arguments = getArguments(fieldEnvironment.arguments) acc.addFieldPath(fieldPath, arguments) return acc } } Finally put it all together; val queryHash = queryTraverser .reducePreOrder(MyReducer(), MyAccumulation()) .toHash() You can now generate a repdocueable hash for a query that does not care about the query structure, only the actual fields that were requested. Note: These code snippets are in kotlin but are transposable to Java.
Depending on how important is to perform this comparison you can inspect all the elements of Document to determine equality. If this is to optimize and return the same result for the same input I would totally recommend just compare the strings and kept two entries (one for each string input). If you really want to go for the deep compare route, you can check the selectionSet and compare each selection. Take a look at the screenshot: You can also give EqualsBuilder.html.reflectionEquals(Object,Object) a try but it might inspect too deep (I tried and returned false)
How to get the property data type in FileNet P8
In FileNet P8, I need to get the datatype of the property of a custom class using JAVA API. Is there any way to do the same?
This should give you an idea of what you need to do: //SymbolicName of the property we will search for. String strSearchName = PropertyNames.DATE_LAST_MODIFIED; //Document (or other object) that we will use to get classDescription. Document document = (Document) arg0; PropertyDescription objPropDesc = null; PropertyDescriptionList pdl = document.get_ClassDescription().get_PropertyDescriptions(); Iterator<?> iter = pdl.iterator(); while (iter.hasNext()) { objPropDesc = (PropertyDescription) iter.next(); // Get SymbolicName property from the property cache String strPropDescSymbolicName = objPropDesc.get_SymbolicName(); if (strPropDescSymbolicName.equalsIgnoreCase(strSearchName)) { // PropertyDescription object found System.out.println("Property description selected: " + strPropDescSymbolicName); System.out.println(objPropDesc); TypeID type = objPropDesc.get_DataType(); System.out.println(type.toString()); break; } } The idea is to: Take an object (Document in this case). Get its Class Description. Get the list of Property Descriptions from the Class Description. Loop through the Property Descritpions until you locate the Description you are trying to find. Get the TypeId from the Property Description. The TypeId contains the information you need to know what the Type is of the Property. I borrowed code from here : Working with Properties You should also familiarize yourself with this : Properties Edit to add a different method: In the case of creating documents, we need to be able to obtain the Class Description object. This means we will need to perform additional round trips. // Get the ClassDescription String strSymbolicName = "myClassName"; ClassDescription objClassDesc = Factory.ClassDescription.fetchInstance(myObjStore, strSymbolicName, null); // find the PropertyDescription PropertyDescription pds = null; PropertyDescriptionList pdl = objClassDesc.get_PropertyDescriptions(); Iterator<?> itr = pdl.iterator(); while(itr.hasNext()){ pds = (PropertyDescription) itr.next(); System.out.println("Symbolic Name is "+pds.get_SymbolicName()+" DataType is "+pds.get_DataType().toString()); } // You can now use it in a loop of several documents if you wish. ... Take a look here as well : Working With Classes
Christopher Powell's answer is correct, there is one thing it does not cover, though (depending on the definition of the custom class in question). Consider this as a "best practice" or just an extension of the code borrowed from the URL that Christopher mentioned. The FileNet P8 class hierarchy allows for inheritance of property definitions. Simple example: one can search through the 'Document' class - which is the root class of the class hierarchy - of an object store, and use some property of a subclass in the search sql. Imagine Subclass1 as an immediate subclass of Document. Subclass1 has a property Property1. Even if Document does not have this property in its class description, a search in Document with Property1='somevalue' will return objects of Subclass1 (if there is a match with 'somevalue'). However, objClassDesc.get_PropertyDescriptions(); won't return property descriptions of subclasses, thus you might end up with API_PROPERTY_NOT_IN_CACHE errors. To give you a good starting point if you are facing this case, look at below code: PropertyDescriptionList ownProps = objClassDesc.get_PropertyDescriptions(); PropertyDescriptionList subclassProps = null; if (objClassDesc.get_HasProperSubclassProperties()) { logger.debug("Document class '"+documentClassname+"' supports 'include descendant properties' queries, including descendant properties."); subclassProps = objClassDesc.get_ProperSubclassPropertyDescriptions(); } List<PropertyDescription> result = mergePropertyDescriptionLists(ownProps, subclassProps); If you need to merge those two lists, you are better off using a List of PropertyDescription objects instead of PropertyDescriptionList: the ones returned by the server are read-only. #SuppressWarnings("unchecked") protected List<PropertyDescription> mergePropertyDescriptionLists(PropertyDescriptionList list1, PropertyDescriptionList list2) throws Exception { try { #SuppressWarnings("unchecked") List<PropertyDescription> mergedList = new ArrayList<PropertyDescription>(); if (list1 != null && (list1.size() > 0)) { mergedList.addAll(list1); } if (list2 != null && (list2.size() > 0)) { mergedList.addAll(list2); } return mergedList; } catch (Throwable t) { throw new Exception("Failed to merge property description lists.", t); } }
Better way to represent array in java properties file
I'm currently making a .properties file that needs to be loaded and transformed into an array. But there is a possibility of anywhere from 0-25 of each of the property keys to exist. I tried a few implementations but i'm just stuck at doing this cleanly. Anyone have any ideas? foo.1.filename=foo.txt foo.1.expire=200 foo.2.filename=foo2.txt foo.2.expire=10 etc more foo's bar.1.filename=bar.txt bar.1.expire=100 where I'll assemble the filename/expire pairings into a data object, as part of an array for each parent property element like foo[myobject] Formatting of the properties file can change, I'm open to ideas.
I can suggest using delimiters and using the String.split(delimiter) Example properties file: MON=0800#Something#Something1, Something2 prop.load(new FileInputStream("\\\\Myseccretnetwork\\Project\\props.properties")); String[]values = prop.get("MON").toString().split("#"); Hope that helps
Didn't exactly get your intent. Do check Apache Commons configuration library http://commons.apache.org/configuration/ You can have multiple values against a key as in key=value1,value2 and you can read this into an array as configuration.getAsStringArray("key")
Either define a delimiter that will not be a potential value or learn to use XML. If you still insist on using properties use one of the methods that will return a list of all keys. Your key appears to have three parts a group identifier (foo, bar) an index (1, 2) and then an element name (filename, expire). Get all the keys break them into their component parts. Create a List for each type of identifier, when processing the list use the identifier to determine which List to add to. Create you paired elements as you said and simply add to the list! If the index order is important either add that as a field to your paired elements or sort the keys before processing.
Use YAML files for properties, this supports properties as an array. Quick glance about YAML: A superset of JSON, it can do everything JSON can + more Simple to read Long properties into multiline values Supports comments Properties as Array YAML Validation
I have custom loading. Properties must be defined as: key.0=value0 key.1=value1 ... Custom loading: /** Return array from properties file. Array must be defined as "key.0=value0", "key.1=value1", ... */ public List<String> getSystemStringProperties(String key) { // result list List<String> result = new LinkedList<>(); // defining variable for assignment in loop condition part String value; // next value loading defined in condition part for(int i = 0; (value = YOUR_PROPERTY_OBJECT.getProperty(key + "." + i)) != null; i++) { result.add(value); } // return return result; }
I highly recommend using Apache Commons (http://commons.apache.org/configuration/). It has the ability to use an XML file as a configuration file. Using an XML structure makes it easy to represent arrays as lists of values rather than specially numbered properties.
here is another way to do by implementing yourself the mechanism. here we consider that the array should start with 0 and would have no hole between indice /** * get a string property's value * #param propKey property key * #param defaultValue default value if the property is not found * #return value */ public static String getSystemStringProperty(String propKey, String defaultValue) { String strProp = System.getProperty(propKey); if (strProp == null) { strProp = defaultValue; } return strProp; } /** * internal recursive method to get string properties (array) * #param curResult current result * #param paramName property key prefix * #param i current indice * #return array of property's values */ private static List<String> getSystemStringProperties(List<String> curResult, String paramName, int i) { String paramIValue = getSystemStringProperty(paramName + "." + String.valueOf(i), null); if (paramIValue == null) { return curResult; } curResult.add(paramIValue); return getSystemStringProperties(curResult, paramName, i+1); } /** * get the values from a property key prefix * #param paramName property key prefix * #return string array of values */ public static String[] getSystemStringProperties( String paramName) { List<String> stringProperties = getSystemStringProperties(new ArrayList<String>(), paramName, 0); return stringProperties.toArray(new String[stringProperties.size()]); } Here is a way to test : #Test public void should_be_able_to_get_array_of_properties() { System.setProperty("my.parameter.0", "ooO"); System.setProperty("my.parameter.1", "oO"); System.setProperty("my.parameter.2", "boo"); // WHEN String[] pluginParams = PropertiesHelper.getSystemStringProperties("my.parameter"); // THEN assertThat(pluginParams).isNotNull(); assertThat(pluginParams).containsExactly("ooO","oO","boo"); System.out.println(pluginParams[0].toString()); } hope this helps and all remarks are welcome..
As user 'Skip Head' already pointed out, csv or a any table file format would be a better fitt in your case. If it is an option for you, maybe this Table implementation might interest you.
I'd suggest having the properties file, reference a CSV file. Then parse the CSV file into a collection/array etc instead. Properties file seems wrong fit for this kind of data.
Actually all answers are wrong Easy: foo.[0]filename
Gracefully avoiding NullPointerException in Java
Consider this line: if (object.getAttribute("someAttr").equals("true")) { // .... Obviously this line is a potential bug, the attribute might be null and we will get a NullPointerException. So we need to refactor it to one of two choices: First option: if ("true".equals(object.getAttribute("someAttr"))) { // .... Second option: String attr = object.getAttribute("someAttr"); if (attr != null) { if (attr.equals("true")) { // .... The first option is awkward to read but more concise, while the second one is clear in intent, but verbose. Which option do you prefer in terms of readability?
I've always used if ("true".equals(object.getAttribute("someAttr"))) { // .... because although it is a little more difficult to read it's much less verbose and I think it's readable enough so you get used to it very easily
In the second option, you can take advantage of short-circuiting &&: String attr = object.getAttribute("someAttr"); if (attr != null && attr.equals("true")) { // ....
There are certain situations where the concise approach feels wrong to start with but effectively becomes idiomatic. This is one of them; the other is something like: String line; while ((line = bufferedReader.readLine()) != null) { // Use line } Side effects in a condition? Unthinkable! Except it's basically nicer than the alternatives, when you recognise the particular pattern. This pattern is similar - it's so common in Java that I'd expect any reasonably experienced developer to recognise it. The result is pleasantly concise. (Amusingly, I sometimes see C# code which uses the same idiom, unnecessarily - the equality operator works fine with strings in C#.) Bottom line: use the first version, and become familiar with it.
I like option 1 and I would argue that it is readable enough. Option 3 btw would be to introduce a getAttribute method that takes a default value as a parameter.
Always aspire for shorter code, given that both are functionaly equivalent. Especially in case like this where readability is not sacrificed.
Util.isEmpty(string) - returns string == null || string.trim().isEmpty() Util.notNull(string) returns "" if string == null, string otherwise. Util.isNotEmpty(string) returns ! Util.isEmpty(string) And we have a convention that for strings, Util.isEmpty(string) semantically means true and Util.isNotEmpty(string) semantically means false.
It's a very good question. I usually use the not graceful: if (object.getAttribute("someAttr") != null && object.getAttribute("someAttr").equals("true")) { // .... (and I will not use it anymore)
I have another answer; List<Map<String, Object>> group = jjDatabase.separateRow(db.Select("SELECT * FROM access_user_group WHERE user_id=1 ;")); there is not "group_c80" as column in 'access_user_group' in my database, so in get(0).get("group_c80") null pointer exception accords. But i handled it through below code : for (int j = 1; j < 100; j++) { String rulId="0";//defult value,to privent null pointer exeption in group_c try { rulId = group.get(0).get("group_c" + j)).toString(); } catch (Exception ex) { ServerLog.Print( "Handeled error in database for " + "group_c" + (j < 10 ? "0" + j : j) +"This error handeled and mot efect in program"); rulId = "0"; }}
Here is my approach, needs a PropertyUtil class though, but its only written once: /** * Generic method to encapsulate type casting and preventing nullPointers. * * #param <T> The Type expected from the result value. * #param o The object to cast. * #param typedDefault The default value, should be of Type T. * * #return Type casted o, of default. */ public static <T> T getOrDefault (Object o, T typedDefault) { if (null == o) { return typedDefault; } return (T) o; } Client code can do this: PropertyUtil.getOrDefault(obj.getAttribute("someAttr"), "").equals("true"); or, for a list: PropertyUtil.getOrDefault( genericObjectMap.get(MY_LIST_KEY), Collections.EMPTY_LIST ).contains(element); Or to a consumer of List, that would reject Object: consumeOnlyList( PropertyUtil.getOrDefault( enericObjectMap.get(MY_LIST_KEY), Collections.EMPTY_LIST ) ) The default might be an impl of the null object pattern https://en.wikipedia.org/wiki/Null_Object_pattern
Order of XML attributes after DOM processing
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.
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 ;-)
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.
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.
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.
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.
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.
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.
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.
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: 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> 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" />
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
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; }
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