# Nodes in Document kopieren, nur die Childs, die noch nicht existieren



## guni (12. Nov 2009)

Hallo,

ich habe ein root-Element aus einem Document.
dann habe ich eine Schleife, die wie folgt aussieht

```
while bedingung {
   Element e = generateElement();
   root.appendChild(e);
}
```
generateElement erzeugt ein XML das die folgende Struktur hat:
[XML]<one value="a">
   <two value="d">
      <three value="g">
        <four value="j">
        <five value="k">
        <six value="l">
      </three>
   </two>
</one>[/XML]
mit root.appendChild hab ich das Problem, dass jedes dieser Elemente tatsächlich ins doc eingefügt wird; in Wirklichkeit will ich aber nur jene Elemente einfügen, die tatsächlich auch neu sind; d.h das Ganze soll so aussehen:
[XML]<root>
   <one value="a">
      <two value="d">
         <three value="g">
           <four value="j">
           <five value="k">
           <six value="l">
         </three>
      </two>
      <two value="e">
         <three value="g">
           <four value="j">
           <five value="k">
           <six value="l">
         </three>
         <three value="h">
           <four value="j">
           <five value="k">
           <six value="l">
         </three>
      </two>
   </one>
   <one value="b">
      <two value="d">
         <three value="g">
           <four value="j">
           <five value="k">
           <six value="l">
         </three>
      </two>
   </one>
</root>[/XML]
seht ihr worum es geht?
aufgrund des Value-Attributes einer Node soll die appendChild-Methode entscheiden, ob eine node bereits vorhanden ist, oder nicht.
wenn sie schon vorhanden ist, sollen die children erst ab da eingefügt werden, wo sie neu sind.

gibt es dafür schon irgendeine Funktion die das kann?
(document.adoptChild / document.importChild scheinen das nicht zu bewerkstelligen).
wenn es noch keine Funktion gibt: wie kann ich sowas sinnvoll schreiben?

danke für eure tipps!

mfg, guni


----------



## guni (12. Nov 2009)

hilfe! kennt sich da irgendwer aus?
ich probier schon ewig herum, aber ich komm einfach keinen schritt weiter!


----------



## guni (13. Nov 2009)

hallo Leute,

bin gestern noch eine Weile gesessen und hab das hier auf die Reihe gekriegt:


```
private void addElement(Element parent, Element child) throws Exception 
	{
		if (child == null) return;
		
		Element e = findNode(parent, child);
		
		boolean nodeAlreadyExists = (child.getTagName().equals(e.getTagName()) && 
				child.getAttribute("value").equals(e.getAttribute("value")));
		
		if (nodeAlreadyExists) {
			addElement(child, (Element)child.getFirstChild());
		} else {
			e.appendChild(child);
		}
	}
	
	private Element findNode(Element parent, Element child) throws Exception
	{
		// wenn child unter parent existiert, 
		// dann gib child zurück, 
		// sonst gib parent zurück
		String childTagName = child.getTagName();
		String childValueAttribute = child.getAttribute("value");
		
		// wenn wir nichts anderes mehr finden, dann geben wir 
		// parent zurück
		Element found = parent;
		
		// finde alle Elemente im Dokument, die dem TagNamen des angegebenen
		// childs entrsprechen
		NodeList foundElements = parent.getElementsByTagName(childTagName);
		
		// wenn Elemente gefunden wurden, nehmen wir das, zu dem der value passt!
		if (foundElements.getLength() > 0) {
			for (int i=0; i<foundElements.getLength(); i++) { 
				Element x = (Element)foundElements.item(i);
				String _tagname = x.getTagName();
				// beim Value ist noch irgendein Fehler!
				String _value = x.getAttribute("value"); 
				if (_tagname.equals(childTagName) && _value.equals(childValueAttribute)) {
					found = x;
				}
			}
		}
		return found;
	}
```

mein Problem:
es funktioniert nicht! Ausserdem denk ich mir, es muss dort IRGENDEINE Methode geben, die genau dieses Problem löst! Es kann ja nicht sein, dass ich der erste bin, der sich mit Gruppierungen in XML herumärgert ...

mfg, simon


----------



## guni (16. Nov 2009)

kann mir da echt keiner weiterhelfen?
the_29 hat mir einen ganz guten Tipp gegeben; vielleicht werd ich den mal probieren ...


