# XML-File parsen mit Composite Pattern



## devo22 (4. Dez 2011)

hi,

ich soll eine Liste mit Büchern, CDs etc. aus einem XML-File (Struktur siehe unten) mit DOM parsen und die Struktur mit dem Composite-Pattern aufbauen. Schließlich soll der User dann in der Konsole den Namen eines Listenobjektes eingeben und die Konsole soll den dazugehörigen Preis zeigen.

Bevor ich aber den letzten Punkt der Aufgabenstellung angehen kann, habe ich aber noch folgende Probleme:

- ich konnte das File zwar parsen, aber ich habe keine Idee, wie ich die Listenobjekte weiterverarbeiten soll ...
- ich weiß zwar über das Composite-Pattern bescheid, aber bei dieser Aufgabenstellung weiß ich nicht, wie die Leaf- und Composition-Klassen aussehen sollen

Das hier habe ich bis jetzt:

[XML]<list name="root">
 <book name="B1" price="30" isbn="123"/>
 <list name="L1">
  <book name="B2" price="20" isbn="234"/>
  <list name="L2">
   <cd name="C1" price="15"/>
   <cd name="C2" price="5"/>
   <book name="B3" price="10" isbn="345"/>
  </list>
  <cd name="C3" price="15"/>
  <book name="B4" price="60" isbn="456"/> 
 </list>
</list>[/XML]


```
public class Composite {

	private String name;
	private int price;
	private int isbn;
			
	public Composite(String name, int price, int isbn) {
		this.name = name;
		this.price = price;
		this.isbn = isbn;
		
	}
	
	public String getName() {
		return name;
	}
	
	public int getPrice() {
		return price;
	}

	public int getISBN() {
		return isbn;
	}

	public void print() {
        System.out.println(price);
        }
    }
```


```
import java.io.*;
import java.util.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

public class CompositeDemo1
{
	Document document;
	String XMLname;
	
	public CompositeDemo1(String XMLname) {
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
	
		try {
		DocumentBuilder builder = builderFactory.newDocumentBuilder();
		document = builder.parse(new FileInputStream(XMLname));
		
		Element rootElement = document.getDocumentElement();
		String rootName = rootElement.getNodeName();
		
		NodeList nodeChildren = null; 
		nodeChildren = rootElement.getChildNodes();
		
		ArrayList<Composite> myItems = new ArrayList<Composite>(); 
		nodeChildren = rootElement.getElementsByTagName ("list");
		
		String name1="";
		int price1=0;
		int isbn1=0;
		
		for (int i=0; i<nodeChildren.getLength(); i++) {
		Composite c1 = new Composite(name1, price1, isbn1);
		myItems.add(c1);
		}
		}
		
		catch (ParserConfigurationException e) { e.printStackTrace();  }
		catch (SAXException e) { e.printStackTrace(); }
		catch (IOException e) { e.printStackTrace(); }
	}
	
	public static void main(String[] args) {
		try {
		BufferedReader r1 = new BufferedReader (new InputStreamReader(System.in));
		System.out.println("Give the filename of an XML file to parse:");
		String input = r1.readLine();
   
		CompositeDemo1 cd1 = new CompositeDemo1(input);
		}
		catch (IOException e) { e.printStackTrace(); }
   } }
```

Hat jemand Tipps für mich, wie ich jetzt weitermachen muss? Würde mir sehr helfen, danke im Voraus!


----------



## devo22 (4. Dez 2011)

hab weitergearbeitet und aus meiner Sicht fehlt nicht mehr viel ... nur leider passiert nichts, wenn ich einen Namen eines Listenobjektes in der Konsole eingebe. Was mache ich falsch? :autsch:


```
public interface Component {
	
	public String getName();
	public int getPrice();
	public int getISBN();
	
    }
```


```
public class Leaf implements Component {

	private String name;
	private int price;
	private int isbn;
	private static boolean next;
			
	public Leaf(String name, int price, int isbn) {
		this.name = name;
		this.price = price;
		this.isbn = isbn;
		
	}
	
	public String getName() {
		return name;
	}
	
	public int getPrice() {
		return price;
	}

	public int getISBN() {
		return isbn;
	}
	
	public static boolean hasNext() {
		return next;
	}

    }
```


```
public class Composite implements Component {

	private String name;
	private int price;
	private int isbn;
	private int sum;
	private int leaves;
			
	public Composite(String name, int sum, int isbn) {
		this.name = name;
		this.sum = sum;
		this.isbn = isbn;
		
	}
	
	public String getName() {
		return name;
	}
	
	public int getPrice() {
		return price;
	}
	
	public int getNumberofLeaves() {
		return leaves;
	}
	
	public int getSum() {
		sum = 0;
		while (Leaf.hasNext() == true) {
		sum = sum + price;
		}
		return sum;
	}
	
	public int getISBN() {
		return isbn;
	}

    }
```


