# java xml sortieren



## Thorus (21. Mrz 2011)

Hi,

Ich moechte gerne ein xml-file auslesen, Nodes die auf derselben Ebene sind alphabetischen sortieren lassen, und dann das xml-file wieder schreiben...

Kann mir hierzu jemand ein link zu nem tutorial fuer einsteiger schicken, weil ich komme nicht so recht voran, kann JDOM das, oder sollte ich hierbei besser ein anderes API benutzen?

Ich hoffe ihr koennt mir helfen!!

Danke
Thorus


----------



## kirax (21. Mrz 2011)

Mir fällt da spontan XML Binding ein (JAXB).
Damit kannst du die XML-Nodes wie Objekte handhaben und mit rumschaukeln.

Benötigt aber ein XSD, von daher bisschen aufwändiger.


----------



## timbeau (21. Mrz 2011)

Mir würde die Möglichkeit einfallen Objekte zu sortieren mittels Collections.sort(Collection);

Also xml-Daten einlesen, sortieren, schreiben. 

A la http://www.java-forum.org/xml-co/24161-root-getchildren-liste-sortieren-classcastexception.html


----------



## Thorus (22. Mrz 2011)

timbeau hat gesagt.:


> Mir würde die Möglichkeit einfallen Objekte zu sortieren mittels Collections.sort(Collection);
> 
> Also xml-Daten einlesen, sortieren, schreiben.
> 
> A la http://www.java-forum.org/xml-co/24161-root-getchildren-liste-sortieren-classcastexception.html



Hab mir das mal angeschaut und das ist glaube ich das was ich brauche, allerdings weiss ich nicht woher in der Klasse AngComparator das Objekt Element kommt, welches muss ich da importieren?

Danke schon mal fuer eure Hilfe


----------



## Thorus (24. Mrz 2011)

Das mit dem Element habe ich jetzt hinbekommen (javax.swing.text.html.parser.Element) , allerdings habe ich jetzt ein aehnliches problem mit dem part:


```
Document doc = getDocument(datei); 
    Element root = getRoot(doc); 
    AngComparator angComp = new AngComparator();
    ArrayList temp = new ArrayList(root.getChildren());
    Collection.sort(temp, angComp);
    root.removeChildren();
    root.setContent(temp);
```

In welchem Paket liegt das Objekt Document, und in welchem Format erwartet er die datei?

Selbes problem wie beim AngComparator auch hier wieder mit Element, javax.swing.text.html.parser.Element passt nicht weil er dann die angegeben Methoden nicht vorhanden sind....


----------



## timbeau (24. Mrz 2011)

Falsche Paket, sorry ich dachte ich hätte schon geantwortet. Das ist alles in org.jdom


```
import org.jdom.Document;
import org.jdom.Element;
```


----------



## Thorus (24. Mrz 2011)

Danke fuer deine Antwort 

Wenn ich diesen beide einbinde bekomme ich folgende Fehlermeldungen:

Methode getDocument(FileReader) nicht vorhanden,
Methode getRoot(Document) nicht vorhanden,
Fehlendes Argument fuer removeChildren() (String oder String,Namespace)

Muss ich bei removeChildren das erste Child eintragen sodass quasi alles geloescht wird, damit dass sortierte xml eingetragen werden kann?

Kannst du mir hierbei helfen?


----------



## timbeau (24. Mrz 2011)

Ich weiß ja nicht wo du deinen Code da oben her hast aber "getDocument(Datei)" lässt auf eine Methode schließen die irgendwo stehen müsste in deiner Java-Klasse.

Allgemeint erhälst du Documente aus bestehenden Dateien z.B. so:


```
SAXBuilder builder = new SAXBuilder();
			Document doc = builder.build(new File(destinationXMLfile));
```

wobei "destinationXMLfile" der Pfad zu deiner Datei ist. 

Ein bestehendes Document kann man so schreiben: 


```
public void writeFile(Document doc, String filename) throws IOException {
        XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
        FileOutputStream output = new FileOutputStream(filename);
        outputter.output(doc, output);

    }
```

edit: Bzgl der Children musst du wie er sagt den Bezeichner der zu löschenden Kinder angeben.

p.s.: Es ist schwer zu raten, was du genau machen willst.


----------



## Thorus (24. Mrz 2011)

Machen moechte ich, ein komplettes XML-File einlesen dieses alphabetisch sortieren, und danach wieder in die Datei schreiben, ohne dabei natuerlich den Sinn des XML-Files zu veraendern, sprich die Elemente sollen auf derselben Ebene bleiben aber alphabetisch sortiert werden, ich hoffe es ist verstaendlich was ich meine?

edit: Den Code habe ich aus dem Beispiel auf das du mich hingewiesen hast...


----------



## timbeau (24. Mrz 2011)

Okay, ja dann sollte das ja helfen. Ansonsten gibts bei thorsten horn einiges. Mal googlen nach jdom bringt viele Beispiele.