----------



## guni (16. Nov 2009)

... nur einiges an Code für ein Problem von dem ich dachte, dass es eigentlich trivial ist ...


----------



## Beni (16. Nov 2009)

Ich hätte die Methode "findNode" etwas anders geschrieben, so dass sie null zurückgibt wenn nichts gefunden wird. Ansonsten sehe ich gerade nicht was falsch läuft, kannst du vielleicht ein KSKB geben?

```
package tos_spielwiese;

import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;


public class Test {
	public static void main(String[] args) throws Exception {
		DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		Document doc = builder.newDocument();
		
		Element root = createElement( "root", "?", doc );
		Element a = createElement( "one", "a", doc );
		Element b = createElement( "two", "b", doc );
		Element c = createElement( "two", "c", doc );
		Element d = createElement( "one", "b", doc );
		Element e = createElement( "two", "b", doc );
		Element f = createElement( "two", "b", doc );
		Element g = createElement( "three", "b", doc );
		
		addElement( root, a );
		addElement( a, b );
		addElement( a, c );
		addElement( root, d );
		addElement( d, e );
		addElement( f, g );
		addElement( d, f );
		
		println( root );
	}
	
	private static void println( Element node ) throws Exception{
		TransformerFactory transFactory = TransformerFactory.newInstance();
		Transformer transformer = transFactory.newTransformer();
		StringWriter buffer = new StringWriter();
		transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
		transformer.setOutputProperty( OutputKeys.INDENT, "yes" );
		transformer.transform(new DOMSource(node),
		      new StreamResult(buffer));
		String str = buffer.toString();
		System.out.println( str );
	}
	
	private static Element createElement( String tag, String value, Document doc ){
		Element e = doc.createElement( tag );
		e.setAttribute( "value", value );
		return e;
	}
	
    private static void addElement(Element parent, Element child) throws Exception {
        if (child == null) 
        	return;
        
        Element next = findNode(parent, child);
        if( next == null ){
        	parent.appendChild( child );
        }
        else{
        	NodeList list = child.getChildNodes();
        	for( int i = 0, n = list.getLength(); i<n; i++ ){
        		addElement( next, (Element)list.item( i ) );
        	}
        }
    }
    
    private static Element findNode(Element parent, Element child) throws Exception
    {
        // wenn child unter parent existiert, 
        // dann gib child zurück, 
        // sonst gib parent zurück
        String childTagName = child.getTagName();
        String childValueAttribute = child.getAttribute("value");
        
        // finde alle Elemente im Dokument, die dem TagNamen des angegebenen
        // childs entrsprechen
        NodeList foundElements = parent.getElementsByTagName(childTagName);
        
        // wenn Elemente gefunden wurden, nehmen wir das, zu dem der value passt!
        if (foundElements.getLength() > 0) {
            for (int i=0; i<foundElements.getLength(); i++) { 
                Element x = (Element)foundElements.item(i);
                String _tagname = x.getTagName();
                // beim Value ist noch irgendein Fehler!
                String _value = x.getAttribute("value"); 
                if (_tagname.equals(childTagName) && _value.equals(childValueAttribute)) {
                    return x;
                }
            }
        }
        return null;
    }
}
```


----------



## guni (16. Nov 2009)

danke beni!

deine Methode funktioniert.
dummerweise bin ich gerade drauf gekommen, dass mir ein XML in der Form eigentlich nix bringt:

in der Praxis bekomme ich nämlich ein CSV mit Abwesenheiten von Mitarbeitern; vereinfacht gesagt sieht es so aus:

<DATUM>, <MITARBEITERNUMMER>, <ABWESENHEIT>:
0101, 1, KRANK
0201, 1, ANWESEND
0301, 1, ANWESEND
0401, 1, ANWESEND
0501, 1, ANWESEND
0601, 1, AUSSENDIENST
0701, 1, AUSSENDIENST
0801, 1, ANWESEND
0901, 1, ANWESEND
...
0101, 2, ANWESEND
0201, 2, ANWESEND
0301, 2, ANWESEND
0401, 2, ANWESEND
0501, 2, URLAUB
0601, 2, URLAUB
0701, 2, URLAUB
0801, 2, URLAUB
0901, 2, ANWESEND
...