```
import java.io.*;
import java.util.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

public class CompositeDemo1
{
	Document document;
	String XMLname;
	String searchString;
	
	public CompositeDemo1(String XMLname, String searchString) {
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
		Element rootElement;
		
		try {
		DocumentBuilder builder = builderFactory.newDocumentBuilder();
		document = builder.parse(new FileInputStream(XMLname));
		
		rootElement = document.getDocumentElement();
		String rootName = rootElement.getNodeName();
		
		NodeList nodeChildren = null; 
		nodeChildren = rootElement.getChildNodes();
		
		ArrayList<Composite> myItems = new ArrayList<Composite>();
		ArrayList<Leaf> myLeaves = new ArrayList<Leaf>();
		nodeChildren = rootElement.getElementsByTagName ("list");
		
		String name1="";
		int price1=0;
		int isbn1=0;
		
		for (int i=0; i<nodeChildren.getLength(); i++) {
		Composite c1 = new Composite(name1, price1, isbn1);
		myItems.add(c1);
		Leaf l1 = new Leaf(name1, price1, isbn1);
		myLeaves.add(l1);
		}
				
		for(int i=0;i<myItems.size();i++) {
			if(searchString.equals(myItems.get(i))) {
				System.out.println(myItems.get(i)); }
			}

		for(int i=0;i<myLeaves.size();i++) {
			if(searchString.equals(myLeaves.get(i))) {
				System.out.println(myLeaves.get(i));
				}
			}
		}
		catch (ParserConfigurationException e) { e.printStackTrace();  }
		catch (SAXException e) { e.printStackTrace(); }
		catch (IOException e) { e.printStackTrace(); }
	}
	
	public static void main(String[] args) {
		try {
		BufferedReader r1 = new BufferedReader (new InputStreamReader(System.in));
		System.out.println("Give the filename of an XML file to parse:");
		String input = r1.readLine();
   
		BufferedReader r2 = new BufferedReader (new InputStreamReader(System.in));
		System.out.println("Give the name of an item: ");
		String input2 = r2.readLine();

		CompositeDemo1 cd1 = new CompositeDemo1(input, input2);
		
		}
		catch (IOException e) { e.printStackTrace(); }
   }
}
```


----------



## devo22 (4. Dez 2011)

ich hoffe, ich führe hier keine Selbstgespräche  hab aber wieder weitergearbeitet. Stand jetzt ist so, dass ich das Beispiel eigentlich als fertig erachte, aber es wird nichts ausgegeben ... wo ist mein Fehler? 


```
import java.io.*;
import java.util.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

public class CompositeDemo1
{
	Document document;
	String XMLname;
	Element rootElement;
	
	public CompositeDemo1(String XMLname, String searchItem) {
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
		
		try {
		DocumentBuilder builder = builderFactory.newDocumentBuilder();
		document = builder.parse(new FileInputStream(XMLname));
		rootElement = document.getDocumentElement();
		
		NodeList list = rootElement.getChildNodes();
		
		for (int i = 0, size = list.getLength(); i < size; i++) {
			Node node = list.item(i);
			if (node instanceof Element) {
				Element subelement = (Element)node;
				if(searchItem.equals(subelement.getAttribute("name"))) {
					String priceValue = subelement.getAttribute("price");
					System.out.println(priceValue);
					}
			}
		}
		}
		
		catch (ParserConfigurationException e) { e.printStackTrace();  }
		catch (SAXException e) { e.printStackTrace(); }
		catch (IOException e) { e.printStackTrace(); }
		
	}
	
	public static void main(String[] args) {
		try {
		BufferedReader r1 = new BufferedReader (new InputStreamReader(System.in));
		System.out.println("Give the filename of an XML file to parse:");
		String input = r1.readLine();
   
		BufferedReader r2 = new BufferedReader (new InputStreamReader(System.in));
		System.out.println("Give the name of an item: ");
		String input2 = r2.readLine();

		CompositeDemo1 cd1 = new CompositeDemo1(input, input2);
				
		}
		catch (IOException e) { e.printStackTrace(); }
   }
}
```


```
import java.util.*;

public class Composite implements Component {
	ArrayList<Component> myItems;
	private String name;
	private int sum;
			
	public Composite(String name) {
		this.name = name;
		myItems = new ArrayList<>();
		
	}
	
	public String getName() {
		return name;
	}
	
	public int getPrice() {
		sum = 0;
		for (Component allItems : myItems) {
		sum = sum + allItems.getPrice();
		}
		return sum;
	}

    }
```

Die Component- und Leaf-Klasse analog wie oben.


----------



## eRaaaa (4. Dez 2011)

Ziemlich viel Code 
Aber, wenn du B1 eingibst, sollte dir eig. zumindest die 30 ausgegeben werden.
Alles andere wird nicht gefunden, da nur diese beiden Elemente
<book name="B1" price="30" isbn="123"/>
<list name="L1">
direkte Kindknoten vom Root sind, daher iterierst du gar nicht über alle anderen. Du müsstest jetzt eig. auch über die Kindknoten dieser Elemente iterieren.
Ich persönlich würde da wohl XPath einsetzen und einfach alle Bücher oder CDs selektieren!
Z.B.

