Main Page » XQuery » Functions » JSON Functions

JSON Functions

This module contains functions to parse and serialize JSON data. JSON (JavaScript Object Notation) is a popular data exchange format for applications written in JavaScript. As there are notable differences between JSON and XML, or XDM types, no single mapping is ideal for all use cases. For this reason, we offer various mappings, each of which is suited to different purposes. See Parsers if you want to import JSON documents into a database.

Conventions

All functions and errors are in the http://basex.org/modules/json namespace, to which the json prefix is statically bound.

Conversion Formats

A little advice: in the Database Creation dialog of the GUI, if you select JSON Parsing and switch to the Parsing tab, you can see the effects of some of the conversion options.

Direct

The direct conversion format allows a lossless conversion from JSON to XML and back. The transformation is based on the following rules:

  • The resulting document has a json root node.
  • Object pairs are represented via elements. The name of a pair is encoded, as described for the Conversion Functions, and used as element name.
  • Array entries are also represented via elements, with _ as element name.
  • Object and array values are stored in text nodes.
  • The types of values are represented via type attributes:
    • The existing types are string, number, boolean, null, object, and array.
    • As most values are strings, the string type is by default omitted.

Attributes

The attributes format is lossless, too. The transformation is based on the following rules:

  • The resulting document has a json root node.
  • Object pairs are represented via pair elements. The name of a pair is stored in a name attribute.
  • Array entries are represented via item elements.
  • Object and array values are stored in text nodes.
  • The types of values are represented via type attributes:
    • The existing types are string, number, boolean, null, object, and array.
    • As most values are strings, the string type is by default omitted.

JsonML

The jsonml format is designed to convert XML to JSON and back, using the JsonML dialect. JsonML allows the transformation of arbitrary XML documents, but namespaces, comments and processing instructions will be discarded in the transformation process. More details are found in the official JsonML documentation.

W3

The w3 format is lossless, too. It converts JSON data to an XDM value (a map, array, string, number, boolean, or empty sequence) and vice versa. The conversion rules are the same as for fn:parse-json.

The resulting representation consumes less memory than XML-based formats, and values can be directly accessed without conversion. Thus, it is recommended for very large inputs and for efficient ad-hoc processing.

W3-XML

The w3-xml format is another lossless format. It converts a JSON document to an XML node and vice versa. The conversion rules are the same as for fn:json-to-xml.

W3-Mapping

Added: New conversion format.

The w3-mapping format converts JSON data to XML without type attributes, and vice versa. The conversion rules are the same as for fn:map-to-element and fn:element-to-map, and their options (including a conversion plan that controls the element layouts) can be supplied with the mapping option:

  • When parsing, the JSON input is converted to an XDM value (as with the w3 format), which is then converted to an element. The result is a document node.
  • When serializing, document and element nodes are converted to maps, which are then serialized. Other items are serialized unchanged.
  • The input must be an object with a single entry, the key of which becomes the name of the root element. Other inputs can be converted by assigning a root element name with the root key of the mapping option.
  • Keys are not encoded: a key that is no valid element name is rejected.
  • The JSON value null is represented by an element with an xsi:nil="true" attribute.
  • Numbers are parsed with the adaptive number format by default.

The format is not lossless: When XML is serialized, the types of values are inferred from their string values, and an element that occurs only once is serialized as a single value. A conversion plan can be used to ensure that JSON data is serialized back to its original representation.

Options

Added: Serialization option escape-solidus, parsing option json-lines.

Updated: number-parser was replaced with number-format.

Removed: Conversion formats xquery and basic (use w3 and w3-xml).

Added: Option mapping for the w3-mapping format.

The following options are available (the Direction column indicates if an option applies to parsing, serialization, or both operations):

Option Description Allowed Default Direction
format Specifies the format for converting JSON data (see above). direct, attributes, jsonml, w3, w3-xml, w3-mapping direct parse, serialize
liberal Determines if minor deviations from RFC 7159 will be ignored. yes, no; map: boolean no parse
number-format Specifies how the string value of a parsed JSON number is converted to an atomic value: double yields an xs:double, decimal an xs:decimal, and adaptive an xs:integer, xs:decimal or xs:double, depending on the lexical form. double, decimal, adaptive double; w3-mapping: adaptive parse
fallback Returns a custom value for a character that cannot be represented in XML. An example:
With fn($n) { 'INV' }, occurrences of \b or \uFFFF will be replaced with the string INV.
function fn { char(0xFFFD) } parse
duplicates Specifies how duplicate keys are handled. reject, use-first, use-last use-first parse
null Specifies how the JSON value null is represented. any value () parse
json-lines Indicates if the input is parsed as JSON Lines: The input may contain multiple JSON values, each starting on a new line, and a sequence with the converted values is returned. If the input is imported into a database, the converted values are wrapped in a single json-lines root element. yes, no; map: boolean no parse
merge This option is considered when direct or attributes conversion is used:
  • If a name has the same type throughout the data, the type attribute will be omitted. Instead, the name will be listed in additional, type-specific attributes in the root node.
  • The attributes are named by their type in plural (numbers, booleans, nulls, objects and arrays), and the attribute value contains all names with that type, separated by whitespace.
yes, no no parse, serialize
strings Indicates if type attributes will be added for strings. yes, no yes parse, serialize
lax Specifies if lax conversion rules are used to convert QNames to JSON names. yes, no no parse, serialize
escape Indicates if escape sequences are preserved. If disabled, they are expanded (for example, \n becomes a single x0A character, while \u20AC becomes the character ). yes, no; map: boolean no parse
escape Indicates if characters are escaped whenever the JSON syntax requires it. This option can be set to no if strings are already in escaped form and no further escaping is permitted. yes, no; map: boolean yes serialize
escape-solidus Indicates if the forward slash / is escaped as \/. yes, no; map: boolean yes serialize
indent Indicates if whitespace should be added to the output with the aim of improving human legibility. If the option is specified in the query prolog, it overrides the indent serialization parameter. yes, no; map: boolean no serialize
mapping This option is considered when w3-mapping conversion is used: The options of fn:map-to-element and fn:element-to-map, such as the conversion plan that specifies the element layouts, or the name-format. In addition, a root element name can be specified: When parsing, the input is wrapped in an element with this name; when serializing, the root element must have this name, and its content is serialized. If JSON documents are imported into a database, the path to a JSON file with the options can be specified instead of a map. An example:
{ 'plan': { 'Q{http://example.ns/}name': { 'layout': 'record' } }, 'name-format': 'local' }
map {} parse, serialize

Functions

json:doc

Signature
json:doc(  $source   as xs:string,  $options  as map(*)?  := {}) as item()*create
SummaryFetches the JSON document referred to by the given $source and converts it to an XDM value. The $options argument can be used to control the way the input is converted.
Errors
optionsThe specified options are conflicting.
parseThe specified input cannot be parsed as JSON document.

json:parse

Signature
json:parse(  $value    as xs:string?,  $options  as map(*)?  := {}) as item()*
SummaryConverts the JSON $value to an XDM value. If the input can be successfully parsed, it can be serialized back to the original JSON representation (with the w3-mapping format, a suitable conversion plan may be required). The $options argument can be used to control the way the input is converted.
Errors
optionsThe specified options are conflicting.
parseThe specified input cannot be parsed as JSON document.

json:serialize

Signature
json:serialize(  $input    as item()?,  $options  as map(*)?  := {}) as xs:string
SummarySerializes the specified $input as JSON, using the specified $options, and returns the result as string:
  • The input is expected to conform to the results that are created by json:parse.
  • Non-conforming items will be serialized as specified in the 'json' output method of the official recommendation.

Values can also be serialized as JSON with the standard Serialization feature of XQuery:

  • The parameter method needs to be set to json, and
  • the options presented in this article need to be assigned to the json parameter.
Errors
serializeThe specified node cannot be serialized as JSON document.

Examples

Direct Format

Example 1: Adds all JSON documents in a directory to a database.

Query:
let $database := "database"
for $name in file:list('.', false(), '*.json')
let $file := file:read-text($name)
let $json := json:parse($file)
return db:add($database, $json, $name)

Example 2: Converts a simple JSON string to XML and back.

Query
json:parse('{}')
Result
<json type="object"/>
Query
(: serialize result as plain text :)
declare option output:method 'text';
json:serialize(<json type="object"/>)
Result
{ }

Example 3: Converts a JSON string with simple objects and arrays

Query
json:parse('{
  "title": "Talk On Travel Pool",
  "link": "http://www.flickr.com/groups/talkontravel/pool/",
  "description": "Travel and vacation photos from around the world."
}')
Result
<json type="object">
  <title>Talk On Travel Pool</title>
  <link>http://www.flickr.com/groups/talkontravel/pool/</link>
  <description>Travel and vacation photos from around the world.</description>
</json>

Example 4: Converts a JSON string with different data types

Query
let $options := { 'merge': true() }
return json:parse('{
  "first_name": "John",
  "last_name": "Smith",
  "age": 25,
  "address": {
    "street": "21 2nd Street",
    "city": "New York",
    "code": 10021
  },
  "phone": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "mobile",
      "number": 1327724623
    }
  ]
}', $options)
Result
<json objects="json address _" arrays="phone" numbers="age code">
  <first__name>John</first__name>
  <last__name>Smith</last__name>
  <age>25</age>
  <address>
    <street>21 2nd Street</street>
    <city>New York</city>
    <code>10021</code>
  </address>
  <phone>
    <_>
      <type>home</type>
      <number>212 555-1234</number>
    </_>
    <_>
      <type>mobile</type>
      <number type="number">1327724623</number>
    </_>
  </phone>
</json>

JsonML Format

Example 1: Converts all XML documents in a database to the JsonML format and writes them to disk.

Query
for $doc in collection('json')
let $name := document-uri($doc)
let $json := json:serialize($doc, { 'format': 'jsonml' })
return file:write($name, $json)

