# CSV zu XML Konverter



## jimbo1000 (17. Jul 2020)

Hallo,

ich schreibe einen Converter komme aber an einer bestimmten Stelle nicht ganz weiter. Das CSV File das ich bearbeite, ist etwas sonderbar. Die Headerinformationen gibt es nicht, jedoch stehen am Anfang der Zeile Node informationen. Die nehme ich dann einfach als Headerersatz. Erstmal das Test Filevon allen Klassen die ich geschrieben habe.


```
Das;ist;ein;;Test Warum;800;diese;das;nich so funktioniert;1000;;;;
test1;test3;;test1;test1;test1;test1;test1;test1;;;;;test1;test1;test1;test1;
test14;test1300;
```

1.In dieser Klasse lese ich nur ein.


```
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class DateiScanner {
  
     public static void ladeDatei (String datName) {

            File file = new File(datName);

            if (!file.canRead() || !file.isFile())
                System.exit(0);

             
                Scanner scan = null;
                try {
                    scan = new Scanner(file);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                while (scan.hasNext()) {

                    Filter filter = new Filter(scan.nextLine());
                }//while
                scan.close();
              
        }// void ladeDatei
    }//class
```

2. Durch die Beschaffenheit der CSV, muss ich irgendwie eine Künstliche Struktur bauen damit ich weis wo ich später bin, wenn ich das File baue. Da ich einen Scanner benutze, wird mir immer Zeilenweise der Inhalt geliefert. Da ich wie anfänglich erwähnt, das erste Wort also das Node für jedes fällt übernehme, Nummeriere ich jedes Wort durch und Filtere Leere stellen heraus.


```
[B]import[/B] java.util.Set;





public class Filter {


  


    private String zeile;


    int zeilenZaehler = 0;


  


    public Filter(String zeile) {


      


        this.zeile = zeile;


        this.ZeilenFilter();


    }


    public String getZeile() {


        return zeile;


    }//getZeile()





    public void setZeile(String zeile) {


        this.zeile = zeile;


    }//setZeile(String zeile)





    public void ZeilenFilter() {


      


        String Wort ="";


      


        for(int i = 0; i< this.getZeile().length();i++) {


          


            if(this.getZeile().charAt(i) != ';' && this.getZeile().toString() != null) {


              


                char extra = this.getZeile().charAt(i);


              


                Wort += extra;


            }else {


              


                StructureBuilder2 structure = new StructureBuilder2(zeilenZaehler+Wort);


                Wort = "";


                zeilenZaehler++;


            }//else


        }//void ZeilenFilter


      


    }//ZeilenFilter()





}//class
```

3. Soweit gibt es keine Probleme und alles funktioniert so wie es soll. Jetzt möchte ich die Datei bauen und bekomme eine für mich nicht nachvollziehbare Ausgabe. Erstmal der Code:


```
package convert;





public class StructureBuilder2 {





    private String wort;


  


    public static String Speicher;


    public static String Speicher2;


  


    public String getWort() {


        return wort;


    }





    public void setWort(String wort) {


       this.wort = wort;


    }





    public StructureBuilder2(String word) {


        this.wort = word;


        this.input();


        System.out.println(Speicher);


        //this.input2();


        //System.out.println(Speicher2);


    }//StructureBuilder(String word)


  


    public void input() {


        //Speicher = "";


        StringBuilder Node = new StringBuilder(this.getWort().toString());


      


        if(Node.charAt(0) == '0' && Node.toString() != null) {


            this.Speicher = Node.deleteCharAt(0).toString();


            //this.BuildHeader(Speicher);


          


        }//if


      


    }


    public void input2() {


      


        StringBuilder Node2 = new StringBuilder(this.getWort());


      


        if (Node2.charAt(0) != '0')


          


            this.Speicher2 = Node2.deleteCharAt(0).toString();


    }


}//class
```

Das ist die Ausgabe:


```
null


ist


ein





Test Warum


800


diese


das


nich so funktioniert


1000


0


1


2


2


test3





test1


test1


test1


test1


test1


test1





0


1


2


3test1


4test1


5test1


6test1


6test1


test1300
```

Sowiet stimmt und passt das alles. ich schaffe es jedoch nicht den ersten Eintrag mit null weg zu bekommen. Durch diesen Eintrag, ist alles im 1 nach unten verrutscht in meiner Finalen Ausgabe, die eigentlich so aussehen soll:


```
<Das>
<Das>ist</Das>
<Das>ein</Das>
<Das>Test</Das
.
.
.
</Das>
<test1>
<test1>test3</test1>
<test1>test1</test1>
.
.
.

</test1>

<test14>
<test14>test1300</test14>
</test14>
```

Immer wenn ich Speicher 2 im Kontruktor aufrufe, nachdem ich ihn in der Klasse befüllt habe, habe ich diesen Fehler. Ich habe auch eine zusätzliche Methode geschrieben, der ich Speicher 2 direkt übergebe und dann eine Ausgabe mache, um zu sehen ob der null Eintrag aus der ersten Zeile dann weg ist. Nur wenn ich in der Methode (input2) ein System out mache, ist das null nicht da. Deshalb habe ich auch versucht die Klasse mit mit return Type String zu machen und dann aufzurufen. Jedoch auch ohne erfolg. Da ich nicht nachvollziehen kann, warum dieser Eintrag überhaupt erzeugt wird, wäre ich um jeden Tipp und Erklärung dankbar.


----------



## mihe7 (18. Jul 2020)

In Anlehnung an https://docs.oracle.com/javase/tutorial/jaxp/xslt/generatingXML.html:


Spoiler: Test.java





```
import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.transform.*;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;

public class Test implements XMLReader {
    private ContentHandler handler;
    private final String xmlNs = "";
    private Attributes attrs = new AttributesImpl();

    public void parse(InputSource is) throws SAXException {
        Reader sourceReader = is.getCharacterStream();
        BufferedReader reader = new BufferedReader(sourceReader);
        handler.startDocument();
        reader.lines()
            .map(line -> line.split(";"))
            .forEach(this::handle);
        handler.endDocument();
    }

    private void handle(String[] line) {
        try {
            String tagName = line[0];
            handler.startElement(xmlNs, tagName, tagName, attrs);
            for (int i = 1; i < line.length; i++) {
                String value = line[i];
                handler.startElement(xmlNs, tagName, tagName, attrs);
                handler.characters(value.toCharArray(), 0, value.length());
                handler.endElement(xmlNs, tagName, tagName);
            }
            handler.endElement(xmlNs, tagName, tagName);
        } catch (SAXException ex) {
            throw new RuntimeException(ex);
        }
    }


    @Override
    public void setContentHandler(ContentHandler handler) {
        this.handler = handler;
    }

    @Override
    public ContentHandler getContentHandler() { return handler; }

    @Override public void setErrorHandler(ErrorHandler handler) {}
    @Override public ErrorHandler getErrorHandler() {return null;}
    @Override public void parse(String systemId) throws IOException, SAXException {}
    @Override public DTDHandler getDTDHandler() {return null;}
    @Override public EntityResolver getEntityResolver() {return null;}
    @Override public void setEntityResolver(EntityResolver resolver) {}
    @Override public void setDTDHandler(DTDHandler handler) {}
    @Override public Object getProperty(String name) {return null;}
    @Override public void setProperty(String name, Object value) {}
    @Override public void setFeature(String name, boolean value) {}
    @Override public boolean getFeature(String name) {return false;}



    public static void main(String[] args) throws Exception {
        String input =
            "Das;ist;ein;;Test Warum;800;diese;das;nich so funktioniert;1000;;;;\n" +
            "test1;test3;;test1;test1;test1;test1;test1;test1;;;;;test1;test1;test1;test1;\n" +
            "test14;test1300;\n";

        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

        InputSource inputSource = new InputSource(new StringReader(input));
        Test reader = new Test();
        SAXSource source = new SAXSource(reader, inputSource);
        StreamResult result = new StreamResult(System.out);
        transformer.transform(source, result);
    }
}
```



liefert

```
<?xml version="1.0" encoding="UTF-8"?><Das>
  <Das>ist</Das>
  <Das>ein</Das>
  <Das/>
  <Das>Test Warum</Das>
  <Das>800</Das>
  <Das>diese</Das>
  <Das>das</Das>
  <Das>nich so funktioniert</Das>
  <Das>1000</Das>
</Das><test1>
  <test1>test3</test1>
  <test1/>
  <test1>test1</test1>
  <test1>test1</test1>
  <test1>test1</test1>
  <test1>test1</test1>
  <test1>test1</test1>
  <test1>test1</test1>
  <test1/>
  <test1/>
  <test1/>
  <test1/>
  <test1>test1</test1>
  <test1>test1</test1>
  <test1>test1</test1>
  <test1>test1</test1>
</test1><test14>
  <test14>test1300</test14>
</test14>
```


----------



## jimbo1000 (18. Jul 2020)

erstmal vielen Dank. Ich verstehe den Code nicht ganz, kannst du mi sagen was genau du da gemacht hast?


