Bonjour.
Une route camel pour convertir du CSV en XML avec Camel bindy et jaxb.
Créez un projet camel (j'utilise maven pour cela)
Ajoutez camel-bindy et camel-jaxb dans les dépendances (2.8.0 pour moi)
Le but est de transformer des données CSV comme
En XML
Code csv : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 4;12235;CHIR;5802;10000446;30;14078;SPE;1 4;19999;CHIR;5888;10000446;30;14078;SPE;1Vous pouvez partir d'un Schéma XSD pour générer les classes avec Jaxb mais il faut les modifier.
Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <reservation xmlns="urn:org.sekaijin.csv.to.xml.metier"> <poste> <ph1>4</ph1> <ph2>12235</ph2> <ph3>CHIR</ph3> <ph4>5802</ph4> <ph5>10000446</ph5> <ph6>30</ph6> <ph7>14078</ph7> <ph8>SPE</ph8> <ph9>1</ph9> </poste> <poste> <ph1>4</ph1> <ph2>19999</ph2> <ph3>CHIR</ph3> <ph4>5888</ph4> <ph5>10000446</ph5> <ph6>30</ph6> <ph7>14078</ph7> <ph8>SPE</ph8> <ph9>1</ph9> </poste> </reservation>
Je préfère donc partir directement du code Java
package-info.java: ce fichier sert à définir le namespace du schéma. sans changement s'il a été généréIl en va de même pour ObjectFactory.java
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 @XmlSchema( namespace = "urn:org.sekaijin.csv.to.xml.metier", elementFormDefault = XmlNsForm.QUALIFIED ) package org.sekaijin.cvs.to.xml; import javax.xml.bind.annotation.XmlNsForm; import javax.xml.bind.annotation.XmlSchema;Reservation.java contient la définition d'une réservation. Par facilité, j'ai ajouté une méthode setPoste qui ajoute un poste. La classe générée par Jaxb nécessite de setter tous les postes d'un coup.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.sekaijin.cvs.to.xml; import javax.xml.bind.annotation.XmlRegistry; @XmlRegistry public class ObjectFactory { public ObjectFactory() { } public Reservation createReservation() { return new Reservation(); } public Poste createPoste() { return new Poste(); } }Poste.js c’est ici que se passe l'opération la plus intéressante par rapport au code généré par Jaxb j'ai ajouté toutes les annotations nécessaires à Bindy pour lire le CSV
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package org.sekaijin.cvs.to.xml; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = {"postes"}) @XmlRootElement(name = "reservation") public class Reservation { @XmlElement(required = false, name = "poste") protected List<Poste> postes; public List<Poste> getPostes() { if (postes == null) { postes = new ArrayList<Poste>(); } return this.postes; } public Reservation(){ } public void setPoste(Poste poste){ this.getPostes().add(poste); } }
ainsi chaque champ est annoté pour jaxb et pour BindyTransform.java cette classe sert à partir de la map de Poste à construire un objet Réservation
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 package org.sekaijin.cvs.to.xml; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlType; import org.apache.camel.dataformat.bindy.annotation.CsvRecord; import org.apache.camel.dataformat.bindy.annotation.DataField; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "Poste", propOrder = { "ph1", "ph2", "ph3", "ph4", "ph5", "ph6", "ph7", "ph8", "ph9" }) @CsvRecord(separator = ";", crlf = "UNIX", skipFirstLine = false) public class Poste { @DataField(pos = 1) @XmlElement(required = true) protected String ph1; @DataField(pos = 2) @XmlElement(required = true) protected String ph2; @DataField(pos = 3) @XmlElement(required = true) protected String ph3; @DataField(pos = 4) @XmlElement(required = true) protected String ph4; @XmlElement(required = true) @DataField(pos = 5) protected String ph5; @DataField(pos = 6) @XmlElement(required = true) protected String ph6; @DataField(pos = 7) @XmlElement(required = true) protected String ph7; @DataField(pos = 8) @XmlElement(required = true) protected String ph8; @DataField(pos = 9) @XmlElement(required = true) protected String ph9; public String getPh1() { return ph1; } public void setPh1(String value) { this.ph1 = value; } // je vous laisse ajouter tous les autres getters et setters }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package org.sekaijin.cvs.to.xml; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.camel.Exchange; import org.apache.camel.Message; public class Transform { public void process(Exchange exchange) throws Exception { Message msg = exchange.getIn(); @SuppressWarnings("unchecked") List<Map<String, Poste>> orders = (List<Map<String, Poste>>) msg.getBody(); ObjectFactory objectFactory = new ObjectFactory(); Reservation resa = objectFactory.createReservation(); Iterator<Map<String, Poste>> orderIterator = orders.iterator(); while (orderIterator.hasNext()) { Map<String, Poste> n = orderIterator.next(); resa.setPoste(n.get(Poste.class.getName())); } exchange.getIn().setBody(resa); } }
Il reste à définir la route
from ce que vous voulez -> bindy qui donne une Map de "Poste" -> Transform qui créé un Réservation et y place la liste des Poste -> jaxb qui en fait un XML -> to ce que vous voulez
J'ai ajouté des log pour y voir clairet c'est tout j'ai utilisé direct et mock pour faire des tests, mais file http ou tout autre protocole fonctionne de la même façon.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.sekaijin.cvs.to.xml; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.converter.jaxb.JaxbDataFormat; import org.apache.camel.model.dataformat.BindyType; public class MyRouteBuilder extends RouteBuilder { public void configure() { JaxbDataFormat jaxbDataFormat = new JaxbDataFormat(Reservation.class.getPackage().getName()); jaxbDataFormat.setPrettyPrint(true); from("direct:start") .to("log: IN ==>") .unmarshal().bindy(BindyType.Csv, Poste.class.getPackage().getName()) .bean(Transform.class) .marshal(jaxbDataFormat) .to("log: OUT ==>") .to("mock:result") ; } }
Une petite classe de test pour vérifier le tout: RouteTest (ajouter camel-test au dépendances)Et voilà l'affaire et dans le sac.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 package org.sekaijin.cvs.to.xml; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.test.junit4.CamelTestSupport; import org.junit.Test; public class RouteTest extends CamelTestSupport { @Override public String isMockEndpoints() { // override this method and return the pattern for which endpoints to // mock. // use * to indicate all return "*"; } @Test public void testRoute() throws Exception { String CamelFileName = "sample.csv"; String inMsg = "4;12235;CHIR;5802;10000446;30;14078;SPE;1" + "\n" + "4;19999;CHIR;5888;10000446;30;14078;SPE;1"; String ouMsg = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" + "<reservation xmlns=\"urn:org.sekaijin.csv.to.xml.metier\">\n" + " <poste>\n" + " <ph1>4</ph1>\n" + " <ph2>12235</ph2>\n" + " <ph3>CHIR</ph3>\n" + " <ph4>5802</ph4>\n" + " <ph5>10000446</ph5>\n" + " <ph6>30</ph6>\n" + " <ph7>14078</ph7>\n" + " <ph8>SPE</ph8>\n" + " <ph9>1</ph9>\n" + " </poste>\n" + " <poste>\n" + " <ph1>4</ph1>\n" + " <ph2>19999</ph2>\n" + " <ph3>CHIR</ph3>\n" + " <ph4>5888</ph4>\n" + " <ph5>10000446</ph5>\n" + " <ph6>30</ph6>\n" + " <ph7>14078</ph7>\n" + " <ph8>SPE</ph8>\n" + " <ph9>1</ph9>\n" + " </poste>\n" + "</reservation>\n"; getMockEndpoint("mock:direct:start").expectedBodiesReceived(inMsg); getMockEndpoint("mock:result").expectedBodiesReceived(ouMsg); template.sendBodyAndHeader("direct:start", inMsg, "CamelFileName", CamelFileName); assertMockEndpointsSatisfied(); } @Override protected RouteBuilder createRouteBuilder() throws Exception { return new MyRouteBuilder(); } }
En partant de Jaxb et en ajoutant les annotations, le travail est plutôt facile. Mais il ne faut par régénérer les classes.
La "difficulté" vient du fait que Bindy fournit une liste d'objet alors que Jaxb attend un objet unique. ça se règle avec la classe Transform
"l'astuce" consiste à utiliser la même classe (Poste) comme modèle du CSV et XML(Poste)
A+JYT
Partager