Example 2: Converts an XML document with elements and text.

Query
json:serialize(doc('flickr.xml'), { 'format': 'jsonml' })
Input flickr.xml
<flickr>
  <title>Talk On Travel Pool</title>
  <link>http://www.flickr.com/groups/talkontravel/pool/</link>
  <description>Travel and vacation photos from around the world.</description>
</flickr>
Result
[ "flickr",
  [ "title", "Talk On Travel Pool" ],
  [ "link", "http://www.flickr.com/groups/talkontravel/pool/" ],
  ["description", "Travel and vacation photos from around the world." ]
]

Example 3: Converts a document with nested elements and attributes to JsonML:

Query
json:serialize(doc('input.xml'), { 'format': 'jsonml' })
Input input.xml
<address id='1'>
  <!-- comments will be discarded -->
  <last_name>Smith</last_name>
  <age>25</age>
  <address xmlns='will be dropped as well'>
    <street>21 2nd Street</street>
    <city>New York</city>
    <code>10021</code>
  </address>
  <phone type='home'>212 555-1234</phone>
</address>
Result
[ "address", { "id": "1" },
  [ "last_name", "Smith" ],
  [ "age", "25"],
  [ "address",
    [ "street", "21 2nd Street" ],
    [ "city", "New York" ],
    [ "code", "10021" ]
  ],
  [ "phone", { "type": "home" }, "212 555-1234" ]
]

W3 Format

Example 1: Converts a JSON string to XQuery.

Query
let $input := '{
  "Title": "Drinks",
  "Author": [ "Jim Daniels", "Jack Beam" ]
}'
let $data := json:parse($input, { 'format': 'w3' })
return map:for-each($data, fn($key, $value) {
  $key || ': ' || string-join($value, ', ')
})
Result
Title: Drinks
Author: Jim Daniels, Jack Beam

Example 2: Converts XDM data to JSON.

Query
for $item in (
  true(),
  'ABC',
  array { 1 to 5 },
  { "Key": "Value" }
)
return json:serialize(
  $item,
  { 'format': 'w3', 'indent': false() }
)
Result
true
"ABC"
[1,2,3,4,5]
{"Key":"Value"}

W3-Mapping Format

Example 1: Converts a JSON string to XML and back, using a conversion plan.

Query
let $json := '{
  "library": {
    "name": "City Library",
    "books": [
      { "title": "Dune", "year": 1965 },
      { "title": "Solaris", "year": 1961 }
    ]
  }
}'
let $options := {
  'format': 'w3-mapping',
  'mapping': {
    'plan': {
      'books': { 'layout': 'list', 'child': 'book' }
    }
  }
}
let $xml := json:parse($json, $options)
return (
  $xml,
  json:serialize($xml, $options)
)
Result
<library>
  <name>City Library</name>
  <books>
    <book>
      <title>Dune</title>
      <year>1965</year>
    </book>
    <book>
      <title>Solaris</title>
      <year>1961</year>
    </book>
  </books>
</library>
{"library":{"name":"City Library","books":[{"title":"Dune","year":1965},{"title":"Solaris","year":1961}]}}

Example 2: Converts a JSON array, using a root element name.

Query
json:parse('[ "a", "b" ]', {
  'format': 'w3-mapping',
  'mapping': {
    'root': 'list',
    'plan': { 'list': { 'layout': 'list', 'child': 'entry' } }
  }
})
Result
<list>
  <entry>a</entry>
  <entry>b</entry>
</list>

Errors

CodeDescription
optionsThe specified options are conflicting.
parseThe specified input cannot be parsed as JSON document.
serializeThe specified node cannot be serialized as JSON document.

Changelog

Version 13.0
  • Added: Serialization option escape-solidus, parsing option json-lines.
  • Added: Conversion format w3-mapping, with the option mapping.
  • Updated: number-parser was replaced with number-format.
  • Removed: Conversion formats xquery and basic (use w3 and w3-xml).
Version 12.0Version 11.0
  • Added: New number-parser, fallback, duplicates and null options (derived from fn:parse-json).
Version 10.0
  • Updated: indent: Default changed from yes to no.
Version 9.4Version 9.1
  • Updated: json:parse can be called with empty sequence.
Version 9.0
  • Updated: map format renamed to xquery.
  • Updated: error codes updated; errors now use the module namespace
Version 8.4
  • Updated: unescape changed to escape.
Version 8.2
  • Added: Conversion format basic.
Version 8.0
  • Added: liberal option.
  • Updated: Serialization aligned with the json output method of the official specification.
  • Removed: spec option.
Version 7.8
  • Updated: json:parse now returns a document node instead of an element, or an XQuery map if format is set to map.
  • Removed: json:parse-ml, json:serialize-ml.
Version 7.7.2
  • Updated: $options argument added to json:parse and json:serialize.
  • Updated: json:parse-ml, json:serialize-ml are now deprecated.
Version 7.0
  • Added: New module added.

⚡Generated with XQuery