```
reader.lines().map(line -> line.split(";"))
```

ich verstehe natürlich dass du hier den Trenner definiert hast. Aber warum du das so gemacht hast und was -> bedeutet, verstehe ich nicht. Ich lese mich schon den halben Tag in Sax ein. Eigentlich war mein plan daraus ein Groovy script zu machen. Vielleicht kannst du mir noch sagen wieso ich in meinem Code ein null in der ersten Zeile habe.


----------



## mihe7 (19. Jul 2020)

jimbo1000 hat gesagt.:


> ich verstehe natürlich dass du hier den Trenner definiert hast. Aber warum du das so gemacht hast und was -> bedeutet, verstehe ich nicht.


Seit Java 8 gibt es Streams (damit sind nicht die I/O-Streams wie InputStream usw. gemeint). Ein Stream ist einfach eine Folge von Elementen, auf die sich bestimmte Operationen anwenden lassen.

read.lines() liefert also eine Folge von Textzeilen. Mit Hilfe von map() lassen sich nun die Elemente im Stream auf andere Elemente abbilden. Dazu wird eine Funktion (s. java.util.function.Function) verwendet. Die Notation mit dem Pfeil "->" ist ein sog. Lambda-Ausdruck, mit dem man eine anonyme Funktion angibt: `line -> line.split(";")` gibt also eine Funktion an, die einen String line als Parameter erhält und ein String-Array zurückgibt (dieses entsteht durch split).

Ausführlich und ohne Lambda-Ausdruck geschrieben:

```
reader.lines().map(new Function<String, String[]>() {
    @Override
    public String[] apply(String line) {
        return line.split(";");
    }
})
```
Was passiert nun durch das map(...)? Zunächst hast Du einen Stream, dessen Elemente String-Objekte (gerade die Textzeilen) sind. Das map(...) verwandelt diesen Stream in einen Stream, dessen Elemente String-Arrays sind. Die String-Arrays werden aus dem String gewonnen, indem die Zeile an den ";" aufgeteilt wird (die split-Methode der Klasse String liefert ein Array).

Das abschließende forEach sorgt dafür, dass für jedes Element des Streams (d. h. für jedes String-Array) etwas ausgeführt wird. In diesem Fall wird die referenzierte Methode augerufen (this::handle ist eine sog. Methodenreferenz).

Der Code

```
reader.lines()
            .map(line -> line.split(";"))
            .forEach(this::handle);
```
macht also nichts anderes als

```
String line;
while ((line = reader.readLine()) != null) {
    String[] parts = line.split(";");
    handle(parts);
}
```
Nur schöner. Wenn Du z. B. eine Kopfzeile hättest und nur 10 Zeilen einlesen wolltest, könntest Du z. B. einfach schreiben:

```
reader.lines()
            .skip(1)
            .limit(10)
            .map(line -> line.split(";"))
            .forEach(this::handle);
```


----------



## Thallius (19. Jul 2020)

mihe7 hat gesagt.:


> ```
> String line;
> while ((line = reader.readLine()) != null) {
> String[] parts = line.split(";");
> ...



Extremste ansichtssache finde ich....


----------



## mihe7 (19. Jul 2020)

Thallius hat gesagt.:


> Extremste ansichtssache finde ich....


Schönheit liegt nunmal im Auge des Betrachters  

Das "schöner" musst Du im Zusammenhang mit dem darauf folgenden Code sehen. Gefällt Dir da eine Schleife immer noch besser?


```
String line;
int ix = 0, skip = 1, limit = 10;
while ((line = reader.readLine()) != null) {
    if (ix >= skip && ix < skip+limit) {
        handle(line.split(";"));
    }
    ix++;
}
```


----------



## Thallius (19. Jul 2020)

mihe7 hat gesagt.:


> Schönheit liegt nunmal im Auge des Betrachters
> 
> Das "schöner" musst Du im Zusammenhang mit dem darauf folgenden Code sehen. Gefällt Dir da eine Schleife immer noch besser?



Man kann es sich natuerlich auch möglich komliziert machen  Ich würde es ohne streams dann doch eher so lösen:


```
String line = reader.readLine();
int index = 0;
while ((line = reader.readLine()) != null && index < 10)
{
    handle(line.split(";"));
    index++;
}
```


----------



## mihe7 (19. Jul 2020)

Thallius hat gesagt.:


> Man kann es sich natuerlich auch möglich komliziert machen


Naja, tausche mal skip oben durch 5.


----------



## jimbo1000 (21. Jul 2020)

@mihe7 vielen dank


----------

