Read a CSV File and Transform It to XML File¶
Overview¶
This is a low-code walkthrough that uses the Ballerina Integrator Data Mapper and file APIs to build an end-to-end pipeline — without writing code by hand. You will:
- Pick up files (CSV) from a folder (e.g.,
input/). - Map each CSV row to a
<Row>element inside<Orders>using the Data Mapper visual UI. - Add a row number as a child
<index>element. Use the mapper’s row position function and set it to 0-based. - Write the result to a new XML file in an
output/folder.
Why this use case¶
- Converts flat CSV order data into XML that downstream, XML-centric systems can validate and consume.
- Demonstrates record indexing as an element (
<index>) for traceability back to the original row. - Scales easily from a single file to batch folders or listener-based near-real-time ingestion.
Input CSV Example¶
order_id,sku,qty,price
S001,P-1001,2,149.99
S002,P-3001,3,39.99
S003,P-2003,1,89.50
Expected Output XML¶
<Orders>
<Row>
<index>0</index>
<order_id>S001</order_id>
<sku>P-1001</sku>
<qty>2</qty>
<price>149.99</price>
</Row>
<Row>
<index>1</index>
<order_id>S002</order_id>
<sku>P-3001</sku>
<qty>3</qty>
<price>39.99</price>
</Row>
<Row>
<index>2</index>
<order_id>S003</order_id>
<sku>P-2003</sku>
<qty>1</qty>
<price>89.50</price>
</Row>
</Orders>
Prerequisites¶
Before you begin, make sure you have the following:
- Visual Studio Code: Install Visual Studio Code if you don't have it already.
- WSO2 Integrator: BI Extension: Install the WSO2 Integrator: BI extension. Refer to Install WSO2 Integrator: BI for detailed instructions.
Step 1: Creating a new integration project¶
WSO2 Integrator: BI extension provides a low-code graphical environment to visually design, map, and deploy integrations using Ballerina.
-
Launch VS Code and click the WSO2 Integrator: BI icon on the left sidebar.
You’ll be taken to the welcome page that introduces the integration workspace. -
Below the Get Started Quickly section, you’ll see three separate options:
- Create New Integration – start a new integration project from scratch using the graphical designer.
- Import External Integration – bring in existing integrations from other platforms (e.g., MuleSoft, TIBCO).
- Explore Pre-Built Samples – open existing templates or tutorials.
-
Click Create New Integration.
This opens the integration creation wizard, where you can:- Define the integration name and location.
- Choose to start with a blank project or from a sample template.
- Initialize the workspace structure with folders for input/output mappings, resources, and configuration.
-
Once the project is created, you’ll enter the graphical integration designer view.
From here, you can start adding connectors, data mappings, and logic components to build your flow visually.
This is the entry point for all low-code projects in Ballerina Integrator.
Step 2: Add configurable variables¶
In this step, you will define the input and output file paths as configurable variables.
These parameters make your integration portable and environment-agnostic — you can change file paths without editing the logic.
- In the Project Explorer, select Configurations.
- Click + Add Configuration to open the Configurable Variables panel.
-
Create the following two variables:
-
Name:
inputCSV
Type:string
Default Value:"./input/customer_order_details.csv"
Description: Path to the source CSV file that contains the customer order details. -
Name:
outputXML
Type:string
Default Value:"./output/customer_order_details.xml"
Description: Output path where the generated XML file will be saved.
-
-
Click Save once both configurables are defined.
Your Configurable Variables view should now display:
Name Type Default Value inputCSV string ./input/customer_order_details.csvoutputXML string ./output/customer_order_details.xml
These configurables can now be used by the file connectors and the data mapper in later steps — allowing your integration to dynamically read and write files based on runtime settings.
Step 3: Create a structure to represent each csv row¶
In this step, you’ll define a structure (called a Type in Ballerina) that describes what one row in your CSV file looks like.
Think of it as creating a template so the Data Mapper can recognize each column by name and map them correctly to the XML later.
- In the Project Explorer, click Types and then + Add Type.
- Enter a name for the new structure:
CSV. -
Add the following fields — these represent the columns in your input CSV file:
Field Name Data Type Description order_idText (string) The unique ID for each order. skuText (string) The product code or SKU for the item. qtyText (string) The quantity ordered. priceText (string) The item price for that row. -
Click Save once all fields are added.
Your CSV structure now acts as the blueprint for reading each record from the file.
When the system reads the CSV file, it will treat every line (after the header) as one CSV record containing these four fields.
This step helps the Data Mapper understand the shape of your data.
Instead of dealing with raw text lines, it can now work with meaningful fields like order_id, sku, qty, and price.
Step 4: Generate xml types from a sample payload¶
In this step, you’ll create the XML output structure automatically by pasting a sample XML.
The Integrator’s Type Creator reads this example and builds the corresponding type definitions for you.
- In the Project Explorer, click Types, then click + Add Type.
- Go to the Import tab.
- Select the Format as XML.
-
Paste your sample XML payload in the dialog box. For this example, use:
```xml <Orders> <Row> <index>1</index> <order_id>S001</order_id> <sku>P-1001</sku> <qty>2</qty> <price>149.99</price> </Row> <Row> <index>2</index> <order_id>S002</order_id> <sku>P-3001</sku> <qty>3</qty> <price>39.99</price> </Row> <Row> <index>3</index> <order_id>S003</order_id> <sku>P-2003</sku> <qty>1</qty> <price>89.50</price> </Row> </Orders> ``` -
Click Import to load the sample XML into the type creator.
The Integrator will automatically analyze the payload and infer the XML structure.
The generated structure will appear as:
Orders
└─ Row[]
├─ index: int
├─ order_id: string
├─ sku: string
├─ qty: int
└─ price: decimal
You now have an record type that defines the exact structure of your output xml file.
This type will act as the target structure in the Data Mapper, allowing each CSV record to be mapped directly into a <Row> element under <Orders>.
Step 5: Create an automation and set up CSV reading¶
In this step, you’ll create an Automation entry point in WSO2 Integrator: BI and configure it to read a CSV file from the path defined in your configurable variable (inputCSV).
This automation serves as the starting point of the data-transformation pipeline that converts CSV orders into XML output.
-
Open the Entry Points Panel
From the Project Explorer on the left, expand the
customer_details_csv_to_xmlintegration.
Click Entry Points (+) to add a new integration artifact. -
Select Automation as the Entry Point
The Artifacts palette opens.
Under the Automation section (top of the list), click Automation.
Automation integrations can be triggered manually or periodically, and are ideal for scheduled file transformations. -
Create the Automation
A page titled Automation – An automation that can be invoked periodically or manually appears.
Click the blue Create button to add a new Automation flow.
You may optionally expand Optional Configurations to adjust scheduling or trigger settings, but for this tutorial we keep defaults.The tool shows a brief “Creating…” indicator, and then a new canvas opens.
-
Observe the Automation canvas
Once created, the visual editor displays a blank automation diagram with a starting node.
This represents the root of your pipeline where you will define the steps to read, map, and write data.
Step 6: Add a file read function to the automation flow¶
In this step, you’ll configure the automation’s first functional node — a Call Function block — to read CSV data from the configured input path (inputCSV) into an array of CSV records (CSV[]).
This forms the initial data acquisition step of the workflow.
-
Open the automation flow
With the automation already created, you’ll now see a Start node connected to an Error Handler node.
This is the default template for new automations. -
Add the first node
Below the Start node, Click on the + to add a new operation. The system briefly displays
Generating next suggestion...— this means it’s fetching available node types. -
Choose the node type
Once suggestions appear, look to the right-hand Node Panel under the Statement category.
You’ll see options such as:- Declare Variable
- Assign
- Call Function
- Map Data
-
Click the Call Function to insert this node into the automation flow.
Step 7: Use fileReadCsv to load the csv file into memory¶
Now that your Call Function flow is open, the next step is to configure a CSV file read function using the built-in io:fileReadCsv() operation.
This function reads CSV data from the path defined in your project’s configurables and loads it as a structured array of CSV records that can be mapped later.
-
Open the function search panel
In the right-side Functions panel, scroll or search for a CSV-related function:
- Click inside the Search library functions field.
- Type csv to filter the standard library.
- You’ll see a list under data.csv and io modules containing functions like:
parseStreamparseStringfileReadCsvfileWriteCsv
-
Select
io:fileReadCsvClick fileReadCsv from the results list. A configuration panel opens on the right describing the function:
“Read file content as a CSV. When the expected data type is record[], the first entry of the CSV file should contain matching headers.”
-
Bind the CSV Reader function to a Configurable Path
After selecting the
io:fileReadCsvfunction, the next task is to configure how the file path is supplied.
Rather than hardcoding a literal path, this step dynamically binds the function to a configurable variable (inputCSV) defined in your project’s configurations.-
Switch the input mode of Path
In the
fileReadCsvconfiguration pane:- Locate the Path field at the top.
- By default, it shows a Text input mode.
- Click Expression next to it.
This changes the mode to accept dynamic values such as variables or configurables instead of a static string.
-
Open the value picker
Once in Expression mode:
- A blue ƒx button appears to the left of the input field.
- Click that icon to open the Value Picker dropdown.
-
The picker shows several categories:
Create ValueInputsVariablesConfigurablesFunctions
-
From the Configurables section, select
inputCSV(this is the configurable variable that stores the file path for the input CSV).
-
-
Name the result variable
In the Result field, enter a meaningful variable name to store the data returned by the reader:
csvRecords. This variable will now hold the array of CSV records returned fromfileReadCsv. -
Define the return type
In Return Type, click the type selector box and pick from the list of available record types under your Types section. This ensures the system interprets each row of the CSV as a structured
CSVrecord instead of a generic string map. -
Save the configuration
Click Save to insert the configured node into the automation flow.
You now have a fully functional CSV reader node that:
- Reads the file from
inputCSV, - Parses its contents into structured
CSVdata, - Stores it in the variable
csvRecords.
- Reads the file from
The fileReadCsv node is added to your Automation diagram, connected directly under Start.
This means the Automation will:
- Start execution.
- Use
fileReadCsvto read the input file located at the path stored ininputCSV. -
Store the resulting CSV records in the variable
csvRecordsfor later mapping.Info
Always ensure the first row of your CSV file includes headers that match the
CSVrecord field names. Otherwise,fileReadCsvwill not correctly map the columns to your CSV record.
Step 8: Create the XML output variable and open data mapper¶
Now that the CSV data has been successfully read into the variable csvRecords, the next step is to prepare the XML output structure that the CSV will be transformed into. For this, you will declare a new variable of type Orders and open it in the Data Mapper.
-
Add a Declare Variable Node
- Click the + icon below the
io:fileReadCsvnode in the automation flow. - In the right-hand panel under Statement, select Declare Variable.
- A new node placeholder titled Select node from node panel appears in the flow.
This node will define a new variable to hold the transformed XML data.
- Click the + icon below the
-
Configure the variable name as
xmlRecord. This variable name represents the XML output structure that will hold the mapped data. -
Assign the variable type
- Click the Type field (marked with the blue
Ticon). - Begin typing
Order. - From the dropdown suggestions, select Orders.
This binds the variable to your predefined
Ordersrecord type, which defines the XML schema structure for the output. - Click the Type field (marked with the blue
-
Open the data mapper
Once the variable name and type are defined, click Open in Data Mapper at the bottom right of the properties pane.
This action opens the Data Mapper where you can map CSV fields to XML elements visually.
-
Explore the data mapper interface
Inside the Data Mapper view, you’ll see two main panels:
Left Panel — Input Data
csvRecords→ Type:CSV[]inputCSV→ Type:stringoutputXML→ Type:string
Right Panel — Output Structure
xmlRecord→ Type:OrdersRow[]index(int)order_id(string)sku(string)qty(int)price(decimal)
This view provides a visual workspace to connect input CSV fields with XML output fields directly.
At this point, you have:
- Declared a new variable named
xmlRecordof the typeOrders. - Opened it inside the Data Mapper.
- Verified that both
csvRecords(input) andxmlRecord(output) are available for mapping.
You’re now ready to perform the visual mapping between CSV and XML data structures.
Step 9: Map CSV fields to XML rows in the data mapper¶
Now that you’ve opened the Data Mapper view, it’s time to visually connect the fields between your CSV input and XML output. This step transforms the tabular data structure into a hierarchical XML format.
-
Understand the mapping context
On the left, you have:
csvRecords→ Type:CSV[]- Each item contains:
order_id,sku,qty, andprice(all asstring).
- Each item contains:
On the right, you have:
xmlRecord→ Type:Orders- Contains:
Row[]- Each
Rowhas:index (int),order_id (string),sku (string),qty (int), andprice (decimal).
- Each
- Contains:
-
Start the mapping by linking the record arrays
- Drag from
csvRecords(left) toRow(right) and selectMap Input Array to Output Arrayin the mapping window. - This creates a parent-level mapping between the two collections.
If you see an error such as:
incompatible types: expected 'Row[]', found 'CSV[]'That’s expected — the data mapper is warning that nested mapping is required to handle field-level conversions.
- Click on the connection line drawn from
csvRecordstoRowand click on the bulb shaped icon (4th icon) and select map with query expression.
- Drag from
-
Map individual fields
- Expand both
csvRecordsItemandRowItem. - Start connecting matching fields:
order_id→order_idsku→skuqty→qtyprice→price
You’ll notice that type mismatches occur for numeric fields (
qty,price) since CSV data is read as strings. - Expand both
-
Resolve type mismatches for numeric fields
When connecting
qtyandprice, errors like the following appear:incompatible types: expected 'int', found 'string'
incompatible types: expected 'decimal', found 'string'To fix this, use type conversion expressions directly in the Data Mapper:
For
qty:- Click on the red error line between
qtyfields. -
In the expression editor at the top, replace it with:
check int:fromString(csvRecordsItem.qty)
For
price- Click on the
priceconnection in the mapping canvas. -
In the expression editor at the top, replace the existing mapping with the following conversion function:
check decimal:fromString(csvRecordsItem.price)
This ensures the string values from the CSV are safely parsed into numeric types before mapping.
- Click on the red error line between
-
Add the missing
indexmappingThe
indexfield in the Row record is required and cannot be left unmapped.
If you skip it, the compiler will show this error:missing non-defaultable required record field 'index'To fix this, we’ll dynamically calculate the index value for each CSV record by following the steps below.
- In the Data Mapper, click on the
indexfield under theRowstructure (right side). -
In the expression editor at the top, type the following expression:
csvRecords.indexOf(csvRecordsItem)This retrieves the current record’s position in the csvRecords array (starting from 0).
-
If you see an error like:
incompatible types: expected 'int', found 'int?'that means the result is an optional integer. To handle it safely, wrap it with
<int>:<int>csvRecords.indexOf(csvRecordsItem)
Once applied, the red error indicator on the index mapping disappears.
All lines in the mapper should now appear blue, indicating valid and complete mappings.
Your data mapper now correctly converts CSV input into a fully-typed XML structure, with each record assigned an auto-generated index.
- In the Data Mapper, click on the
-
Click on the x (close button) icon at the right top corner of the datamapper view to return back to automation flow.
Step 10: Convert the Orders record to xml and write it to the output file¶
You already have xmlRecord : Orders populated by the Data Mapper. Now we’ll turn that record into XML and save it.
-
Add
xmldata:toXmlto convert the record to XML.- Click on the + icon in the automation flow and select Call Function to add a new function node to the flow.
- Search for
toXml(from Standard Library → data.xmldata) in the opened functions panel in the right side. -
Click toXml to insert the node below the Declare Variable node.
Configure the toXml node:
- Map Value:
xmlRecord - Result:
xmlResultAsString - Result Type:
xml
- Map Value:
After saving, you should see
xmldata:toXmlin the flow with the result variable name xmlResultAsString. -
Add
io:fileWriteXmlto write the XML to disk- With the Functions panel open, search for
fileWriteXml(under Imported Functions → io). -
Click fileWriteXml to insert it after
xmldata:toXml.Configure the fileWriteXml node:
- Path:
outputXML(this is your output file path variable/string.)- Toggle from Text to Expression by clicking on the Expression icon at the right top corner of the Path Textbox
- Choose the configurables and select outputXML
- Content:
xmlResultAsString
- Path:
Click Save.
- With the Functions panel open, search for
-
Verify the flow and run
Your flow should now show (top → bottom):
io:fileReadCsv→Declare Variable(xmlRecord) →xmldata:toXml(xmlResultAsString) →io:fileWriteXml(outputXML, xmlResultAsString) → Error Handler
Make sure the input CSV file is in the correct location identified by the inputCSV configurable.
Run the integration. On success, the XML produced from xmlRecord will be written to the file path in outputXML.
Note
Now you have successfully converted a CSV file to a XML file.








