Lecture Simple SnakeYAML

De EjnTricks

Cette page présente l'utilisation de SnakeYAML pour charger une ou plusieurs données depuis un flux d'entrées.

A noter que les exemples suivants seront basés sur des fichiers texte. Le principal problème de ces fichiers concernent l'encoding. Or le framework SnakeYAML permet de s'afranchir de celui-ci, en détectant automatiquement l'encoding lors du chargement. Ainsi, les développeurs n'ont plus besoin de vérifier l'encoding, facilitant l'intégration des données.


Hand-icon.png Votre avis

Nobody voted on this yet

 You need to enable JavaScript to vote


Study icon.png Déclarations

Pour rappel, le code commun est utilisé dans le cadre de ces exemples. Chacun des services est implémenté et démontré à l'aide de tests unitaires.

Le code est disponible dans le module snakeyaml du projet d'étude.


Icon-database-search.png Chargement

Dans les deux cas de chargement, unique ou multiple, cela s'effectue à l'aide d'une instance de org.yaml.snakeyaml.Yaml appelée avec la classe utilisée pour mapper les données. Le chargement d'une données unique s'effectue par la fonction loadAs.

  /**
   * {@inheritDoc}
   */
  @Override
  public <T> T loadProperty(InputStream resource, Class<T> objectClass) {
    Yaml yaml = new Yaml(new Constructor(objectClass));

    return yaml.loadAs(resource, objectClass);
  }

L'argument objectClass permet de spécifier au framework quelle implémentation est utilisée pour charger les données.

Le chargement multiple s'effectue de la même façon à l'aide de la fonction loadAll.

  /**
   * {@inheritDoc}
   */
  @SuppressWarnings("unchecked")
  @Override
  public <T> List<T> loadProperties(InputStream resource, Class<T> objectClass) {
    Yaml yaml = new Yaml(new Constructor(objectClass));

    return IterableUtils.toList((Iterable<T>) yaml.loadAll(resource));
  }

Attention, la fonction loadAll retourne une instance de Iterable. Afin de la convertir en une instance de List, la classe Iterableutils, de Apache Commons Collections est utilisée.


Run-icon.png Exécution

L'utilisation de ces implémentations est démontrée à l'aide de tests unitaires, écrits dans la classe fr.ejn.tutorial.snakeyaml.impl.service.PropertiesServiceImplTest au format JUnit, avec validation avec le framework AssertJ.

La fonction loadProperty est testée à travers testLoadProperty.

package fr.ejn.tutorial.snakeyaml.impl.service;

import static org.assertj.core.api.Assertions.assertThat;

import fr.ejn.tutorial.yaml.data.Address;
import fr.ejn.tutorial.yaml.data.Person;
import fr.ejn.tutorial.yaml.service.PropertiesService;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

public class PropertiesServiceImplTest {

  @BeforeClass
  public static void setUpBeforeClass() throws Exception {
  }

  @AfterClass
  public static void tearDownAfterClass() throws Exception {
  }

  private PropertiesService instance;

  private Address createAddress(String city, String country, String street) {
    Address address = new Address();

    address.setCity(city);
    address.setCountry(country);
    address.setStreet(street);

    return address;
  }

  private Person createPerson(int age, String name, double size, String surname, boolean vip) {
    Person person = new Person();

    person.setAge(age);
    person.setName(name);
    person.setSize(size);
    person.setSurname(surname);
    person.setVip(vip);

    return person;
  }

  @Before
  public void setUp() throws Exception {
    instance = new PropertiesServiceImpl();
  }

  @After
  public void tearDown() throws Exception {
  }

  @Test
  public void testLoadProperty() throws Exception {
    Person expected = createPerson(18, "Name 1", 170.5, "Surname 1", true);
    List<Address> addresses = new ArrayList<Address>();
    addresses.add(createAddress("City 1.1", "Country 1.1", "Street 1.1"));
    addresses.add(createAddress("City 1.2", "Country 1.2", "Street 1.2"));
    expected.setAddresses(addresses);

    Person actual = instance.loadProperty(PropertiesServiceImplTest.class
        .getResourceAsStream("person.yaml"), Person.class);

    assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
  }

}

Les premières instructions permettent juste de construire les données attendues. Puis le fichier person.yaml est chargé avec le contenu suivant.

name: Name 1
surname: Surname 1
size: 170.5
age: 18
vip: true
addresses :
  -
    city: City 1.1
    country: Country 1.1
    street: Street 1.1
  -
    city: City 1.2
    country: Country 1.2
    street: Street 1.2

De la même façon, la fonction loadProperties est testée à travers testLoadProperties.

package fr.ejn.tutorial.snakeyaml.impl.service;

import static org.assertj.core.api.Assertions.assertThat;

import fr.ejn.tutorial.yaml.data.Address;
import fr.ejn.tutorial.yaml.data.Person;
import fr.ejn.tutorial.yaml.service.PropertiesService;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

public class PropertiesServiceImplTest {

  @BeforeClass
  public static void setUpBeforeClass() throws Exception {
  }

  @AfterClass
  public static void tearDownAfterClass() throws Exception {
  }

  private PropertiesService instance;

  private Address createAddress(String city, String country, String street) {
    Address address = new Address();

    address.setCity(city);
    address.setCountry(country);
    address.setStreet(street);

    return address;
  }

  private Person createPerson(int age, String name, double size, String surname, boolean vip) {
    Person person = new Person();

    person.setAge(age);
    person.setName(name);
    person.setSize(size);
    person.setSurname(surname);
    person.setVip(vip);

    return person;
  }

  @Before
  public void setUp() throws Exception {
    instance = new PropertiesServiceImpl();
  }

  @After
  public void tearDown() throws Exception {
  }

  @Test
  public void testLoadProperties() throws Exception {
    // Build expected datas.
    List<Person> expected = new ArrayList<Person>();

    Person person = createPerson(18, "Name 1", 170.5, "Surname 1", true);
    List<Address> addresses = new ArrayList<Address>();
    addresses.add(createAddress("City 1.1", "Country 1.1", "Street 1.1"));
    addresses.add(createAddress("City 1.2", "Country 1.2", "Street 1.2"));
    person.setAddresses(addresses);
    expected.add(person);

    person = createPerson(20, "Name 2", 180.5, "Surname 2", false);
    addresses = new ArrayList<Address>();
    addresses.add(createAddress("City 2.1", "Country 2.1", "Street 2.1"));
    addresses.add(createAddress("City 2.2", "Country 2.2", "Street 2.2"));
    person.setAddresses(addresses);
    expected.add(person);

    List<Person> actual = instance.loadProperties(PropertiesServiceImplTest.class
        .getResourceAsStream("persons.yaml"), Person.class);

    assertThat(actual).usingRecursiveFieldByFieldElementComparator()
        .containsExactlyElementsOf(expected);
  }

}

Dans celui-ci, le fichier persons.yaml est chargé avec le contenu est le suivant.

---
name: Name 1
surname: Surname 1
size: 170.5
age: 18
vip: true
addresses :
  -
    city: City 1.1
    country: Country 1.1
    street: Street 1.1
  -
    city: City 1.2
    country: Country 1.2
    street: Street 1.2
---
name: Name 2
surname: Surname 2
size: 180.5
age: 20
vip: false
addresses :
  -
    city: City 2.1
    country: Country 2.1
    street: Street 2.1
  -
    city: City 2.2
    country: Country 2.2
    street: Street 2.2


Examples-icon.png Définition POJO

Le chargement d'appuye sur de simples POJO où sont définis les fonctions get et set. Même si cela n'est pas forcément une bonne pratique, les fonctions get ne sont pas nécessaires. Un exemple est mis à disposition avec le POJO suivant.

package fr.ejn.tutorial.snakeyaml.data;

import fr.ejn.tutorial.yaml.data.Address;

import java.util.ArrayList;
import java.util.List;

/**
 * POJO for Person model, with just setters.
 *
 * @author Etienne Jouvin
 *
 */
public class Person {

  private List<Address> addresses;
  private int age;
  private String name;
  private double size;
  private String surname;
  private boolean vip;

  /**
   * Set the addresses.
   *
   * @param addresses the addresses to set.
   */
  public void setAddresses(List<Address> addresses) {
    this.addresses = new ArrayList<Address>(addresses);
  }

  /**
   * Set the age.
   *
   * @param age the age to set.
   */
  public void setAge(int age) {
    this.age = age;
  }

  /**
   * Set the name.
   *
   * @param name the name to set.
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * Set the size.
   *
   * @param size the size to set.
   */
  public void setSize(double size) {
    this.size = size;
  }

  /**
   * Set the surname.
   *
   * @param surname the surname to set.
   */
  public void setSurname(String surname) {
    this.surname = surname;
  }

  /**
   * Set the VIP flag value.
   *
   * @param vip the vip to set.
   */
  public void setVip(boolean vip) {
    this.vip = vip;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String toString() {
    return "Person [addresses=" + addresses + ", age=" + age + ", name=" + name + ", size=" + size
        + ", surname=" + surname + ", vip=" + vip + "]";
  }

}

A noter pour les besoins des tests, la fonction toString() a été générée afin de présenter les valeurs lues.

Un test unitaire est mis en place pour la lecture du contenu YAML suivant.

name: Name 1
surname: Surname 1
size: 170.5
age: 18
vip: true
addresses :
  -
    city: City 1.1
    country: Country 1.1
    street: Street 1.1
  -
    city: City 1.2
    country: Country 1.2
    street: Street 1.2

Le test est réalisé en comparant les résultats de la fonction toString.

  @Test
  public void testWithoutGetter() throws Exception {
    String expected = "Person ["
        + "addresses=[{city=City 1.1, country=Country 1.1, street=Street 1.1}, {city=City 1.2, country=Country 1.2, street=Street 1.2}], "
        + "age=18, name=Name 1, size=170.5, surname=Surname 1, vip=true]";

    fr.ejn.tutorial.snakeyaml.data.Person actual = instance
        .loadProperty(PropertiesServiceImplTest.class
            .getResourceAsStream("person.yaml"), fr.ejn.tutorial.snakeyaml.data.Person.class);

    assertThat(actual.toString()).isEqualTo(expected);
  }


Warning-icon.png Conclusion

Ces exemples ont permit de démontrer la simplicié de chargement des données dans des classes Java.

Le point important concerne le chargement des données multiples, dont les définitions sont séparées par le marqueur ---. Ceci présente une distinction importante par rapport à YAMLBeans, qui met en évidence un adhérence entre le format du fichier et le framework utilisé.


Viewer icon.png Voir Aussi

Code source : http://www.dev.jouvinio.net/svn/study/trunk/yaml/snakeyaml