drop support for versions before OTP-17 and bump to 3.0.0
This commit is contained in:
parent
1bbe8986c7
commit
fab436e1d5
14 changed files with 64 additions and 286 deletions
33
.github/workflows/main.yml
vendored
Normal file
33
.github/workflows/main.yml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
name: EUnit
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Test on OTP ${{ matrix.otp_version }} and ${{ matrix.os }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
# important to check a pre-23 version that still uses nodetool
|
||||||
|
# plus 23 once it is released
|
||||||
|
otp_version: [22.3.2]
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: gleam-lang/setup-erlang@v1.0.0
|
||||||
|
with:
|
||||||
|
otp-version: ${{ matrix.otp_version }}
|
||||||
|
|
||||||
|
- name: compile
|
||||||
|
run: rebar3 compile
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
run: rebar3 eunit
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
.rebar3
|
||||||
.eunit
|
.eunit
|
||||||
deps
|
deps
|
||||||
ebin
|
ebin
|
||||||
|
|
10
.travis.yml
10
.travis.yml
|
@ -1,10 +0,0 @@
|
||||||
language: erlang
|
|
||||||
script: rebar compile && rebar skip_deps=true eunit
|
|
||||||
otp_release:
|
|
||||||
- 22.0.7
|
|
||||||
- 21.3.3
|
|
||||||
- 20.3
|
|
||||||
- 19.3
|
|
||||||
- 18.3
|
|
||||||
- 17.5
|
|
||||||
- R16B03-1
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
v3.0
|
||||||
|
|
||||||
|
* drop support for OTP versions before 17.0
|
||||||
|
* remove definition options for disabling maps globally, `{return_maps, false}` is still an accepted option to `decode/2`
|
||||||
|
|
||||||
v2.8.2
|
v2.8.2
|
||||||
|
|
||||||
* enable `debug_info` for rebar3
|
* enable `debug_info` for rebar3
|
||||||
|
|
92
README.md
92
README.md
|
@ -1,23 +1,17 @@
|
||||||
# jsx (v2.9.0) #
|
# jsx (v3.0.0) #
|
||||||
|
|
||||||
|
|
||||||
an erlang application for consuming, producing and manipulating [json][json].
|
an erlang application for consuming, producing and manipulating [json][json].
|
||||||
inspired by [yajl][yajl]
|
inspired by [yajl][yajl]
|
||||||
|
|
||||||
**jsx** is built via [rebar3][rebar3], [rebar][rebar] or [mix][mix] and continuous integration testing provided courtesy [travis-ci][travis]
|
**jsx** is built via [rebar3][rebar3]
|
||||||
|
|
||||||
current status: [](http://travis-ci.org/talentdeficit/jsx)
|
current status: 
|
||||||
|
|
||||||
**jsx** is released under the terms of the [MIT][MIT] license
|
**jsx** is released under the terms of the [MIT][MIT] license
|
||||||
|
|
||||||
copyright 2010-2016 alisdair sullivan
|
copyright 2010-2016 alisdair sullivan
|
||||||
|
|
||||||
## really important note ##
|
|
||||||
|
|
||||||
there are a few changes for users upgrading from 1.x. see [CHANGES.md](CHANGES.md)
|
|
||||||
for the overview or [migrating from 1.x](#migrating) for the details
|
|
||||||
|
|
||||||
|
|
||||||
## index ##
|
## index ##
|
||||||
|
|
||||||
* [quickstart](#quickstart)
|
* [quickstart](#quickstart)
|
||||||
|
@ -39,7 +33,6 @@ for the overview or [migrating from 1.x](#migrating) for the details
|
||||||
- [`prettify/1`](#prettify1)
|
- [`prettify/1`](#prettify1)
|
||||||
- [`is_json/1,2`](#is_json12)
|
- [`is_json/1,2`](#is_json12)
|
||||||
- [`is_term/1,2`](#is_term12)
|
- [`is_term/1,2`](#is_term12)
|
||||||
- [`maps_support/0`](#maps_support0)
|
|
||||||
* [callback exports](#callback_exports)
|
* [callback exports](#callback_exports)
|
||||||
- [`Module:init/1`](#moduleinit1)
|
- [`Module:init/1`](#moduleinit1)
|
||||||
- [`Module:handle_event/2`](#modulehandle_event2)
|
- [`Module:handle_event/2`](#modulehandle_event2)
|
||||||
|
@ -55,7 +48,7 @@ Add to `rebar.config`
|
||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
{deps, [
|
{deps, [
|
||||||
...
|
...
|
||||||
{jsx, {git, "https://github.com/talentdeficit/jsx.git", {branch, "v2.8.0"}}}
|
{jsx, "~> 3.0"}
|
||||||
]}.
|
]}.
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
@ -65,19 +58,15 @@ Add to `rebar.config`
|
||||||
```bash
|
```bash
|
||||||
$ rebar3 compile
|
$ rebar3 compile
|
||||||
$ rebar3 eunit
|
$ rebar3 eunit
|
||||||
$ rebar compile
|
|
||||||
$ rebar eunit
|
|
||||||
$ mix compile
|
|
||||||
$ mix eunit
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### to convert a utf8 binary containing a json string into an erlang term ####
|
#### to convert a utf8 binary containing a json string into an erlang term ####
|
||||||
|
|
||||||
```erlang
|
```erlang
|
||||||
1> jsx:decode(<<"{\"library\": \"jsx\", \"awesome\": true}">>).
|
1> jsx:decode(<<"{\"library\": \"jsx\", \"awesome\": true}">>, []).
|
||||||
[{<<"library">>,<<"jsx">>},{<<"awesome">>,true}]
|
|
||||||
2> jsx:decode(<<"{\"library\": \"jsx\", \"awesome\": true}">>, [return_maps]).
|
|
||||||
#{<<"awesome">> => true,<<"library">> => <<"jsx">>}
|
#{<<"awesome">> => true,<<"library">> => <<"jsx">>}
|
||||||
|
2> jsx:decode(<<"{\"library\": \"jsx\", \"awesome\": true}">>, [{return_maps, false}]).
|
||||||
|
[{<<"library">>,<<"jsx">>},{<<"awesome">>,true}]
|
||||||
3> jsx:decode(<<"[\"a\",\"list\",\"of\",\"words\"]">>).
|
3> jsx:decode(<<"[\"a\",\"list\",\"of\",\"words\"]">>).
|
||||||
[<<"a">>, <<"list">>, <<"of">>, <<"words">>]
|
[<<"a">>, <<"list">>, <<"of">>, <<"words">>]
|
||||||
```
|
```
|
||||||
|
@ -85,10 +74,10 @@ $ mix eunit
|
||||||
#### to convert an erlang term into a utf8 binary containing a json string ####
|
#### to convert an erlang term into a utf8 binary containing a json string ####
|
||||||
|
|
||||||
```erlang
|
```erlang
|
||||||
1> jsx:encode([{<<"library">>,<<"jsx">>},{<<"awesome">>,true}]).
|
1> jsx:encode(#{<<"library">> => <<"jsx">>, <<"awesome">> => true}).
|
||||||
<<"{\"library\": \"jsx\", \"awesome\": true}">>
|
|
||||||
2> jsx:encode(#{<<"library">> => <<"jsx">>, <<"awesome">> => true}).
|
|
||||||
<<"{\"awesome\":true,\"library\":\"jsx\"}">>
|
<<"{\"awesome\":true,\"library\":\"jsx\"}">>
|
||||||
|
2> jsx:encode([{<<"library">>,<<"jsx">>},{<<"awesome">>,true}]).
|
||||||
|
<<"{\"library\": \"jsx\", \"awesome\": true}">>
|
||||||
3> jsx:encode([<<"a">>, <<"list">>, <<"of">>, <<"words">>]).
|
3> jsx:encode([<<"a">>, <<"list">>, <<"of">>, <<"words">>]).
|
||||||
<<"[\"a\",\"list\",\"of\",\"words\"]">>
|
<<"[\"a\",\"list\",\"of\",\"words\"]">>
|
||||||
```
|
```
|
||||||
|
@ -132,13 +121,6 @@ false
|
||||||
}">>
|
}">>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### to compile **jsx** so that it always decodes json objects to maps ####
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ JSX_FORCE_MAPS rebar3 compile
|
|
||||||
$ JSX_FORCE_MAPS mix compile
|
|
||||||
```
|
|
||||||
|
|
||||||
## description ##
|
## description ##
|
||||||
|
|
||||||
|
|
||||||
|
@ -172,29 +154,6 @@ quotes but must end with single quotes and must escape any single quotes they co
|
||||||
json and **jsx** only recognize escape sequences as outlined in the json spec. it just
|
json and **jsx** only recognize escape sequences as outlined in the json spec. it just
|
||||||
ignores bad escape sequences leaving them in strings unaltered
|
ignores bad escape sequences leaving them in strings unaltered
|
||||||
|
|
||||||
|
|
||||||
### migrating from 1.x ###
|
|
||||||
|
|
||||||
if you're migrating from jsx v1.x to v2.x in most cases you won't need to
|
|
||||||
make any changes to your code
|
|
||||||
|
|
||||||
support for otp 17.0's new map type is now enabled by default when compiling
|
|
||||||
via rebar for any release that supports them. jsx should still compile cleanly for
|
|
||||||
earlier releases without any user intervention
|
|
||||||
|
|
||||||
if you used any of `replaced_bad_utf8`, `single_quoted_strings`, `comments`,
|
|
||||||
`ignored_bad_escapes` or `relax` you can simply omit them from your calls to jsx,
|
|
||||||
they are all enabled by default now. if you want stricter parsing see the new
|
|
||||||
[`strict` options](#option) available
|
|
||||||
|
|
||||||
if you were using jsx to parse partial json using it's streaming features it is now
|
|
||||||
disabled by default. you'll need to pass the `stream` option to calls to jsx functions
|
|
||||||
to reenable it
|
|
||||||
|
|
||||||
support for `pre_encode` and `post_decode` has been removed. they were fragile and hard
|
|
||||||
to understand and they prevented evolution of the encoding and decoding code
|
|
||||||
|
|
||||||
|
|
||||||
### json <-> erlang mapping ###
|
### json <-> erlang mapping ###
|
||||||
|
|
||||||
**json** | **erlang**
|
**json** | **erlang**
|
||||||
|
@ -261,18 +220,7 @@ see below | `datetime()`
|
||||||
|
|
||||||
* objects
|
* objects
|
||||||
|
|
||||||
json objects are represented by erlang proplists. json maps may also be
|
json objects are represented by erlang maps.
|
||||||
encoded to json and optionally decoded to maps (via the `return_maps`
|
|
||||||
option)
|
|
||||||
|
|
||||||
the empty object has the special representation `[{}]` to differentiate it
|
|
||||||
from the empty list. ambiguities like `[true, false]` prevent the use of
|
|
||||||
the shorthand form of property lists using atoms as properties so all
|
|
||||||
properties must be tuples. all keys must be encoded as in `string` or as
|
|
||||||
atoms or integers (which will be escaped and converted to binaries for
|
|
||||||
presentation to handlers). values should be valid json values. repeated
|
|
||||||
keys are tolerated in json text decoded to erlang terms but are not allowed
|
|
||||||
in erlang terms encoded to json
|
|
||||||
|
|
||||||
* datetime
|
* datetime
|
||||||
|
|
||||||
|
@ -533,9 +481,8 @@ new atoms to the atom table and will result in a `badarg` error if the atom
|
||||||
does not exist. `attempt_atom` will convert keys to atoms when they exist,
|
does not exist. `attempt_atom` will convert keys to atoms when they exist,
|
||||||
and leave them as binary otherwise
|
and leave them as binary otherwise
|
||||||
|
|
||||||
the option `return_maps` will attempt to return objects as maps instead of
|
the option `{return_maps, false}` will return objects as proplists instead
|
||||||
proplists. this option has no effect when used with releases that do not
|
of maps.
|
||||||
support maps
|
|
||||||
|
|
||||||
raises a `badarg` error exception if input is not valid json
|
raises a `badarg` error exception if input is not valid json
|
||||||
|
|
||||||
|
@ -650,17 +597,6 @@ returns true if input is a valid erlang representation of json, false if not
|
||||||
|
|
||||||
what exactly constitutes valid json may be altered via [options](#option)
|
what exactly constitutes valid json may be altered via [options](#option)
|
||||||
|
|
||||||
|
|
||||||
#### `maps_support/0` ####
|
|
||||||
|
|
||||||
```erlang
|
|
||||||
maps_support() -> true | false
|
|
||||||
```
|
|
||||||
|
|
||||||
if **jsx** was compiled with map support enabled returns `true`, else
|
|
||||||
`false`
|
|
||||||
|
|
||||||
|
|
||||||
## callback exports ##
|
## callback exports ##
|
||||||
|
|
||||||
the following functions should be exported from a **jsx** callback module
|
the following functions should be exported from a **jsx** callback module
|
||||||
|
@ -753,8 +689,6 @@ jsx wouldn't be what it is without the contributions of [Paul J. Davis](https://
|
||||||
[yajl]: http://lloyd.github.com/yajl
|
[yajl]: http://lloyd.github.com/yajl
|
||||||
[MIT]: http://www.opensource.org/licenses/mit-license.html
|
[MIT]: http://www.opensource.org/licenses/mit-license.html
|
||||||
[rebar3]: https://rebar3.org
|
[rebar3]: https://rebar3.org
|
||||||
[rebar]: https://github.com/rebar/rebar
|
|
||||||
[mix]: http://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html
|
|
||||||
[meck]: https://github.com/eproxus/meck
|
[meck]: https://github.com/eproxus/meck
|
||||||
[rfc4627]: http://tools.ietf.org/html/rfc4627
|
[rfc4627]: http://tools.ietf.org/html/rfc4627
|
||||||
[travis]: https://travis-ci.org/
|
[travis]: https://travis-ci.org/
|
||||||
|
|
43
mix.exs
43
mix.exs
|
@ -1,43 +0,0 @@
|
||||||
defmodule JSX.Mixfile do
|
|
||||||
use Mix.Project
|
|
||||||
|
|
||||||
def project do
|
|
||||||
[
|
|
||||||
app: :jsx,
|
|
||||||
version: "2.11.0",
|
|
||||||
description: "an erlang application for consuming, producing and manipulating json. inspired by yajl",
|
|
||||||
deps: deps(Mix.env),
|
|
||||||
package: package(),
|
|
||||||
language: :erlang,
|
|
||||||
erlc_options: opts(Mix.env)
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
defp opts(:dev), do: [d: :TEST] ++ opts(:prod)
|
|
||||||
defp opts(_) do
|
|
||||||
force_maps = case System.get_env("JSX_FORCE_MAPS") do
|
|
||||||
nil -> []
|
|
||||||
_ -> [d: :maps_always]
|
|
||||||
end
|
|
||||||
[:debug_info, d: :maps_support] ++ force_maps
|
|
||||||
end
|
|
||||||
|
|
||||||
defp deps(_), do: [{:mixunit, "~> 0.9.2", only: :dev}]
|
|
||||||
|
|
||||||
defp package do
|
|
||||||
[
|
|
||||||
files: [
|
|
||||||
"CHANGES.md",
|
|
||||||
"LICENSE",
|
|
||||||
"mix.exs",
|
|
||||||
"rebar.config",
|
|
||||||
"rebar.config.script",
|
|
||||||
"README.md",
|
|
||||||
"src"
|
|
||||||
],
|
|
||||||
contributors: ["alisdair sullivan"],
|
|
||||||
links: %{"github" => "https://github.com/talentdeficit/jsx"},
|
|
||||||
licenses: ["MIT"]
|
|
||||||
]
|
|
||||||
end
|
|
||||||
end
|
|
1
mix.lock
1
mix.lock
|
@ -1 +0,0 @@
|
||||||
%{"mixunit": {:hex, :mixunit, "0.9.2"}}
|
|
|
@ -1,15 +0,0 @@
|
||||||
Def0 = case erlang:is_builtin(erlang, binary_to_integer, 1) andalso
|
|
||||||
erlang:is_builtin(erlang, binary_to_float, 1) of
|
|
||||||
true -> [];
|
|
||||||
false -> [{d, no_binary_to_whatever}]
|
|
||||||
end,
|
|
||||||
Def1 = case erlang:is_builtin(erlang, is_map, 1) of
|
|
||||||
true -> [{d, maps_support}|Def0];
|
|
||||||
false -> Def0
|
|
||||||
end,
|
|
||||||
Defs = case os:getenv("JSX_FORCE_MAPS") of
|
|
||||||
false -> Def1;
|
|
||||||
_ -> [{d, maps_always}|Def1]
|
|
||||||
end,
|
|
||||||
lists:keystore(erl_opts, 1, CONFIG,
|
|
||||||
{erl_opts, proplists:get_value(erl_opts, CONFIG, []) ++ Defs}).
|
|
|
@ -1,7 +1,7 @@
|
||||||
{application, jsx,
|
{application, jsx,
|
||||||
[
|
[
|
||||||
{description, "a streaming, evented json parsing toolkit"},
|
{description, "a streaming, evented json parsing toolkit"},
|
||||||
{vsn, "2.11.0"},
|
{vsn, "3.0.0"},
|
||||||
{modules, [
|
{modules, [
|
||||||
jsx,
|
jsx,
|
||||||
jsx_encoder,
|
jsx_encoder,
|
||||||
|
@ -18,11 +18,7 @@
|
||||||
stdlib
|
stdlib
|
||||||
]},
|
]},
|
||||||
{env, []},
|
{env, []},
|
||||||
{files, [
|
|
||||||
"src",
|
|
||||||
"rebar.config", "rebar.config.script", "rebar.lock"
|
|
||||||
"README.md", "CHANGES.md", "LICENSE"
|
|
||||||
]},
|
|
||||||
{licenses, ["MIT"]},
|
{licenses, ["MIT"]},
|
||||||
{links, [{"Github", "https://github.com/talentdeficit/jsx"}]}
|
{links, [{"Github", "https://github.com/talentdeficit/jsx"}]}
|
||||||
]}.
|
]}.
|
||||||
|
|
25
src/jsx.erl
25
src/jsx.erl
|
@ -29,7 +29,6 @@
|
||||||
-export([consult/1, consult/2]).
|
-export([consult/1, consult/2]).
|
||||||
-export([encoder/3, decoder/3, parser/3]).
|
-export([encoder/3, decoder/3, parser/3]).
|
||||||
-export([resume/3]).
|
-export([resume/3]).
|
||||||
-export([maps_support/0]).
|
|
||||||
|
|
||||||
-export_type([json_term/0, json_text/0, token/0]).
|
-export_type([json_term/0, json_text/0, token/0]).
|
||||||
-export_type([encoder/0, decoder/0, parser/0, internal_state/0]).
|
-export_type([encoder/0, decoder/0, parser/0, internal_state/0]).
|
||||||
|
@ -42,18 +41,6 @@
|
||||||
-export([init/1, handle_event/2]).
|
-export([init/1, handle_event/2]).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
|
|
||||||
-ifndef(maps_support).
|
|
||||||
-type json_term() :: [{binary() | atom(), json_term()}] | [{},...]
|
|
||||||
| [json_term()] | []
|
|
||||||
| {with_tail, json_term(), binary()}
|
|
||||||
| true | false | null
|
|
||||||
| integer() | float()
|
|
||||||
| binary() | atom()
|
|
||||||
| calendar:datetime().
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
-ifdef(maps_support).
|
|
||||||
-type json_term() :: [{binary() | atom(), json_term()}] | [{},...]
|
-type json_term() :: [{binary() | atom(), json_term()}] | [{},...]
|
||||||
| [json_term()] | []
|
| [json_term()] | []
|
||||||
| {with_tail, json_term(), binary()}
|
| {with_tail, json_term(), binary()}
|
||||||
|
@ -62,7 +49,6 @@
|
||||||
| integer() | float()
|
| integer() | float()
|
||||||
| binary() | atom()
|
| binary() | atom()
|
||||||
| calendar:datetime().
|
| calendar:datetime().
|
||||||
-endif.
|
|
||||||
|
|
||||||
-type json_text() :: binary().
|
-type json_text() :: binary().
|
||||||
|
|
||||||
|
@ -183,17 +169,6 @@ resume(Term, {decoder, State, Handler, Acc, Stack}, Config) ->
|
||||||
resume(Term, {parser, State, Handler, Stack}, Config) ->
|
resume(Term, {parser, State, Handler, Stack}, Config) ->
|
||||||
jsx_parser:resume(Term, State, Handler, Stack, jsx_config:parse_config(Config)).
|
jsx_parser:resume(Term, State, Handler, Stack, jsx_config:parse_config(Config)).
|
||||||
|
|
||||||
|
|
||||||
-spec maps_support() -> boolean().
|
|
||||||
|
|
||||||
-ifndef(maps_support).
|
|
||||||
maps_support() -> false.
|
|
||||||
-endif.
|
|
||||||
-ifdef(maps_support).
|
|
||||||
maps_support() -> true.
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
|
@ -35,18 +35,6 @@
|
||||||
-type config() :: list().
|
-type config() :: list().
|
||||||
-export_type([config/0]).
|
-export_type([config/0]).
|
||||||
|
|
||||||
-ifndef(maps_support).
|
|
||||||
-type json_value() :: list(json_value())
|
|
||||||
| list({binary() | atom(), json_value()})
|
|
||||||
| true
|
|
||||||
| false
|
|
||||||
| null
|
|
||||||
| integer()
|
|
||||||
| float()
|
|
||||||
| binary().
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
-ifdef(maps_support).
|
|
||||||
-type json_value() :: list(json_value())
|
-type json_value() :: list(json_value())
|
||||||
| map()
|
| map()
|
||||||
| true
|
| true
|
||||||
|
@ -55,15 +43,8 @@
|
||||||
| integer()
|
| integer()
|
||||||
| float()
|
| float()
|
||||||
| binary().
|
| binary().
|
||||||
-endif.
|
|
||||||
|
|
||||||
|
|
||||||
-ifdef(maps_always).
|
|
||||||
opts(Opts) -> [return_maps, multi_term] ++ Opts.
|
opts(Opts) -> [return_maps, multi_term] ++ Opts.
|
||||||
-endif.
|
|
||||||
-ifndef(maps_always).
|
|
||||||
opts(Opts) -> [multi_term] ++ Opts.
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
-spec consult(File::file:name_all(), Config::config()) -> [json_value()].
|
-spec consult(File::file:name_all(), Config::config()) -> [json_value()].
|
||||||
|
|
||||||
|
|
|
@ -946,15 +946,8 @@ exp(_, N) -> {finish_float, N}.
|
||||||
finish_number(Rest, Handler, Acc, Stack, Config) ->
|
finish_number(Rest, Handler, Acc, Stack, Config) ->
|
||||||
maybe_done(Rest, handle_event(format_number(Acc), Handler, Config), Stack, Config).
|
maybe_done(Rest, handle_event(format_number(Acc), Handler, Config), Stack, Config).
|
||||||
|
|
||||||
|
|
||||||
-ifndef(no_binary_to_whatever).
|
|
||||||
format_number({integer, Acc}) -> {integer, binary_to_integer(Acc)};
|
format_number({integer, Acc}) -> {integer, binary_to_integer(Acc)};
|
||||||
format_number({float, Acc}) -> {float, binary_to_float(Acc)}.
|
format_number({float, Acc}) -> {float, binary_to_float(Acc)}.
|
||||||
-else.
|
|
||||||
format_number({integer, Acc}) -> {integer, list_to_integer(unicode:characters_to_list(Acc))};
|
|
||||||
format_number({float, Acc}) -> {float, list_to_float(unicode:characters_to_list(Acc))}.
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
|
|
||||||
true(<<$r, $u, $e, Rest/binary>>, Handler, Stack, Config) ->
|
true(<<$r, $u, $e, Rest/binary>>, Handler, Stack, Config) ->
|
||||||
maybe_done(Rest, handle_event({literal, true}, Handler, Config), Stack, Config);
|
maybe_done(Rest, handle_event({literal, true}, Handler, Config), Stack, Config);
|
||||||
|
@ -1882,26 +1875,26 @@ custom_incomplete_handler_test_() ->
|
||||||
return_tail_test_() ->
|
return_tail_test_() ->
|
||||||
[
|
[
|
||||||
{"return_tail with tail", ?_assertEqual(
|
{"return_tail with tail", ?_assertEqual(
|
||||||
{with_tail,[{}],<<"3">>},
|
{with_tail,#{},<<"3">>},
|
||||||
jsx:decode(<<"{} 3">>, [return_tail])
|
jsx:decode(<<"{} 3">>, [return_tail])
|
||||||
)},
|
)},
|
||||||
{"return_tail without tail", ?_assertEqual(
|
{"return_tail without tail", ?_assertEqual(
|
||||||
{with_tail,[{}],<<"">>},
|
{with_tail,#{},<<"">>},
|
||||||
jsx:decode(<<"{}">>, [return_tail])
|
jsx:decode(<<"{}">>, [return_tail])
|
||||||
)},
|
)},
|
||||||
{"return_tail with trimmed whitespace", ?_assertEqual(
|
{"return_tail with trimmed whitespace", ?_assertEqual(
|
||||||
{with_tail,[{}],<<"">>},
|
{with_tail,#{},<<"">>},
|
||||||
jsx:decode(<<"{} ">>, [return_tail])
|
jsx:decode(<<"{} ">>, [return_tail])
|
||||||
)},
|
)},
|
||||||
{"return_tail and streaming", ?_assertEqual(
|
{"return_tail and streaming", ?_assertEqual(
|
||||||
{with_tail,[{}],<<"3">>},
|
{with_tail,#{},<<"3">>},
|
||||||
begin
|
begin
|
||||||
{incomplete, F} = jsx:decode(<<"{">>, [return_tail, stream]),
|
{incomplete, F} = jsx:decode(<<"{">>, [return_tail, stream]),
|
||||||
F(<<"} 3">>)
|
F(<<"} 3">>)
|
||||||
end
|
end
|
||||||
)},
|
)},
|
||||||
{"return_tail and streaming", ?_assertEqual(
|
{"return_tail and streaming", ?_assertEqual(
|
||||||
{with_tail,[{}],<<"">>},
|
{with_tail,#{},<<"">>},
|
||||||
begin
|
begin
|
||||||
%% In case of infinite stream of objects a user does not know
|
%% In case of infinite stream of objects a user does not know
|
||||||
%% when to call F(end_stream).
|
%% when to call F(end_stream).
|
||||||
|
|
|
@ -39,17 +39,11 @@ encode(Term) -> encode(Term, ?MODULE).
|
||||||
|
|
||||||
-spec encode(Term::any(), EntryPoint::module()) -> any().
|
-spec encode(Term::any(), EntryPoint::module()) -> any().
|
||||||
|
|
||||||
-ifndef(maps_support).
|
|
||||||
encode(Term, EntryPoint) -> encode_(Term, EntryPoint).
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
-ifdef(maps_support).
|
|
||||||
encode(Map, _EntryPoint) when is_map(Map), map_size(Map) < 1 ->
|
encode(Map, _EntryPoint) when is_map(Map), map_size(Map) < 1 ->
|
||||||
[start_object, end_object];
|
[start_object, end_object];
|
||||||
encode(Term, EntryPoint) when is_map(Term) ->
|
encode(Term, EntryPoint) when is_map(Term) ->
|
||||||
[start_object] ++ unpack(Term, EntryPoint);
|
[start_object] ++ unpack(Term, EntryPoint);
|
||||||
encode(Term, EntryPoint) -> encode_(Term, EntryPoint).
|
encode(Term, EntryPoint) -> encode_(Term, EntryPoint).
|
||||||
-endif.
|
|
||||||
|
|
||||||
encode_([], _EntryPoint) -> [start_array, end_array];
|
encode_([], _EntryPoint) -> [start_array, end_array];
|
||||||
encode_([{}], _EntryPoint) -> [start_object, end_object];
|
encode_([{}], _EntryPoint) -> [start_object, end_object];
|
||||||
|
@ -75,16 +69,11 @@ unhitch([V|Rest], EntryPoint) ->
|
||||||
EntryPoint:encode(V, EntryPoint) ++ unhitch(Rest, EntryPoint);
|
EntryPoint:encode(V, EntryPoint) ++ unhitch(Rest, EntryPoint);
|
||||||
unhitch([], _) -> [end_array].
|
unhitch([], _) -> [end_array].
|
||||||
|
|
||||||
|
|
||||||
-ifdef(maps_support).
|
|
||||||
unpack(Map, EntryPoint) -> unpack(Map, maps:keys(Map), EntryPoint).
|
unpack(Map, EntryPoint) -> unpack(Map, maps:keys(Map), EntryPoint).
|
||||||
|
|
||||||
unpack(Map, [K|Rest], EntryPoint) when is_integer(K); is_binary(K); is_atom(K) ->
|
unpack(Map, [K|Rest], EntryPoint) when is_integer(K); is_binary(K); is_atom(K) ->
|
||||||
[K] ++ EntryPoint:encode(maps:get(K, Map), EntryPoint) ++ unpack(Map, Rest, EntryPoint);
|
[K] ++ EntryPoint:encode(maps:get(K, Map), EntryPoint) ++ unpack(Map, Rest, EntryPoint);
|
||||||
unpack(_, [], _) -> [end_object].
|
unpack(_, [], _) -> [end_object].
|
||||||
-endif.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
|
@ -44,19 +44,6 @@
|
||||||
-type config() :: list().
|
-type config() :: list().
|
||||||
-export_type([config/0]).
|
-export_type([config/0]).
|
||||||
|
|
||||||
-ifndef(maps_support).
|
|
||||||
-type json_value() :: list(json_value())
|
|
||||||
| list({binary() | atom(), json_value()}) | [{},...]
|
|
||||||
| {with_tail, json_value(), binary()}
|
|
||||||
| true
|
|
||||||
| false
|
|
||||||
| null
|
|
||||||
| integer()
|
|
||||||
| float()
|
|
||||||
| binary().
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
-ifdef(maps_support).
|
|
||||||
-type json_value() :: list(json_value())
|
-type json_value() :: list(json_value())
|
||||||
| list({binary() | atom(), json_value()}) | [{},...]
|
| list({binary() | atom(), json_value()}) | [{},...]
|
||||||
| {with_tail, json_value(), binary()}
|
| {with_tail, json_value(), binary()}
|
||||||
|
@ -67,19 +54,11 @@
|
||||||
| integer()
|
| integer()
|
||||||
| float()
|
| float()
|
||||||
| binary().
|
| binary().
|
||||||
-endif.
|
|
||||||
|
|
||||||
|
|
||||||
-spec to_term(Source::binary(), Config::config()) -> json_value().
|
-spec to_term(Source::binary(), Config::config()) -> json_value().
|
||||||
|
|
||||||
-ifdef(maps_always).
|
|
||||||
to_term(Source, Config) when is_list(Config) ->
|
to_term(Source, Config) when is_list(Config) ->
|
||||||
(jsx:decoder(?MODULE, [return_maps] ++ Config, jsx_config:extract_config(Config)))(Source).
|
(jsx:decoder(?MODULE, [return_maps] ++ Config, jsx_config:extract_config(Config)))(Source).
|
||||||
-endif.
|
|
||||||
-ifndef(maps_always).
|
|
||||||
to_term(Source, Config) when is_list(Config) ->
|
|
||||||
(jsx:decoder(?MODULE, Config, jsx_config:extract_config(Config)))(Source).
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
parse_config(Config) -> parse_config(Config, #config{}).
|
parse_config(Config) -> parse_config(Config, #config{}).
|
||||||
|
|
||||||
|
@ -166,41 +145,6 @@ format_key(Key, Config) ->
|
||||||
|
|
||||||
start_term(Config) when is_list(Config) -> {[], parse_config(Config)}.
|
start_term(Config) when is_list(Config) -> {[], parse_config(Config)}.
|
||||||
|
|
||||||
|
|
||||||
-ifndef(maps_support).
|
|
||||||
%% allocate a new object on top of the stack
|
|
||||||
start_object({Stack, Config}) -> {[{object, []}] ++ Stack, Config}.
|
|
||||||
|
|
||||||
|
|
||||||
%% allocate a new array on top of the stack
|
|
||||||
start_array({Stack, Config}) -> {[{array, []}] ++ Stack, Config}.
|
|
||||||
|
|
||||||
|
|
||||||
%% finish an object or array and insert it into the parent object if it exists or
|
|
||||||
%% return it if it is the root object
|
|
||||||
finish({[{object, []}], Config}) -> {[{}], Config};
|
|
||||||
finish({[{object, []}|Rest], Config}) -> insert([{}], {Rest, Config});
|
|
||||||
finish({[{object, Pairs}], Config}) -> {lists:reverse(Pairs), Config};
|
|
||||||
finish({[{object, Pairs}|Rest], Config}) -> insert(lists:reverse(Pairs), {Rest, Config});
|
|
||||||
finish({[{array, Values}], Config}) -> {lists:reverse(Values), Config};
|
|
||||||
finish({[{array, Values}|Rest], Config}) -> insert(lists:reverse(Values), {Rest, Config});
|
|
||||||
finish(_) -> erlang:error(badarg).
|
|
||||||
|
|
||||||
|
|
||||||
%% insert a value when there's no parent object or array
|
|
||||||
insert(Value, {[], Config}) -> {Value, Config};
|
|
||||||
%% insert a key or value into an object or array, autodetects the 'right' thing
|
|
||||||
insert(Key, {[{object, Pairs}|Rest], Config}) ->
|
|
||||||
{[{object, Key, Pairs}] ++ Rest, Config};
|
|
||||||
insert(Value, {[{object, Key, Pairs}|Rest], Config}) ->
|
|
||||||
{[{object, [{Key, Value}] ++ Pairs}] ++ Rest, Config};
|
|
||||||
insert(Value, {[{array, Values}|Rest], Config}) ->
|
|
||||||
{[{array, [Value] ++ Values}] ++ Rest, Config};
|
|
||||||
insert(_, _) -> erlang:error(badarg).
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
|
|
||||||
-ifdef(maps_support).
|
|
||||||
%% allocate a new object on top of the stack
|
%% allocate a new object on top of the stack
|
||||||
start_object({Stack, Config=#config{return_maps=true}}) ->
|
start_object({Stack, Config=#config{return_maps=true}}) ->
|
||||||
{[{object, #{}}] ++ Stack, Config};
|
{[{object, #{}}] ++ Stack, Config};
|
||||||
|
@ -239,8 +183,6 @@ insert(Value, {[{object, Key, Pairs}|Rest], Config}) ->
|
||||||
insert(Value, {[{array, Values}|Rest], Config}) ->
|
insert(Value, {[{array, Values}|Rest], Config}) ->
|
||||||
{[{array, [Value] ++ Values}] ++ Rest, Config};
|
{[{array, [Value] ++ Values}] ++ Rest, Config};
|
||||||
insert(_, _) -> erlang:error(badarg).
|
insert(_, _) -> erlang:error(badarg).
|
||||||
-endif.
|
|
||||||
|
|
||||||
|
|
||||||
get_key({[{object, Key, _}|_], _}) -> Key;
|
get_key({[{object, Key, _}|_], _}) -> Key;
|
||||||
get_key(_) -> erlang:error(badarg).
|
get_key(_) -> erlang:error(badarg).
|
||||||
|
@ -368,7 +310,6 @@ rep_manipulation_test_() ->
|
||||||
].
|
].
|
||||||
|
|
||||||
|
|
||||||
-ifdef(maps_support).
|
|
||||||
rep_manipulation_with_maps_test_() ->
|
rep_manipulation_with_maps_test_() ->
|
||||||
[
|
[
|
||||||
{"allocate a new object on an empty stack", ?_assertEqual(
|
{"allocate a new object on an empty stack", ?_assertEqual(
|
||||||
|
@ -420,10 +361,10 @@ return_maps_test_() ->
|
||||||
[
|
[
|
||||||
{"an empty map", ?_assertEqual(
|
{"an empty map", ?_assertEqual(
|
||||||
#{},
|
#{},
|
||||||
jsx:decode(<<"{}">>, [return_maps])
|
jsx:decode(<<"{}">>, [])
|
||||||
)},
|
)},
|
||||||
{"an empty map", ?_assertEqual(
|
{"an empty map", ?_assertEqual(
|
||||||
[{}],
|
#{},
|
||||||
jsx:decode(<<"{}">>, [])
|
jsx:decode(<<"{}">>, [])
|
||||||
)},
|
)},
|
||||||
{"an empty map", ?_assertEqual(
|
{"an empty map", ?_assertEqual(
|
||||||
|
@ -432,18 +373,17 @@ return_maps_test_() ->
|
||||||
)},
|
)},
|
||||||
{"a small map", ?_assertEqual(
|
{"a small map", ?_assertEqual(
|
||||||
#{<<"awesome">> => true, <<"library">> => <<"jsx">>},
|
#{<<"awesome">> => true, <<"library">> => <<"jsx">>},
|
||||||
jsx:decode(<<"{\"library\": \"jsx\", \"awesome\": true}">>, [return_maps])
|
jsx:decode(<<"{\"library\": \"jsx\", \"awesome\": true}">>, [])
|
||||||
)},
|
)},
|
||||||
{"a recursive map", ?_assertEqual(
|
{"a recursive map", ?_assertEqual(
|
||||||
#{<<"key">> => #{<<"key">> => true}},
|
#{<<"key">> => #{<<"key">> => true}},
|
||||||
jsx:decode(<<"{\"key\": {\"key\": true}}">>, [return_maps])
|
jsx:decode(<<"{\"key\": {\"key\": true}}">>, [])
|
||||||
)},
|
)},
|
||||||
{"a map inside a list", ?_assertEqual(
|
{"a map inside a list", ?_assertEqual(
|
||||||
[#{}],
|
[#{}],
|
||||||
jsx:decode(<<"[{}]">>, [return_maps])
|
jsx:decode(<<"[{}]">>, [])
|
||||||
)}
|
)}
|
||||||
].
|
].
|
||||||
-endif.
|
|
||||||
|
|
||||||
|
|
||||||
handle_event_test_() ->
|
handle_event_test_() ->
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue