Groepen‎ > ‎Java‎ > ‎Oefeningen - Pure Java‎ > ‎

Naar de bib

Doel

Leer hoe je gebruik kan maken van krachtige bibliotheken om je het leven als programmeur gemakkelijker te maken. Wil je een website maken, een database aanspreken of afbeeldingen bewerken, voor elk probleem bestaat er wel een bibliotheek die het lastigste werk voor zijn rekening neemt. In deze opdracht gaan we gegevens naar een bestand wegschrijven als JSON en weer ophalen wanneer we het programma opnieuw opstarten. Op deze manier kunnen we gegevens onthouden ook al wordt het programma gestopt of de computer afgesloten.


Dit programma zal een lijstje van taken bewaren en opnieuw inladen als het programma opnieuw opgestart wordt. Je kunt taken toevoegen en verwijderen.

Aan de slag

1. Een bibliotheek toevoegen

Eerst heb je Jackson nodig. Jackson is een bibliotheek die objecten kan omzetten in JSON, een compact bestandsformaat en weer omzetten vanuit JSON naar een object.

Download de 3 Jackson jars:
Voeg dan de jars toe als bibliotheek. Dit doe je via het menu Tools => Preferences op Windows of BlueJ => Preferences op Mac OS X. Klik dan op het tabblad Libraries en voeg de 3 jars toe met de knop Add.
Herstart nu de Java Virtual Machine via Tools => Reset Java Virtual Machine. Nu is Jackson ingeladen en kun je de bibliotheek gebruiken.

2. Een object opslaan als JSON

Maak een nieuwe klasse Tasks en voeg een methode start() toe. We beginnen met een lijstje van taken te maken en dat op te slaan als een bestand. Voor de lijst hebben we de klassen java.util.List en java.util.Arrays nodig. Jackson, die het lijstje zal omzetten naar JSON, spreken we aan via com.fasterxml.jackson.databind.ObjectMapper, tenslotte hebben we nog java.io.File nodig om de JSON op te slaan als een bestand. Voeg import statements toe bovenaan je klasse voor alle 4 deze klassen. Vervolgens kun je met deze code de lijst maken van taken en opslaan als JSON.

Object opslaan als JSON

public void start() throws Exception {
    List<String> tasks = Arrays.asList("De vaat doen", "Stofzuigen", "Opruimen");
    ObjectMapper mapper = new ObjectMapper();
    File file = new File("tasks.json");
    mapper.writeValue(file, tasks);
    System.out.println("Taken opgeslagen in " + file.getAbsolutePath());
}

In deze code maken we eerst een lijst van taken aan. Dan de ObjectMapper, die we zullen gebruiken om de taken op te slaan, en een verwijzing naar een bestand waarin we de taken opslaan. Daarna schrijven we de lijst naar het bestand weg en tenslotte tonen we waar het bestand is opgeslagen, zo vind je het gemakkelijk terug.

Let op: new File("...") oproepen maakt nog geen nieuw bestand aan, in tegenstelling wat je zou denken. Het is enkel een verwijzing naar een bestand. Ook voor een bestaand bestand gebruik je new File("..."). Let ook op de throws Exception na de haakjes van de methode. Daarmee geven we aan dat deze method kan fout lopen. We moeten dit schrijven om de compiler tevreden te stellen omdat ook ObjectMapper.writeValue() kan fout lopen. In de opdracht over Exceptions komen we daar uitgebreid op terug.

Compileer de code, maak een nieuw Tasks object aan en voer de methode start() uit. Laten we eens kijken naar het bestand tasks.json:

tasks.json

["De vaat doen", "Stofzuigen", "Opruimen"]

3. JSON opnieuw inlezen

JSON opnieuw inlezen doen we met behulp van volgende code:

JSON inlezen