```
XPath xpath = XPathFactory.newInstance().newXPath();
			XPathExpression expr = xpath.compile("//book | //cd");
			NodeList nodes = (NodeList) expr.evaluate(document, XPathConstants.NODESET);
			for (int i = 0; i < nodes.getLength(); i++) {
				if(nodes.item(i).getAttributes().getNamedItem("name").getNodeValue().equals(searchItem)){
					System.out.println("Price:" +nodes.item(i).getAttributes().getNamedItem("price").getNodeValue());
					//isbn etc...
				}
			}
```

Das wäre alles  (anstelle deiner Schleife Zeile 19 - ~32)


----------



## devo22 (4. Dez 2011)

Vielen Dank schon mal :toll:

Eines fehlt mir aber noch (hatte das oben vergessen, zu schreiben ): wenn ich L1 oder L2 eingebe, soll die Summe der Kindknoten ausgegeben werden. Kann ich dies auch beim XPath machen?


----------



## eRaaaa (4. Dez 2011)

Klar, das oben war nur ein Beispiel. Eig. hätte man nicht einmal iterieren müssen wenn man schon eine spezielle CD oder Buch haben möchte. Man könnte ja auch im XPath schreiben "//book[@name='"+searchItem+"']"
Dann würde die if-Abfrage etc. entfallen. 
Genauso würde z.B. auch "//list[@name='"+searchItem+"']" gehen, dann musst du eben wieder über die Kindelemente iterieren.
Wie du das jetzt genau auf deine Bedürfnisse umbaust musst du mal schauen. Auch was du unter Kindelementen verstehst. Vermutlich willst du auch die Liste2 erhalten wenn du nach Liste1 suchst, weil Liste1 die 2. beinhaltet oder? Eben das muss man sich dann eben zurecht stricken


----------



## devo22 (4. Dez 2011)

eRaaaa hat gesagt.:


> Auch was du unter Kindelementen verstehst. Vermutlich willst du auch die Liste2 erhalten wenn du nach Liste1 suchst, weil Liste1 die 2. beinhaltet oder?


gemeint ist das so, dass zB bei L2 dann "30" ausgegeben wird, weil in L2 die CDs C1 (15 Euro) und C2 (5 Euro) sowie das Buch B3 (10 Euro) liegen. Was zB bei L1 ist, die ja wieder Listen enthält, bin ich mir aus der Angabe heraus auch unsicher, das steht da nicht explizit.


----------



## eRaaaa (4. Dez 2011)

Tja, wenn du dir da schon unsicher bist 

```
XPath xpath = XPathFactory.newInstance().newXPath();
			XPathExpression expr = xpath.compile("//list[@name='" + searchItem + "']/*");
			NodeList nodes = (NodeList) expr.evaluate(document,XPathConstants.NODESET);
			int price = 0;
			for (int i = 0; i < nodes.getLength(); i++) {
				if (nodes.item(i).getAttributes().getNamedItem("price") != null) {
					price += Integer.parseInt(nodes.item(i).getAttributes().getNamedItem("price").getNodeValue());
				}
			}
			System.out.println(price);
```
L2 = 30, für L1 würde das dann 95 ergeben, (also L2 dann eben nicht mit eingerechnet) ....


----------



## devo22 (4. Dez 2011)

wow, vielen vielen Dank!!!!!!!!!!


----------



## devo22 (8. Jan 2012)

hi,

wir sollen das Beispiel nun durch das Visitor-Pattern erweitern. Aufgabenstellung ist es, mithilfe des Patterns Informationen (zB den Preis) aller Listenelemente aus der XML-Datei (die aus dem Ausgangspost) auszugeben.

Das ist mir in der Theorie klar, die Visitor-Klassen waren auch kein Ding ... nun habe ich aber as Problem, dass mir nicht alle Elemente ausgegeben werden, sondern nur das zweite und dritte, also B1 und L1  hier der Code meiner Visitor-Klasse und der Main-Methode:


```
public class DebugVisitor implements Visitor {

	public void visit(Book b) {
		System.out.println(b.name + "(" + b.getPrice() + ")");
	}
	
	public void visit(Cd c) {
		System.out.println(c.name + "(" + c.getPrice() + ")");
	}

	public void visit(Item i) {
		System.out.println(i.name + "(" + i.getPrice() + ")");
	}
}
```
In der Main-Methode werden die Daten aus der XML-Datei dann in einer Liste gespeichert. Nun dachte ich mir, mit der for each-Schleife alle Listenelemente zu besuchen. Leider klappt das aber nicht, es werden wie gesagt nur das zweite und dritte Element ausgegeben:


```
for (Item item : list) {
item.accept(new DebugVisitor());
}
```
Wäre für alle Vorschläge dankbar!!!


----------



## devo22 (8. Jan 2012)

hat sich erledigt, habs mit dem hierarchical visitor pattern hinbekommen.


----------

