Lecture Simple SnakeYAML
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.
Sommaire
Votre avis
Nobody voted on this yet
|
|
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.
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.
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
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);
}
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é.
Voir Aussi
Code source : http://www.dev.jouvinio.net/svn/study/trunk/yaml/snakeyaml