.. _library_json_lines:

``json_lines``
==============

The ``json_lines`` library provides predicates for parsing and
generating data in the JSON Lines format based on the proposal found at:

https://jsonlines.org

It includes parametric objects whose parameters allow selecting the
representation for parsed JSON objects (``curly`` or ``list``), JSON
text strings (``atom``, ``chars``, or ``codes``) and JSON pairs
(``dash``, ``equal``, or ``colon``).

API documentation
-----------------

Open the
`../../apis/library_index.html#json_lines <../../apis/library_index.html#json_lines>`__
link in a web browser.

Loading
-------

To load all entities in this library, load the ``loader.lgt`` file:

::

   | ?- logtalk_load(json_lines(loader)).

Testing
-------

To test this library predicates, load the ``tester.lgt`` file:

::

   | ?- logtalk_load(json_lines(tester)).

Some of the sample JSON test files are based on examples published at:

https://jsonlines.org/

Representation
--------------

The following choices of syntax have been made to represent JSON
elements as terms:

- By default, JSON objects are represented using curly-bracketed terms,
  ``{Pairs}``, where each pair uses the representation ``Key-Value``
  (see below for alternative representations).

- Arrays are represented using lists.

- Text strings can be represented as atoms, ``chars(List)``, or
  ``codes(List)``. The default when decoding is to use atoms when using
  the ``json_lines`` object. To decode text strings into lists of chars
  or codes, use the ``json_lines/1`` object with the parameter bound to
  ``chars`` or ``codes``. For example:

  ::

       | ?- json_lines::parse(codes([34,104,101,108,108,111,34]), Terms).
       Terms = [hello]
       yes

       | ?- json_lines(atom)::parse(codes([34,104,101,108,108,111,34]), Terms).
       Terms = [hello]
       yes

       | ?- json_lines(chars)::parse(codes([34,104,101,108,108,111,34]), Terms).
       Terms = [chars([h,e,l,l,o])]
       yes

       | ?- json_lines(codes)::parse(codes([34,104,101,108,108,111,34]), Terms).
       Terms = [codes([104,101,108,108,111])]
       yes

- The JSON values ``false``, ``true`` and ``null`` are represented by,
  respectively, the ``@false``, ``@true`` and ``@null`` compound terms.

The following table exemplifies the term equivalents of JSON elements
using default representations for objects, pairs, and strings:

========================= =========================
JSON                      term
========================= =========================
[1,2]                     [1,2]
true                      @true
false                     @false
null                      @null
-1                        -1
[1.2345]                  [1.2345]
[]                        []
[2147483647]              [2147483647]
[0]                       [0]
[1234567890123456789]     [1234567890123456789]
[false]                   [@false]
[-2147483648]             [-2147483648]
{"a":null,"foo":"bar"}    {a-@null, foo-bar}
[2.225073858507201e-308]  [2.225073858507201e-308]
[0,1]                     [0,1]
[2.2250738585072014e-308] [2.2250738585072014e-308]
[1.7976931348623157e+308] [1.7976931348623157e+308]
[0.0]                     [0.0]
[4294967295]              [4294967295]
[-1234567890123456789]    [-1234567890123456789]
["foo"]                   [foo]
[1]                       [1]
[null]                    [@null]
[-1.2345]                 [-1.2345]
[5.0e-324]                [5.0e-324]
[-1]                      [-1]
[true]                    [@true]
[9223372036854775807]     [9223372036854775807]
========================= =========================

For JSON objects that are two possible term representations:

===================== ===============
JSON object           term (curly)
===================== ===============
{"a":1, "b":2, "c":3} {a-1, b-2, c-3}
{}                    {}
===================== ===============

and:

===================== =====================
JSON object           term (list)
===================== =====================
{"a":1, "b":2, "c":3} json([a-1, b-2, c-3])
{}                    json([])
===================== =====================

For JSON pairs that are three possible representations:

===================== ===============
JSON object           term (dash)
===================== ===============
{"a":1, "b":2, "c":3} {a-1, b-2, c-3}
===================== ===============

and:

===================== ===============
JSON object           term (equal)
===================== ===============
{"a":1, "b":2, "c":3} {a=1, b=2, c=3}
===================== ===============

and:

===================== ===============
JSON object           term (colon)
===================== ===============
{"a":1, "b":2, "c":3} {a:1, b:2, c:3}
===================== ===============

By default, the curly-term representation and the dash pair
representation are used. The ``json/3`` parametric object allows
selecting the desired representation choices. For example:

::

   | ?- json_lines(curly,dash,atom)::parse(atom('{"a":1, "b":2, "c":3}'), JSONL).
   JSONL = [{a-1, b-2, c-3}]
   yes

   | ?- json_lines(list,equal,atom)::parse(atom('{"a":1, "b":2, "c":3}'), JSONL).
   JSONL = [json([a=1, b=2, c=3])]
   yes

   | ?- json_lines(curly,colon,atom)::parse(atom('{"a":1, "b":2, "c":3}'), JSONL).
   JSONL = [{a:1, b:2, c:3}]
   yes

Encoding
--------

Encoding is accomplished using the ``generate/2`` predicate. For
example:

::

   | ?- json_lines::generate(codes(Encoding), [a,{b-c}]).
   Encoding = [34,97,34,10,123,34,98,34,58,34,99,34,125,10]
   yes

Alternatively:

::

   | ?- json_lines::generate(chars(Encoding), [a,{b-c}]).
   Encoding = ['"',a,'"','\n','{','"',b,'"',:,'"',c,'"','}','\n']
   Yes

   | ?- json_lines::generate(atom(Encoding), [a,{b-c}]).
   Encoding = '"a"\n{"b":"c"}\n'
   Yes

Notice that ``generate/2`` takes, as second argument, a Prolog term that
corresponds to the JSON syntax in Prolog and produces the corresponding
JSON output in the format specified in the first argument:
(``codes(Variable)``, ``stream(Stream)``, ``file(File)``,
``chars(Variable)`` or ``atom(Variable)``).

Decoding
--------

Decoding is accomplished using the ``parse/2`` predicate. For example,
to decode a given json file:

::

   | ?- json_lines::parse(file('simple/data.jsonl'), Terms).
   Term = [{a-[b]}]
   yes

The ``parse/2`` predicate first argument must indicate the input source
(``codes(Codes)``, ``stream(Stream)``, ``line(Stream)``, ``file(Path)``,
``chars(Chars)`` or ``atom(Atom)``) containing a JSON payload to be
decoded into the Prolog term in the second argument.

Known issues
------------

Some tests may fail on backends such as ECLiPSe and GNU Prolog that
don't support Unicode.