meine Aufgabe ist es, eine CSV in folgender Form zu schreiben:
<MITARBEITER>, <VON>, <BIS>, <GRUND>:
1, 0101, 0101, KRANK
1, 0201, 0501, ANWESEND
1, 0601, 0701, AUSSENDIENST
1, 0801, 0901, ANWESEND
2, 0101, 0401, ANWESEND
2, 0501, 0801, URLAUB
2, 0901, 0901, ANWESEND

... natürlich ist das Problem in der Praxis noch etwas komplexer; die Kriterien, für die ein neuer Datumszeitraum definiert werden soll sind nicht nur 
* "andere Mitarbeiternummer als in der Vorzeile" und 
* "anderer Abwesenheitsgrund als in der Vorzeile"

also ... ich hab einen Ansatz, in dem ich meine CSV zeilenweise einlese, das result in einem gesplitteten String-Array speichere.
Dieses speichere ich wiederum in einer linked list. beginnt ein neuer Datumsblock (aufgrund verschiedener Kriterien, dann schreibe ich das resultat der linkedList mit VON=DATUM[LinkedList.getFirst] und BIS=DATUM[LinkedList.getLast]

... soweit klar, oder?

jetzt hab ich mir gedacht; das Ganze wäre doch viel schöner, wenn ich ein XML dazu bastle, dass ich dann einfach per XSL in eine CSV umwandle.

ich lese also die CSV ein und create mir für jeden String ein eigenes Element.
dann baue ich mir die Struktur so auf, wie sie mir gefällt:


```
grund.appendChild(vond);
penr.appendChild(grund);
finr.appendChild(penr);
```

damit erhalte ich folgende Xml-Struktur:
[XML]<FINR value="">
   <PENR value="">
      <VOND value=""/>
   </PENR>
</FINR>[/XML]

über den Merge, den mir Beni obenstehend ausgebessert hat, erhalte ich nun eine XML- in der alle Daten gruppiert sind.
das sollte dann nach obenstehendem Beispiel für Mitarbeiter 1 so aussehen:

[XML]<FINR value="1">
   <PENR value="1">
      <GRUND value="KRANK">
         <VOND value="0101"/>
      </GRUND>
      <GRUND value="ANWESEND">
         <VOND value="0201"/>
         <VOND value="0301"/>
         <VOND value="0401"/>
         <VOND value="0501"/>
         <VOND value="0801"/>
         <VOND value="0901"/>
      </GRUND>
      <GRUND value="AUSSENDIENST">
         <VOND value="0601"/>
         <VOND value="0701"/>
      </GRUND>
   </PENR>
</FINR>[/XML]

hey! seht ihr das Problem?!
ich kann unmöglich durch mein XML gehen und einfach sagen
VON = aktuellerGrund.getFirstChild();
BIS = aktuellerGrund.getLastChild();

das Problem ist, dass meine Sortierung verloren geht!
obenstehendes XML würde folgenden Output geben:

1, 0101, 0101, KRANK
1, 0201, 0901, ANWESEND
1, 0601, 0701, AUSSENDIENST

gewünscht ist aber natürlich folgender Output:

1, 0101, 0101, KRANK
*1, 0201, 0501, ANWESEND*
1, 0601, 0701, AUSSENDIENST
*1, 0801, 0901, ANWESEND*

mein XML müsste also eigentlich so aussehen:

[XML]<FINR value="1">
   <PENR value="1">
      <GRUND value="KRANK">
         <VOND value="0101"/>
      </GRUND>
      <GRUND value="ANWESEND">
         <VOND value="0201"/>
         <VOND value="0301"/>
         <VOND value="0401"/>
         <VOND value="0501"/>
      </GRUND>
      <GRUND value="AUSSENDIENST">
         <VOND value="0601"/>
         <VOND value="0701"/>
      </GRUND>
      <GRUND value="ANWESEND">
         <VOND value="0801"/>
         <VOND value="0901"/>
      </GRUND>
   </PENR>
</FINR>[/XML]

hat von euch wer eine Idee dazu?!
muss ich meinen Ansatz jetzt völlig verwerfen?
XML hat mir so viel schöner ausgesehen wie meine blöden LinkedLists für die Datumsblöcke!!

mfg, guni


----------



## guni (17. Nov 2009)

so - hab das thema gestern noch gelöst.
wer interesse daran hat: Skype ;-)


----------

