Skip to main content

JSON output

Some partners do not want XML. They want a JSON document, with prices as numbers, availability as true/false, and a missing value spelled null rather than left out.

An XML feed can produce that file for you. Turn on Generate JSON on the Feed information tab and every generation writes a .json copy next to the .xml; its link appears beside the Feed access URL. The option exists for XML feeds only - CSV and TXT have nothing to convert.

The JSON is built from the XML your template produced, so the template decides the shape of both files. What it does not decide by default is the type of each value - that is what the rest of this page is about.

Default output

Without any type declarations every value arrives as a string, exactly as it is written in the XML:

<data><item><id>63</id><price>10.00</price><description/></item></data>
{
"data": {
"item": {
"id": "63",
"price": "10.00",
"description": []
}
}
}

A feed that declares nothing produces exactly the same JSON it always did, including the empty element that comes out as [].

Declaring value types

To get a number, a boolean or a null, say so on the element that carries the value - the same way an XML schema does, with xsi:type and xsi:nil:

DeclarationJSON result
xsi:type="xs:integer"a whole number. Also int, long, short, byte, nonNegativeInteger, positiveInteger, negativeInteger, nonPositiveInteger, unsignedInt, unsignedLong, unsignedShort, unsignedByte
xsi:type="xs:decimal"a number. Also xs:double and xs:float
xsi:type="xs:boolean"true / false. Accepts true, 1, false, 0
xsi:nil="true"null
xsi:type="array"always a list, even with one element

Nothing is guessed from the value itself. A SKU of 0012333 keeps its leading zeros, and a text attribute that happens to read "true" stays text - only what you declare is converted.

Two conveniences worth knowing: the prefix is yours to choose (xs:, xsd: or anything else bound to the schema namespace - all are recognised), and declaring xmlns:xsi on the root element is optional. A template that omits it still gets its types.

One thing to keep in mind: a declaration is an ordinary XML attribute, so it also ends up in the .xml file - only the JSON conversion strips it. If the same feed is submitted as XML to a channel that validates attributes strictly, declare types on a separate feed built for the JSON consumer instead.

Example
<data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<code><![CDATA[{{ product.sku }}]]></code>
<stock xsi:type="xs:integer">{{ product.qty }}</stock>
<price xsi:type="xs:decimal">{{ product.price }}</price>
<discounted xsi:type="xs:decimal">{{ product.final_price }}</discounted>
<availability xsi:type="xs:boolean">{{ product.is_in_stock }}</availability>
</data>
{
"data": {
"code": "0012333",
"stock": 35,
"price": 9999,
"discounted": 34.5,
"availability": true
}
}

price shows what a declared decimal does with a formatted value: 9999.00 ships as 9999, its shortest form, while a genuine fraction such as 34.50 keeps it (34.5). JSON has one number type, so there is no difference between the two beyond how they read.

Empty values

xsi:nil="true" is how the schema vocabulary spells "no value", and it outranks any type declared alongside it. An element that declares a type but carries nothing means the same thing:

<data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<old_price xsi:nil="true"/>
<stock xsi:type="xs:integer" xsi:nil="true"/>
<qty xsi:type="xs:integer"/>
</data>
{
"data": {
"old_price": null,
"stock": null,
"qty": null
}
}

Nilness is itself a boolean, so xsi:nil="1" works as well as xsi:nil="true". And xsi:nil="false" (or "0") says the opposite - the value is present and is converted normally.

Always a list

The shape of a field should not depend on how many rows your catalog happens to have. Without a declaration, a tag that appears once becomes an object and the same tag appearing twice becomes a list - so a product stocked in one warehouse ships something structurally different from every other product. xsi:type="array" settles it:

<data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<warehouses xsi:type="array">
<id>WH-Kyiv-Main</id>
<stock xsi:type="xs:integer">25</stock>
</warehouses>
</data>
{
"data": {
"warehouses": [
{
"id": "WH-Kyiv-Main",
"stock": 25
}
]
}
}

array is the one declaration with no counterpart in the schema vocabulary - it describes the XML-to-JSON mapping, not the XML - so it takes no prefix.

An array declaration with nothing in it (<warehouses xsi:type="array"/>) comes out as null, not as a list holding one empty member.

Attributes and text content

xsi:type and xsi:nil are consumed by the conversion and never reach the JSON. Your own attributes are preserved, under the usual @ prefix, with the element's value under $:

<price xsi:type="xs:decimal" currency="UAH">9999.00</price>
{
"price": {
"@currency": "UAH",
"$": 9999
}
}
A value that does not fit its type stays a string

<stock xsi:type="xs:integer">many</stock> comes out as "many", not as 0. A typo in a template must not silently corrupt a feed, and a wrong value is far easier to spot in the .json than a plausible zero. The same applies to a type nobody recognises - the value is left alone.

For the same reason, a number too long for the platform's integer range keeps its digits as a string instead of being replaced by a wrong one.

Do not declare identifiers as numbers. A GTIN or SKU of 0123456789012 declared as xs:integer becomes 123456789012 - the leading zero is gone, and the partner reading your feed has no way to know it was ever there. Leave such fields undeclared.

Extract level

JSON Extract level sets the depth at which the export begins. 0 exports the whole feed content, and each higher value drops one layer of wrapping tags:

{
"data": {
"item": { "id": "63" }
}
}

With an extract level of 1, the outer data key is gone and the export starts at what it contained:

{
"item": { "id": "63" }
}

Use it when a partner expects the products themselves at the top of the document rather than the envelope your XML needs.

Keep the level as low as the partner allows. The export collects every key found at that depth into one object, so if two different branches of the feed use the same tag name at the extract depth, only the last one survives.

Feeds without a single root element

A template whose output is a sequence of top-level elements - a header tag plus one element per product - is not a document an XML parser would take, but it is converted to JSON all the same. The wrapper used internally to read it never shows up as a key:

<total xsi:type="xs:integer">2</total>
<data xsi:type="array"><code>A</code><price xsi:type="xs:decimal">9999.00</price></data>
<data xsi:type="array"><code>B</code><old_price xsi:nil="true"/></data>
{
"total": 2,
"data": [
{ "code": "A", "price": 9999 },
{ "code": "B", "old_price": null }
]
}

Checking the JSON before you export

With Generate JSON enabled, the feed preview shows both formats: XML and JSON buttons appear above the output, and switching to JSON shows what the export would write. It uses the same extract level the export uses, and it reads Generate JSON from the form you are looking at - so a value you have not saved yet already takes effect.

That is the cheapest place to check a type declaration. Write it, hit Refresh, and see whether the price came out as 9999 or as "9999.00" - without generating the whole feed.

When the JSON cannot be produced

If the generated XML is not well-formed - a truncated file, unbalanced tags, an empty result - the export stops with a message that names the reason and the line in your template's output:

The generated XML cannot be transformed to JSON: Opening and ending tag mismatch: item line 2 and mst_feed_fragment_root (line 2). Fix the template so its output is well-formed.

The reason and the line come from the XML parser and point into your template's output, which is where the problem can be fixed. mst_feed_fragment_root in a message like the one above is not something your template contains - it is the internal wrapper used to read output that has no single root element, and it appears whenever a tag was left unclosed. Read such a message as "the item tag opened on line 2 was never closed".

Fix the template at the tag and line named, then confirm it in the preview - the same message appears in the JSON pane, on the screen where the template is being written.

Why the export stops instead of skipping the JSON

The .json is written before the .xml is published. Skipping it quietly would leave the previous, now stale .json sitting next to a fresh .xml, and anyone consuming the JSON link would keep reading yesterday's catalog without a hint that anything is wrong.