Skip to main content

Transform

priint cloud renderings service allows transforming incoming XML or JSON inputs into target XMLs following the xml-data-format.

Transformation of CSV to XML is a special case treated at the end of this document.

Transformations are requested via the transform or transformBefore parameters of the rest-api. See REST API.

Currently, we only support XSLT transformations.

Given a transform parameter value of stylesheet.xslt the transform worker will search for a valid XSLT document in project path /{project}/transform/xslt/mystylesheet.xslt.

JSON Input​

It is allowed to use JSON as input.

JSON will be transformed into an XML structure that is then piped into the configured XSLT processor.

There is no existing standard how JSON should be converted into XML. The process has several degrees of freedom. At least the following questions mus be answered:

  • JSON allows arrays as direct children of arrays. This only works in XML if the arrays are wrapped with a named element.
  • JSON keys are arbitrary Unicode strings, whereas in XML an element name must follow the rules of a QName (cannot start with a digit, cannot contain whitespace, etc.). We can handle this by putting all keys into XML attributes, thereby making the XML even more verbose

We allow configuring the XML output by the following settings in the config.ion under key jsonToXml:

NameDescription
rootTagName of the document element in the XML.
discardNullsMake the processor skip JSON properties with null values.
convertKeysToAttributesJSON key names are added as XML attributes with name 'keyAttributeName' into elements with name 'elementTag'
keyAttributeNameSee 'convertKeysToAttributes'.
elementTagSee 'convertKeysToAttributes'.
useArrayWrapperEach JSON array is wrapped into an XML element named 'arrayWrapperTag'
arrayWrapperTagSee 'arrayWrapperTag'.

Example for a config.ion. It contains the default settings, so using this is the same as commiting the whole config.ion.

$project/transform/config.json (default settings)
{
jsonToXml: {
rootTag: "root",
elementTag: "item",
discardNulls: true,
convertKeysToAttributes: false,
keyAttributeName: "name",
useArrayWrapper: false,
arrayWrapperTag: "instance",
},
}

JSON example

{
"id": 57478,
"active": true,
"product name": "08/15",
"props": {
"color": "gray"
},
"nil": null,
"list": [
1,
2,
3,
[
"äöü",
{
"3": -1
}
]
]
}

Commented XML output for the above JSON example when config.ion is empty:

default output
<root>
<id>57478</id>
<active>true</active>
<product-name>08/15</product-name> <!-- all chars in JSON key not matching regex [\w.-] will be replaced with dash -->
<props>
<color>gray</color>
</props>
<!-- null values in JSON will not appear in XML -->
<list>1</list>
<list>2</list>
<list>3</list>
<list>äöü</list><!-- arrays in arrays are not supported by default -> useArrayWrapper -->
<list>
<_>-1</_><!-- first char in a JSON key must be alphabetic or underscore - we replace with underscore -->
</list>
</root>

Commented XML output for the above JSON example when convertKeysToAttributes is activated:

convertKeysToAttributes: true
<item name="root">
<item name="id">57478</item>
<item name="active">true</item>
<item name="product name">08/15</item>
<item name="props">
<item name="color">gray</item>
</item>
<item name="list">1</item>
<item name="list">2</item>
<item name="list">3</item>
<item name="list">äöü</item><!-- arrays in arrays are not supported by default -> useArrayWrapper -->
<item name="list">
<item name="3">-1</item>
</item>
</item>

Commented XML output for the above JSON example when arrayWrapper is activated - here with custom element name:

useArrayWrapper: true, arrayWrapperTag: 'arr'
<root>
<id>57478</id>
<active>true</active>
<product-name>08/15</product-name>
<props>
<color>gray</color>
</props>
<list>
<arr>1</arr>
<arr>2</arr>
<arr>3</arr>
<arr><!-- arrays in arrays are supported here -->
<arr>äöü</arr>
<arr>
<_>-1</_>
</arr>
</arr>
</list>
</root>

Showing the effect on deactivating the discardNulls on the JSON example:

discardNulls: false
<root>
<id>57478</id>
<active>true</active>
<product-name>08/15</product-name>
<props>
<color>gray</color>
</props>
<nil/> <!-- nil element appears with empty content -->
<list>1</list>
<list>2</list>
<list>3</list>
<list>äöü</list>
<list>
<_>-1</_>
</list>
</root>
tip
  • If you know that your JSON has no arrays within arrays and uses simple alphanumeric keys (allowing for underscore or dash as delimiter char), then the default settings are a good choice.
  • When you want to be sure that nearly any JSON can be transformed into XML without information loss, then you should use: discardNulls=false, convertKeysToAttributes=true, useArrayWrapper=true. The XML will be more verbose, and hence the XPath expressions in an XSL transformation will be more complicated.

CSV Input​

priint cloud rendering supports CSV format next to XML and JSON.

To use CSV data users MUST send the data with Content-Type text/csv.

Incoming CSV data must be transformed into an XML representation. To achieve this, you MUST add a transform configuration and specify how the CSV data should be parsed.

// /transform/config.ion
{
csvToXml: {
useHeader: true,
columnSeparator: ",",
},
}

If "csvToXml" is not specified, then the CSV data will enter the rendering phase as is. Fanout or fanout-and-aggregate configurations will be excluded then because they require the transformation to XML.

Do not use transformBefore together with CSV data. Users cannot specify an output template for CSV data. There is only one XML output format, and the transformation is implied by the Content-Type header.

If you need another XML format, you can specify a transform step for an XSLT transformation. The input data will be the XML output of the CSV transformation.

CSV Processing Example​

Data​

Alias,Fullname
bborg,"Bork, Björn",1956-06-6

This example is comma separated and uses double-quote to embrace cells that contain comma as part of the content. We have the field names in the first line. Also, we have an additional cell in the first data row that has no corresponding header cell. This cell - the birthdate - is of no interest to use but should not lead to a parsing error. Therefore, we need the following configuration:

Configuration​

// transform/config.ion
{
useHeader: true,
columnSeparator: ",",
quoteChar: '"',
anyProperty: "-", // effectively allows that we have superflous cells in the input (here the birthdate)
}

Request​

> POST /rest/tenants/example.com/projects/my-csv-test/renderpdf
> Host: api.priintcloud.com
> Content-Type: text/csv; charset=latin1
>
> Alias,Fullname
> bborg,"Bork, Björn",1956-06-6

If no charset is given in the content-type header, then UTF-8 is assumed.

Final XML Representation​

The CSV will be transformed into a standard XML structure for the next processing step.

<?xml version="1.0" encoding="UTF-8"?>
<data>
<item type="sheet">
<item type="row">
<item key="Alias" content="bborg"/>
<item key="Fullname" content="Bork, Björn"/>
<item key="-" content="1956-06-6"/>
</item>
</item>
</data>

Complete List of CSV Configuration Properties​

PropertyTypeDefaultDescription
useHeaderBooleantrueColumn names are read from the first row.
columnsArray of StringsnullAn optional array of column names like ["Field1", "Field2", "Field3"].
quoteCharCharacter"Character used for quoting values that contain quote characters or linefeeds.
columnSeparatorCharacter,Character used to separate values. Other commonly used values include tab ('\t') and pipe ('|')
lineSeparatorStringString used to separate data rows. The default allows any of the three standard linefeeds ("\r", "\r\n", "\n")
escapeCharCharacternullOptional character used to escape values. Most commonly defined as backslash (''). Default is 'none'.
skipFirstDataRowBooleanfalseAllows to ignore the first line of data (line 1 if useHeader=false, line 2 if useHeader=true)
strictHeadersBooleanfalseWhether names of columns defined MUST match with actual declaration from the header row (if header row handling enabled): if true, they must be and an exception if thrown if order differs: if false, no verification is performed.
allowCommentsBooleanfalseWhether lines that start with character "#" are processed as comment lines and skipped/ignored.
anyPropertyStringnullAllows to support rows to be longer than the header count. Additional cell will all be named as given in "anyProperty".