Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 1 sur 1
  1. #1
    Expert Confirmé Sénior
    Avatar de sekaijin
    Homme Profil pro
    Urbaniste
    Inscrit en
    juillet 2004
    Messages
    3 183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Santé

    Informations forums :
    Inscription : juillet 2004
    Messages : 3 183
    Points : 6 878
    Points
    6 878

    Par défaut [camel] Convertir CSV->XML avec bindy jaxb

    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
    Code csv :
    1
    2
    4;12235;CHIR;5802;10000446;30;14078;SPE;1
    4;19999;CHIR;5888;10000446;30;14078;SPE;1
    En XML
    Code xml :
    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>
    Vous pouvez partir d'un Schéma XSD pour générer les classes avec Jaxb mais il faut les modifier.

    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é
    Code :
    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;
    Il en va de même pour ObjectFactory.java
    Code :
    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();
       }
    }
    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 :
    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);
       }
    }
    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
    ainsi chaque champ est annoté pour jaxb et pour Bindy
    Code :
    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
    }
    Transform.java cette classe sert à partir de la map de Poste à construire un objet Réservation
    Code :
    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 clair
    Code :
    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")
               ;
     
        }
    }
    et 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.
    Une petite classe de test pour vérifier le tout: RouteTest (ajouter camel-test au dépendances)
    Code :
    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();
       }
    }
    Et voilà l'affaire et dans le sac.
    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
    Fichiers attachés Fichiers attachés

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •