Kindelemente mit JDOM eines bestimmten Attributes auslesen

Wambui

Aktives Mitglied
Hallo,

ich suche eine Möglichkeit, aus dieser XML-Datei
Code:
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <city name="München">
    <distance>221.357</distance>
    <duration>2.32</duration>
  </city>
  <city name="Frankfurt">
    <distance>204.365</distance>
    <duration>2.049</duration>
  </city>
  <city name="Köln">
    <distance>367.153</distance>
    <duration>3.404</duration>
  </city>
</root>

mit dem entsprechenden Attribut der City-Elemente dei passenden Werte der Kindelemente auszulesen.
Dieser Versuch liefert eine NullPointerException:
Java:
package com.company;

import org.jdom2.*;
import org.jdom2.input.SAXBuilder;
import java.io.IOException;


public class ReadXmlFile {
    public static void main(String[] args) throws IOException, JDOMException {
        SAXBuilder builder = new SAXBuilder();
        Document jdomDoc = builder.build("City.xml");

        Element root = jdomDoc.getRootElement();



        for (int i = 0; i < root.getContentSize(); i++) {
            Content content = root.getContent().get(i);
            if (content instanceof Element) {
                if ("München".equals(((Element) content).getChild("city").getAttributeValue("name"))) {
                    System.out.println(((Element) content).getChildText("distance"));
                }

            }

        }
    }
}

Während diese Version alle Werte der Distance-Elemente aller City-Elemente liefert:
Java:
package com.company;

import org.jdom2.*;
import org.jdom2.input.SAXBuilder;
import java.io.IOException;

public class ReadXmlFile {
    public static void main(String[] args) throws IOException, JDOMException {
        SAXBuilder builder = new SAXBuilder();
        Document jdomDoc = builder.build("StuttgartCity.xml"); //Bildet gesamtes XML im Speicher ab

        Element root = jdomDoc.getRootElement();
        
        for (int i = 0; i < root.getContentSize(); i++) {
            Content content = root.getContent().get(i);
            if (content instanceof Element) {
                if ("München".equals(root.getChild("city").getAttributeValue("name"))) {
                    System.out.println(((Element) content).getChildText("distance"));
                }

            }

        }
    }
}

Wie kann ich mit jdom2 gezielt nur die Kindelemente mit einem bestimmten Attribut im Elternelement auslesen?

Gruß
Wambui
 
Zunächst mal solltest du deinen Bandwurm-Code mit den verketten Methodenaufrufen "aufknoten", damit du siehst, welches Element null ist. Dan solltest du auch erkennen, wieso es null ist, und warum der Code aus deinem ersten Beispiel nicht funktioniert.
Tipp: die Variable "root" steht für das Wurzelelement "root". "getContent" liefert dir, wie ich gerade aus der jdom-API gerlent habe, ALLE Kindknoten. Manche der Knoten in "getContent" heissen zwar "city", aber keiner der Kindknoten von "root" hat ein Kindknoten mit dem Namen "city".
Ich würde eher so vorgehen:
root.getChildren("city") liefert dir eine Liste mit allen city-Elementen unterhalb von "root".
Mit getAttributeValue("name") bekommst du den Namen der Stadt. Falls der passt, kannst du über getChild("distance") und getChild("duration") auf die jeweiligen Kindelemente von "city" zugreifen, und kannst daraus dann den Textinhalt (wahrscheinlich wieder über getContent()) auslesen.
 
Danke, jetzt sehe ich das auch. Der ursprüngliche Gedankenansatz war, wenn ein Wert wie "München" in dem Attribute-Namen des Kindelements "city" auftaucht, dann zeige mir den gewünschten Text aus den Kindelementen von City mit dem passenden Attribut an.
Da komme ich nicht nicht ran.
Dass in
Java:
Element root = jdomDoc.getRootElement();
das gesamte XML-Document abgebildet wird, ist mir bewusst.
Es gibt mit org.jdom2.filter.Filter und org.jdom2.filter.AttributeFilter auch Interfaces mit denen man gegebenfalls das machen kann, was ich will. Ich finde aber keine gescheiten Examples, die die Anwendung dokumentieren und zeigen.
 
Zuletzt bearbeitet:
Suchst du vielleich so etwas?
Java:
public static void main(String[] args) {
    try {
        readXml();
    }
    catch(JDOMException ex) {
        System.out.println(ex);
    }
    catch(IOException ex) {
        System.out.println(ex);
    }
}

private static void readXml() throws JDOMException, IOException {
    SAXBuilder builder = new SAXBuilder();
    Document jdomDoc = builder.build("city.xml");
    Element root = jdomDoc.getRootElement();
    List<Element> cities = root.getChildren();
    for (Element element : cities) {
        System.out.println(element.getAttributeValue("name"));        
        for(Element e : element.getChildren())  {
            System.out.println("-" + e.getName() + ": " + e.getValue() );
        }
    }
}
 
Im Prinzip habe ich das in anderer Schreibweise ohne "getName" auch schon. Das ist aber nicht mein Ziel.
Java:
private static void readXml() throws JDOMException, IOException {
    SAXBuilder builder = new SAXBuilder();
    Document jdomDoc = builder.build("city.xml");
    Element root = jdomDoc.getRootElement();
    List<Element> cities = root.getChildren();
    for (Element element : cities) {
        System.out.println(element.getAttributeValue("name"));        
        for(Element e : element.getChildren())  {
            System.out.println("-" + e.getName() + ": " + e.getValue() );
        }
    }

Ich habe ja nicht umsonst versucht, die if-Abfrage in Verbindung mit dem Attribut-Vergleich nach dem Namen der Stadt eingebaut. Abhängig von dem Attribut werden nur die zwei Elemente Dauer und Entfernung benötigt, denn nur die zwei werden später in einer GUI an die betreffenden Objekte übergeben. Da macht eine Liste aller Städte inklusive aller Elemente keinen Sinn.
 
Du möchtest also nur die Child- Elemente von einer Stadt, die du auswählen möchtest. Dann vielleicht so etwas?
Java:
import java.io.IOException;
import java.util.List;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;


public class Test {
    private static String xmlFile = "City.xml";
    public static void main(String[] args) {
        printCityElements("München");
    }
    
    private static List<Element> getRootElements() {
        SAXBuilder builder = null;
        Document jdomDoc = null;
        try {
            builder = new SAXBuilder();
            jdomDoc = builder.build(xmlFile);        
        } catch (JDOMException ex) {
            System.err.println(ex);
        } catch (IOException ex) {
            System.err.println(ex);
        }
        if(builder != null && jdomDoc != null) {
            return jdomDoc.getRootElement().getChildren();
        }
        else {
            return null;
        }
    }
    
    private static List<Element> getCityElements(String cityName){
        List<Element> cities = getRootElements();
        if(cities != null) {
            for (Element element : cities) {
                if(element.getAttributeValue("name").equals(cityName)) {
                    return element.getChildren();
                }
            }
        }
        return null;
    }
    
    private static void printCityElements(String cityName) {
        List<Element> elements = getCityElements(cityName);
        if(elements != null) {
            if(elements.size() > 0) {
                System.out.println(cityName);
                for (Element element : elements) {
                    System.out.println("-" + element.getName() + ": " + element.getValue() );
                }
            }
            else {
                System.err.println("Keine Elemente für " + cityName + " gefunden!");
            }
        }
    }
}
Statt printCityElements("München"); kannst du dann eine Methode erstellen, welche distance und duration in das Gui füllt.
 
Ja, das sieht bereits besser aus. Und ich verstehe auch meinen Denkfehler. Ich wollte aus der Liste aller Elemente die Speziellen herausfiltern. Ich bin nicht auf die Idee gekommen, dass das ein Weg über zwei Schritte mit zwei Methoden ist.

Vielen Dank, das hilft mir in jedem Fall weiter,

Wambui
 

Neue Themen


Zurück
Oben