private ObjectMapper mapper = new ObjectMapper();
private File file = new File("tasks.json");
@SuppressWarnings("unchecked")
public List<String> loadTasks() throws Exception {
    if(file.exists()) {
        System.out.println("Taken inlezen uit " + file.getAbsolutePath() + " ...");
        return mapper.readValue(file, List.class);
    } else {
        System.out.println("Bestand niet gevonden (" + file.getAbsolutePath() + "), we maken een nieuwe lijst");
        return Arrays.asList("Nieuwe taak");
    }
}

Eerst controleren we of het bestand wel bestaat, zo ja, lezen we het in via mapper.readValue(). Als het bestand niet bestaat, maken we een nieuwe lijst aan met een voorbeeldtaak.

Vervolgens gaan we de taken die we ingelezen hebben op het scherm tonen via een eenvoudige for-lus en een paar System.out.println() statements: 

Taken tonen

private void printTasks(List<String> tasks) {
    System.out.println();
    System.out.println("Taken:");
    for(String task : tasks) {
        System.out.println((tasks.indexOf(task) + 1) + ". " + task);
    }
    System.out.println();
    System.out.println("Voer het nummer van een taak gevolgd door ENTER in om de taak af te werken.");
    System.out.println("Typ een taak gevolgd door ENTER om een taak toe te voegen.");
    System.out.println("Of druk gewoon op ENTER om te stoppen.");
}

Dan gaan we de invoer van de gebruiker inlezen:

Invoer van de gebruiker

private Scanner scanner = new Scanner(System.in);
private String readInput(List<String> tasks) {
    System.out.print("> ");
    String input = scanner.nextLine();
    
    if(!input.isEmpty()) {
        try {
            int index = Integer.parseInt(input);
            if(index <= tasks.size() && index > 0) {
                String task = tasks.remove(index - 1);
                System.out.println("Taak afgewerkt: " + task);
            }
        } catch(NumberFormatException e) {
            tasks.add(input);
            System.out.println("Taak toegevoegd: " + input);
        }
    }
    return input;
}

Hierover een beetje meer uitleg: Eerst lezen we een lijn in met een Scanner (vergeet ook geen import toe te voegen voor java.util.Scanner). Als de input niet leeg is kijken we of het een getal is of niet. Integer.parseInt() gaat een String proberen om te zetten naar een int. Lukt dit niet, dan wordt er een NumberFormatException gegooid. Zonder de try/catch constructie zou je programma daarmee afgelopen zijn. Maar wij vangen de NumberFormatException op. Als het geen getal is, gaan we de invoer toevoegen aan de taken. Als het wel een getal is, verwijderen we de taak met dat nummer. Let op de -1 omdat de index van een List bij 0 in plaats van 1 begint.

De code om taken op te slaan steken we ook in een nieuwe methode:

Taken opslaan

private void saveTasks(List<String> tasks) throws Exception {
    mapper.writeValue(file, tasks);
    System.out.println("Taken opgeslagen in " + file.getAbsolutePath());
}

Tenslotte sturen we alles aan vanuit de start() methode:

De nieuwe start() methode

public void start() throws Exception {
    System.out.println("==== START ====");
    List<String> tasks = loadTasks();
    
    String input;
    do {
        printTasks(tasks);
        input = readInput(tasks);
    } while(!input.isEmpty());

    saveTasks(tasks);
    System.out.println("==== EINDE ====");
}

Eerst laden we de taken in, vervolgens tonen we de taken en lezen vervolgens de invoer van de gebruiker in, zolang de invoer niet leeg is. Tenslotte, als de gebruiker gekozen heeft om te stoppen, slaan we de nieuwe lijst met taken op voor het programma stopt.

Compileer je code, maak een nieuw Tasks object en voer de start() method uit. Na gewoon op ENTER gedrukt te hebben om te stoppen, voer de start() methode opnieuw uit.
Proficiat! Je hebt Jackson toegevoegd als bibliotheek en ze gebruikt om een object om te zetten naar een JSON bestand en weer naar een object.
Comments