----------



## Thorus (29. Mrz 2011)

Hab mich jetzt mit JDOM und XML beschaeftigt, und bin jetzt soweit dass das xml-file eingelesen wird, DOM kenn ich gluecklicherweise schon von HTML und habe damit keine probleme, allerdings funktioniert das sortieren nicht!

```
SAXBuilder builder1 = new SAXBuilder();
		            Document doc1 = null;
					try {
						doc1 = builder1.build(textXML1.getText());
					} catch (JDOMException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
			        Element root1 = doc1.getRootElement();
			        AngComparator angComp1 = new AngComparator();
			        ArrayList temp1 = new ArrayList(root1.getChildren());
			        Collections.sort(temp1, angComp1);
			        root1.removeContent();
			        root1.setContent(temp1);
			        XMLOutputter out1 = new XMLOutputter();
			        try {
						FileOutputStream output1 = new FileOutputStream(textXML1.getText());
						out1.output(doc1,output1);
					} catch (FileNotFoundException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					} catch (IOException e2) {
						// TODO Auto-generated catch block
						e2.printStackTrace();
					}
```

In diesem Abschnitt wird der Pfad aus einem Textfeld der GUI genommen das XML-File eingelesen, dann mit dem AngComparator sortiert ( was nicht richtig funktioniert ), und danach wieder in das file geschrieben wird.

Hier noch der AngComparator:


```
import java.util.Comparator;
import org.jdom.Element;



public class AngComparator implements Comparator {
	
	public int compare(Object arg0, Object arg1) {
		int result = 0;
		Element elm0 = (Element) arg0;
		Element elm1 = (Element) arg1;
		String feld0 = elm0.getAttribute("name").getValue();
		String feld1 = elm1.getAttribute("name").getValue();
		
		result = feld0.compareTo(feld1);

		return result;
	}
	
}
```

getestet habe ich das mit dem folgenden beispiel:

[XML]
<?xml version="1.0" encoding="UTF-8"?>
<root>
<buchstaben>
	<a />
	<d />
	<b />
	<c />
</buchstaben>
</root>
[/XML]

sortiert sollte dann ja eig. das rauskommen:

[XML]
<?xml version="1.0" encoding="UTF-8"?>
<root><buchstaben>
	<a />
	<b />
	<c />
	<d />
</buchstaben></root>
[/XML]

Allerdings wird bei der Sortierung nichts veraendert...

Was mache ich falsch?? Habe dazu nichts gefunden bei google...


----------



## Bierhumpen (29. Mrz 2011)

Zum einen hat dein root nur 1 Kindelement, also gibt's da nicht viel zu sortieren und zum zweiten hast du überhaupt gar keine name Attribute in deiner XML.


----------



## kirax (29. Mrz 2011)

Du müsstest rekursiv alles durchsortieren.


----------



## Niki (29. Mrz 2011)

```
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

public class XMLSorter {

	private Comparator<Element> comp = new ElementComparator();

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		new XMLSorter().sortXML(new File("sort.xml"));

	}


	public void sortXML(File xml) throws IOException, JDOMException{
		SAXBuilder saxBuilder = new SAXBuilder();
		Document doc = saxBuilder.build(xml);
		Element root = doc.getRootElement();

		sort(root);

		XMLOutputter outputter = new XMLOutputter();
		FileOutputStream fos = new FileOutputStream(xml);
		outputter.output(doc, fos);
		fos.flush();
		fos.close();
	}

	@SuppressWarnings("unchecked")
	private void sort(Element e){
		List<Element> children = e.getChildren();
		if(children.isEmpty())
			return;

		for(Element child : children)
			sort(child);

		List<Element> tmp = new ArrayList<Element>(children);

		Collections.sort(tmp, comp);
		e.removeContent();
		e.addContent(tmp);
	}

	private static class ElementComparator implements Comparator<Element>{
		public int compare(Element o1, Element o2) {
			return o1.getName().compareTo(o2.getName());
		}
	}

}
```

das sollte genau das sein was du suchst


----------



## Thorus (30. Mrz 2011)

Ja das ist fast genau das was ich brauche,

wenn ich das richtig sehe wird das auch hier ueber Collections.sort sortiert, oder?

denn bspw.: 

[XML]
<b key="467">
    467
</b>
<b key="367">
    367
</b>
[/XML]

wird nicht nach 

[XML]
<b key="367">
    367
</b>
<b key="467">
    467
</b>
[/XML]

sortiert, sondern in seiner Reihenfolge belassen, gibt es eine Moeglichkeit, Collections.sort anzupassen sodass sollten die Elemente denselben namen haben dass dann nach Attribut oder Content sortiert wird? (ich brauche eine eindeutige Reihenfolge)


----------



## Niki (30. Mrz 2011)

klar geht das. das hat aber nichts mit Collections.sort zu tun, sondern mit der compare Methode im Comparator:


```
public int compare(Element o1, Element o2) {
			if(o1.getName().equals(o2.getName())){
				return o1.getText().compareTo(o2.getText());			}


			return o1.getName().compareTo(o2.getName());
		}
```


----------



## Thorus (1. Apr 2011)

Habe den Comparator jetzt meinen Beduerfnissen angepasst, allerdings bekomme ich jetzt immer eine NullPointerException


```
private static class ElementComparator implements Comparator<Element>{
        public int compare(Element o1, Element o2) {
            if(o1.getName().equals(o2.getName())){
            	boolean hasConfigidChild = false;
            	try{
            		o1.getChild("ConfigID");
            		o2.getChild("ConfigID");
            		hasConfigidChild = true;
            	}
            	catch(NullPointerException e){
            		System.out.println(e);
            		hasConfigidChild = false;
            	}
            	
				if(hasConfigidChild){
						return o1.getChild("ConfigID").toString().compareTo(o2.getChild("ConfigID").toString());
                }
            }
 
            return o1.getName().compareTo(o2.getName());
        }
    }
```

Er soll, falls beide Elemente denselben Namen haben, schauen ob beide elemente ein Kind haben welches ConfigID heisst, und falls dass der Fall ist nach dem Content dieses Childs sortieren...

Allerdings bekomme ich eine NullPointerException bei "return o1.getChild("ConfigID").toString().compareTo(o2.getChild("ConfigID").toString());", aber eig sollte doch das try/catch diese abfangen oder nicht?


----------



## Niki (1. Apr 2011)

probier mal

```
public int compare(Element o1, Element o2) {
            if(o1.getName().equals(o2.getName())){
                 if(o1.getChild("ConfigID") != null && o2.getChild("ConfigID") != null){
                        return o1.getChild("ConfigID").toString().compareTo(o2.getChild("ConfigID").toString());
                }
            }
 
            return o1.getName().compareTo(o2.getName());
        }
```


----------



## Thorus (1. Apr 2011)

Ok das geht jetzt ohne NullPointerException, allerdings bekomme ich jetzt einen Fehler beim Starten den ich nicht kenne, und sortieren tut er folgendes XML auch nicht richtig (benutze ich zum testen):

[XML]
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a key="125">
		125
</a>
<b key="567">
        <ConfigID>
		127
	</ConfigID>
</b>
<b key="867">
         <ConfigID>
		67
	</ConfigID>
</b>
<f key="135">
                 <baum>
			gruen
		</baum>
</f></root>

[/XML]

Wenn es richtig funktionieren wuerde sollte, er <b key="867"> vor <b key="567"> einordnen...

Der Fehler lautet:

java.lang.NoClassDefFoundError: com/sun/java/accessibility/util/TopLevelWindowListener
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(Unknown Source)
	at java.security.SecureClassLoader.defineClass(Unknown Source)
	at java.net.URLClassLoader.defineClass(Unknown Source)
	at java.net.URLClassLoader.access$000(Unknown Source)
	at java.net.URLClassLoader$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at java.net.FactoryURLClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClassInternal(Unknown Source)


----------



## Niki (1. Apr 2011)

dann tausche toString() mit getValue() aus:

javadoc lesen hilft


```
if(o1.getName().equals(o2.getName())){
                if(o1.getChild("ConfigID") != null && o2.getChild("ConfigID") != null){
                       return o1.getChild("ConfigID").getValue().compareTo(o2.getChild("ConfigID").getValue());
               }
}
```


----------



## Thorus (4. Apr 2011)

Danke, habs jetzt mal mit javadoc probiert, allerdings ohne Erfolg, bei getValue, getText und getTextTrim sortiert er trotzdem nicht...


```
public int compare(Element o1, Element o2) {
            if(o1.getName().equals(o2.getName())){
                 if(o1.getChild("ConfigID") != null && o2.getChild("ConfigID") != null){
                	 	System.out.println(o1.getChild("ConfigID").getTextTrim());
                	 	System.out.println(o2.getChild("ConfigID").getTextTrim());
                        return o1.getChild("ConfigID").getTextTrim().compareTo(o2.getChild("ConfigID").getTextTrim());
                }
            }
 
            return o1.getName().compareTo(o2.getName());
        }
    }
```
er gibt aber 67 und 127 korrekt aus, veraendert aber deren reihenfolge nicht....


----------



## Niki (4. Apr 2011)

dann wandel den String in Integer um und vergleich diese


```
public int compare(Element o1, Element o2) {
            if(o1.getName().equals(o2.getName())){
                 if(o1.getChild("ConfigID") != null && o2.getChild("ConfigID") != null){
                        Integer i1 = new Integer(o1.getChild("ConfigID").getTextTrim());
                        Integer i2 = new Integer(o2.getChild("ConfigID").getTextTrim());
                        return i1.compareTo(i2);
                }
            }
 
            return o1.getName().compareTo(o2.getName());
        }
```


----------

