Générer un fichier de données est à priori une tâche aisée avec Mulesoft. Sur fichier et par SFTP, divers composants permettent d'écrire (Write - mode OVERWRITE) ou de compléter (Write - mode APPEND) le contenu d'un fichier. Un besoin récurrent est de créer un fichier au format JSON car l'usage de CSV - plus simple - est parfois insuffisant car très fragile:
- Le choix d'un séparateur est parfois un dilemme cornélien (quel est le caractère imprimable dont on n'est sûr qu'il ne sera JAMAIS utilisé, dans un environnement de plus en plus international)
- Comment intégrer dans un fichier des données de type texte (contenant des retours à la ligne) que CSV interprète faussement comme étant plusieurs lignes de données distinctes
- CSV est mal adapté pour contenir des données structurées (des objets contenant une ou plusieurs sous listes).
Le problème est que s'il est très facile de créer un fichier contenant une liste au format JSON en une seule opération, le faire en plusieurs appels n'est pas évident. En effet, écrire plusieurs sous listes au format JSON ne constitue pas un document bien formé car JSON impose qu'un document ne contienne qu'un seul objet racine (objet ou tableau). Or constituer une unique liste a écrire d'un seul coup n'est pas toujours possible:
- soit parce que la liste est trop longue (des centaines de milliers ou des millions de lignes)
- soit parce que les données à collecter le sont dans des processus successifs et non dans un unique processus (par lectures successives d'une source de données par exemple)
Construire une liste JSON par enrichissements successifs est en fait assez simple à réaliser. Comme bien des développeurs se sentent démunis face à ce problème, je fournis ici la manière de faire. Nous allons construire trois sous-flots:
- un flot d'ouverture qui entame la liste. Ce flot doit être appelé en premier lieu, une unique fois (obligatoire),
- un flot d'enrichissement qui peut être appelé autant de fois que nécessaire (éventuellement pas du tout),
- un flot de clôture qui ferme la liste. Ce flot doit être appelé en dernier lieu, une unique fois (obligatoire).
Le flot d'ouverture doit ressembler à celui-ci:
Bien que ne tenant que sur quatre lignes, cette snippet mérite quelques explications:
- Le format de sortie est "text/plain" afin que Mulesoft ne nous entrave pas pour des questions de format. En effet, l'objet que nous allons écrire, sera incomplet et donc rejeté par JSON.
- La structure du tableau (c'est à dire le "[" ouvrant et le "," qui sépare les objets de la liste sont ajoutés par la snippet elle-même. La description des objets est elle déléguée à la fonction DataWeave "Write".
- L'écriture se fait en mode OVERWRITE pour s'assurer que le contenu du fichier est correctement réinitialisé.
- Noter l'absence du "]" fermant, permettant de venir enrichir le document après ce premier appel. Noter aussi que pour le document doit bien formé, il ne manque que ce "]".
Le flot d'enrichissement doit ressembler à celui-ci:
Le code Dataweave qui assure le formatage de l'enrichissement est le suivant:
Cette snippet ressemble beaucoup à la précédente. Pointons les quelques différences:
- La continuité de la structure du tableau, c'est à dire le "," qui permet de rajouter des produits à la liste déjà présente et le "," qui sépare les objets de la suite de la liste sont ajoutés par la snippet elle-même.
- L'écriture se fait en mode APPEND pour que le contenu déjà présent ne soit pas écrasé.
- Noter l'absence des "[" ouvrant et "]" fermant, permettant de s'inscrire dans l'enrichissement du document.
Le flot de clôture est beaucoup plus simple. On se contente, en me APPEND, d'écrire le "]" fermant.
Ce système fonctionne quelque soit la taille de la liste (y comprit une liste vide) à deux conditions:
- qu'un segment de liste ne soit pas trop grand (et "tienne" dans une chaine de caractères DataWeave),
- que le flot d'ouverture ne présente une liste vide QUE si la liste globale est réellement vide, sinon, elle doit contenir au moins un objet.
- que le flot d'enrichissement ne présente JAMAIS une liste vide (elle doit contenir au moins un objet).
________________________________________________________________________
Generating a data file is, on the face of it, an easy task with Mulesoft. On file and via SFTP, various components enable you to write (Write - OVERWRITE mode) or complete (Write - APPEND mode) the contents of a file. A recurring need is to create a file in JSON format, as the simpler CSV format is sometimes insufficient due to its fragility:
- Choosing a separator is sometimes a Cornelian dilemma (which printable character can you be sure will NEVER be used, in an increasingly international environment).
- How to integrate text data (containing line breaks) into a file, which CSV misinterprets as several separate lines of data?
- CSV is ill-suited to holding structured data (objects containing one or more sub-lists).
The problem is that while it's very easy to create a file containing a list in JSON format in a single operation, doing so in several calls is not straightforward. Indeed, writing several sub-lists in JSON format does not constitute a well-formed document, as JSON requires that a document contain only one root object (object or array). However, it's not always possible to write a single list all at once:
- either because the list is too long (hundreds of thousands or millions of lines)
- or because the data to be collected are collected in successive processes rather than in a single process (by successive readings of a data source, for example).
Building a JSON list by successive enrichments is actually quite simple to achieve. As many developers find themselves at a loss when confronted with this problem, I've provided a step-by-step guide here. We're going to build three sub-flows:
- an opening stream that starts the list. This stream must be called first, once only (mandatory),
- an enrichment stream, which can be called as many times as necessary (or not at all),
- a closing flow, which closes the list. This stream must be called last, once only (mandatory).
The opening flow should look like this:
Although only four lines long, this snippet deserves some explanation:
- The output format is "text/plain", so that Mulesoft doesn't hinder us with format questions. Indeed, the object we're about to write will be incomplete and therefore rejected by JSON.
- The structure of the array (i.e. the opening "[" and the "," separating the objects in the list) is added by the snippet itself. Object description is delegated to the DataWeave "Write" function.
- Writing is done in OVERWRITE mode to ensure that file contents are correctly reset.
- Note the absence of the closing "]", allowing the document to be enriched after this first call. Note also that, for the well-formed document, only the "]" is missing.
The enrichment flow should look like this:
The Dataweave code used for enrichment formatting is as follows:
This snippet is very similar to the previous one. Let's point out the few differences:
- The continuity of the table structure, i.e. the "," that allows products to be added to the list already present and the "," that separates objects from the rest of the list are added by the snippet itself.
- Writing is done in APPEND mode, so that existing content is not overwritten.
- Note the absence of opening and closing "[" and "]", allowing you to take part in the enrichment of the document.
The closing flow is much simpler. In APPEND mode, you simply write the closing "]".
This system works regardless of the size of the list (including an empty list) on two conditions:
- a list segment is not too large (and "fits" into a DataWeave string),
- the opening stream presents an empty list ONLY if the global list is truly empty, otherwise it must contain at least one object.
- the enrichment flow NEVER presents an empty list (it must contain at least one object).