Giter Club home page Giter Club logo

ppx_yojson's Introduction

ppx_yojson

PPX extension for JSON literals and patterns. It supports yojson and ezjsonm.

Based on an original idea by @emillon.

Build Status

Overview

ppx_yojson lets you write Yojson or Ezjsonm expressions and patterns using ocaml syntax to make your code more concise and readable.

It rewrites %yojson and %ezjsonm extension points based on the content of the payload.

For example you can turn:

let json =
  `List
    [ `Assoc
        [ ("name", `String "Anne")
        ; ("grades", `List [`String "A"; `String "B-"; `String "B+"]
        ]
    ; `Assoc
        [ ("name", `String "Bernard")
        ; ("grades", `List [`String "B+"; `String "A"; `String "B-"]
        ]
    ]

into:

let json =
  [%yojson
    [ {name = "Anne"; grades = ["A"; "B-"; "B+"]}
    ; {name = "Bernard"; grades = ["B+"; "A"; "B-"]}
    ]
  ]

Installation and usage

You can install ppx_yojson using opam:

$ opam install ppx_yojson

If you're building your library or app with dune, add the following field to your library, executable or test stanza:

(preprocess (pps ppx_yojson))

You can now use the %yojson or %ezjsonm extensions in your code. See the expressions and patterns sections for the detailed syntax.

Syntax

Expressions

The expression rewriter supports the following Yojson and Ezjsonm values:

  • Null: [%yojson None] or [%ezjsonm None]
  • Bool of bool: [%yojson true] or [%ezjsonm true]
  • Float of float: [%yojson 1.2e+10] or [%ezjsonm 1.2e+10]. Note that with %ezjsonm all numeric literals are converted to Float of float.
  • Int of int: [%yojson 0xff]. As long as the int literal in the payload fits in an int, the 0x, 0o and 0b notations are accepted.
  • Intlit of string: [%yojson 100000000000000000000000000000000]. For arbitrary long integers. int64, int32 and nativeint literals are also rewritten as Intlit for consistency with ppx_deriving_yojson. 0x, 0o and 0b notations are currently not supported and the rewriter will raise an error.
  • String of string: [%yojson "abc"] or [%ezjsonm "abc"]
  • List of json list: [%yojson [1; 2; 3]]. It supports mixed type list as well such as ["a"; 2].
  • A of value list: [%ezjsonm ["a"; 2]]
  • Assoc of (string * json) list: [%yojson {a = 1; b = "b"}]
  • O of (string * value) list: [%ezjsonm {a = 1; b = "b"}]
  • Any valid combination of the above

The resulting expression are not constrained, meaning it works with Yojson.Safe or Yojson.Basic regardless.

Anti-quotation

You can escape regular Yojson or Ezjsonm expressions within a payload using [%aq json_expr]. You can use this to insert variables in the payload. For example:

let a = `String "a"
let json = [%yojson { a = [%aq a]; b = "b"}]

is rewritten as:

let a = `String "a"
let json = `Assoc [("a", a); (b, `String "b")]

Note that the payload in a %aq extension should always subtype one of the Yojson types.

Patterns

Note that the pattern extension expects a pattern payload and must thus be invoked as [%yojson? pattern] or [%ezjsonm? pattern].

The pattern rewriter supports the following:

  • Null, Bool of bool, Float of float, Int of int, Intlit of string, String of string, List of json list with the same syntax as for expressions and will be rewritten to a pattern matching that json value.
  • Assoc of (string * json) list and O of (string * value) list with the same syntax as for expressions but with a few restrictions. The record pattern in the payload must be closed (ie no ; _}) and have less than 4 fields. See details below.
  • Var patterns: they are just rewritten as var patterns meaning they will bind to a Ezjsonm.value, Yojson.Safe.json or whatever Yojson type you're using that's compatible with the above.
  • The wildcard pattern: it gets rewritten as, well, a wildcard pattern
  • Any valid combination of the above

Record patterns

Json objects fields order doesn't matter so you'd expect the {a = 1; b = true} pattern to match regardless of the parsed json being {"a": 1, "b": true} or {"b": true, "a": 1}.

Since json objects are represented as lists, the order of the fields in the rewritten pattern does matter.

To allow you to write such patterns concisely and without having to care for the order of the fields, the record pattern is expanded to an or-pattern that matches every permutation of the (string * json) list. This is the reason of the limitations mentioned in the above list. Also note that there is no limitation on nesting such patterns but you probably want to avoid doing that too much.

This is provided mostly for convenience. If you want efficient code and/or to handle complex json objects I recommend that you use ppx_deriving_yojson or ppx_yojson_conv instead.

To clarify, the following code:

let f = function
  | [%yojson? {a = 1; b = true}] -> (1, true)

is expanded into:

let f = function
  | ( `Assoc [("a", `Int 1); ("b", `Bool true)]
    | `Assoc [("b", `Bool true); ("a", `Int 1)]
    ) -> (1, true)

Anti-quotation

You can also escape regular Ezjsonm or Yojson patterns in ppx_yojson pattern extensions' payload using [%aq? json_pat]. You can use it to further deconstruct a JSON value. For example:

let f = function
  | [%yojson? {a = [%aq? `Int i]} -> i + 1

is expanded into:

let f = function
  | `Assoc [("a", `Int i)] -> i + 1

Advanced use

Object keys

Some valid JSON object key's name are not valid OCaml record fields. Any capitalized word or OCaml keyword such as type or object.

You can still assemble such JSON literals with ppx_yojson using a leading underscore in the record field name. It will strip exactly one leading underscore.

For example, the following:

let json = [%yojson { _type = "a"; __object = "b"}]

will be expanded to:

let json = `Assoc [("type", `String "a"); ("_object", `String "b")]

Alternatively, you can attach an [@as "key_name"] attribute to the relevant field to provide the key to use in the JSON object literal.

The following:

let json = [%yojson {dummy = "a" [@as "type"]; some_key = "b"}

will be expanded to:

let json = `Assoc [("type", `String "a"); ("some_key", `String "b")]

ppx_yojson's People

Contributors

mefyl avatar nathanreb avatar norubidium avatar pitag-ha avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

ppx_yojson's Issues

Add an install section to the README

The README should have a section for complete ocaml beginners, having a section explaining how to install the package using opam and how to build code using ppx_yojson with dune would be a nice minimal step in the right direction.

Embed errors in the generated AST instead of raising.

Embedding errors directly into the AST using the special [%ocaml.error "Some error msg"] is the preferred way of reporting errors in ppxlib based ppx-es so it would be nice to migrate from raising through Location.raise_error* to embedding errors directly.

This would allow reporting several errors at once for a given ppx_yojson extension.

Example in README is wrong

let json =
  `List
    [ `Assoc
        [ ("name", `String "Anne")
        ; ("grades", `List [`String "A"; `String "B-"; `String "B+"]
        ]
    ; `Assoc
        [ ("name", `String "Bernard")
        ; ("grades", `List [`String "B+"; `String "A"; `String "B-"]
        ]
    ]

into:

let json =
  [%yojson
    [ {name = "Anne"; grades = ["A"; "B-"; "B+"]}
    ; {name = "Anne"; grades = ["A"; "B-"; "B+"]}
    ]
  ]

Note that the PPX code does not contain the grades for Bernhard but for Anne twice.

v1.0.0

Internals

  • Test successful rewriting
  • Add CI (#2)
  • Extract Ppx_yojson_lib (#5)
  • Extract Ppx_yojson_lib tests (#5)
  • Test error reporting (#7)

Features

  • Expressions
  • Patterns (#8)
  • Antiquotations for expressions (#12)
  • Antiquotations for patterns (#18)

Antiquotations for expressions

We want this:

let json =
  let s = `String "s" in
  [%yojson {a = [%y s]; b = 2}]

to be rewritten as:

let json =
  let s = `String "s" in
  `Assoc [("a", s); ("b", `Int 2)]

Antiquotations for patterns

We want this:

match json with
| [%yojson? [%y? `String s]] -> s
| [%yojson? {a = [%y? a]; b = [%y? `Int b]}] ->
  Printf.sprintf "%s and %d" (Yojson.Safe.to_string a) b

to be rewritten as:

match json with
| `String s -> s
| `Assoc [("a", a); ("b", `Int b)] ->
  Printf.sprintf "%s and %d" (Yojson.Safe.to_string a) b

OCaml 5.2 support

Using the preview of ppxlib for OCaml 5.2 I get the following failure when in the tests of ppx_yojson:

#=== ERROR while compiling ppx_yojson.1.3.0 ===================================#
# context              2.2.0~beta2~dev | linux/x86_64 | ocaml-variants.5.2.0+trunk | file:///home/opam/opam-repository
# path                 ~/.opam/5.2/.opam-switch/build/ppx_yojson.1.3.0
# command              ~/.opam/5.2/bin/dune build -p ppx_yojson -j 1 @install @runtest
# exit-code            1
# env-file             ~/.opam/log/ppx_yojson-183-904b4b.env
# output-file          ~/.opam/log/ppx_yojson-183-904b4b.out
### output ###
# File "test/rewriter/errors/expr_anti_quotation_payload.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/expr_anti_quotation_payload.expected _build/default/test/rewriter/errors/expr_anti_quotation_payload.actual
# diff --git a/_build/default/test/rewriter/errors/expr_anti_quotation_payload.expected b/_build/default/test/rewriter/errors/expr_anti_quotation_payload.actual
# index 0bc8ed5..05faae9 100644
# --- a/_build/default/test/rewriter/errors/expr_anti_quotation_payload.expected
# +++ b/_build/default/test/rewriter/errors/expr_anti_quotation_payload.actual
# @@ -1,4 +1,2 @@
#  File "expr_anti_quotation_payload.ml", line 1, characters 38-45:
# -1 | let bad_anti_quote_payload = [%yojson [%y? _]]
# -                                          ^^^^^^^
#  Error: ppx_yojson: bad antiquotation payload, should be a single expression
# File "test/rewriter/errors/expr_integer_literal_binary.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/expr_integer_literal_binary.expected _build/default/test/rewriter/errors/expr_integer_literal_binary.actual
# diff --git a/_build/default/test/rewriter/errors/expr_integer_literal_binary.expected b/_build/default/test/rewriter/errors/expr_integer_literal_binary.actual
# index 7ca0aae..264ae9d 100644
# --- a/_build/default/test/rewriter/errors/expr_integer_literal_binary.expected
# +++ b/_build/default/test/rewriter/errors/expr_integer_literal_binary.actual
# @@ -1,4 +1,2 @@
#  File "expr_integer_literal_binary.ml", line 1, characters 39-150:
# -1 | let invalid_integer_literal = [%yojson 0b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111]
# -                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#  Error: ppx_yojson: unsupported payload
# File "test/rewriter/errors/expr_integer_literal_hex.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/expr_integer_literal_hex.expected _build/default/test/rewriter/errors/expr_integer_literal_hex.actual
# diff --git a/_build/default/test/rewriter/errors/expr_integer_literal_hex.expected b/_build/default/test/rewriter/errors/expr_integer_literal_hex.actual
# index 801b52a..86d1959 100644
# --- a/_build/default/test/rewriter/errors/expr_integer_literal_hex.expected
# +++ b/_build/default/test/rewriter/errors/expr_integer_literal_hex.actual
# @@ -1,4 +1,2 @@
#  File "expr_integer_literal_hex.ml", line 1, characters 39-83:
# -1 | let invalid_integer_literal = [%yojson 0xffffffffffffffffffffffffffffffffffffffffff]
# -                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#  Error: ppx_yojson: unsupported payload
# File "test/rewriter/errors/expr_integer_literal_octal.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/expr_integer_literal_octal.expected _build/default/test/rewriter/errors/expr_integer_literal_octal.actual
# diff --git a/_build/default/test/rewriter/errors/expr_integer_literal_octal.expected b/_build/default/test/rewriter/errors/expr_integer_literal_octal.actual
# index 9ac6775..3fee815 100644
# --- a/_build/default/test/rewriter/errors/expr_integer_literal_octal.expected
# +++ b/_build/default/test/rewriter/errors/expr_integer_literal_octal.actual
# @@ -1,4 +1,2 @@
#  File "expr_integer_literal_octal.ml", line 1, characters 39-86:
# -1 | let invalid_integer_literal = [%yojson 0o777777777777777777777777777777777777777777777]
# -                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#  Error: ppx_yojson: unsupported payload
# File "test/rewriter/errors/expr_unsupported_payload.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/expr_unsupported_payload.expected _build/default/test/rewriter/errors/expr_unsupported_payload.actual
# diff --git a/_build/default/test/rewriter/errors/expr_unsupported_payload.expected b/_build/default/test/rewriter/errors/expr_unsupported_payload.actual
# index 5e5333b..796cee0 100644
# --- a/_build/default/test/rewriter/errors/expr_unsupported_payload.expected
# +++ b/_build/default/test/rewriter/errors/expr_unsupported_payload.actual
# @@ -1,4 +1,2 @@
#  File "expr_unsupported_payload.ml", line 1, characters 35-40:
# -1 | let unsupported_payload = [%yojson Ok ()]
# -                                       ^^^^^
#  Error: ppx_yojson: unsupported payload
# File "test/rewriter/errors/expr_unsupported_record_field.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/expr_unsupported_record_field.expected _build/default/test/rewriter/errors/expr_unsupported_record_field.actual
# diff --git a/_build/default/test/rewriter/errors/expr_unsupported_record_field.expected b/_build/default/test/rewriter/errors/expr_unsupported_record_field.actual
# index a69dcac..b6a7be9 100644
# --- a/_build/default/test/rewriter/errors/expr_unsupported_record_field.expected
# +++ b/_build/default/test/rewriter/errors/expr_unsupported_record_field.actual
# @@ -1,4 +1,2 @@
#  File "expr_unsupported_record_field.ml", line 1, characters 41-48:
# -1 | let unsupported_record_field = [%yojson {A.field = 0}]
# -                                             ^^^^^^^
#  Error: ppx_yojson: unsupported record field
# File "test/rewriter/errors/pat_anti_quotation_payload.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/pat_anti_quotation_payload.expected _build/default/test/rewriter/errors/pat_anti_quotation_payload.actual
# diff --git a/_build/default/test/rewriter/errors/pat_anti_quotation_payload.expected b/_build/default/test/rewriter/errors/pat_anti_quotation_payload.actual
# index 972110f..86b71e4 100644
# --- a/_build/default/test/rewriter/errors/pat_anti_quotation_payload.expected
# +++ b/_build/default/test/rewriter/errors/pat_anti_quotation_payload.actual
# @@ -1,4 +1,2 @@
#  File "pat_anti_quotation_payload.ml", line 2, characters 14-26:
# -2 |   | [%yojson? [%y `Int _a]] -> false
# -                  ^^^^^^^^^^^^
#  Error: ppx_yojson: bad antiquotation payload, should be a pattern
# File "test/rewriter/errors/pat_integer_literal_binary.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/pat_integer_literal_binary.expected _build/default/test/rewriter/errors/pat_integer_literal_binary.actual
# diff --git a/_build/default/test/rewriter/errors/pat_integer_literal_binary.expected b/_build/default/test/rewriter/errors/pat_integer_literal_binary.actual
# index 1c8e521..14b4be3 100644
# --- a/_build/default/test/rewriter/errors/pat_integer_literal_binary.expected
# +++ b/_build/default/test/rewriter/errors/pat_integer_literal_binary.actual
# @@ -1,4 +1,2 @@
#  File "pat_integer_literal_binary.ml", line 2, characters 14-125:
# -2 |   | [%yojson? 0b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111] -> false
# -                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#  Error: ppx_yojson: unsupported payload
# File "test/rewriter/errors/pat_integer_literal_hex.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/pat_integer_literal_hex.expected _build/default/test/rewriter/errors/pat_integer_literal_hex.actual
# diff --git a/_build/default/test/rewriter/errors/pat_integer_literal_hex.expected b/_build/default/test/rewriter/errors/pat_integer_literal_hex.actual
# index ec866aa..f3c14ca 100644
# --- a/_build/default/test/rewriter/errors/pat_integer_literal_hex.expected
# +++ b/_build/default/test/rewriter/errors/pat_integer_literal_hex.actual
# @@ -1,4 +1,2 @@
#  File "pat_integer_literal_hex.ml", line 2, characters 14-58:
# -2 |   | [%yojson? 0xffffffffffffffffffffffffffffffffffffffffff] -> false
# -                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#  Error: ppx_yojson: unsupported payload
# File "test/rewriter/errors/pat_integer_literal_octal.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/pat_integer_literal_octal.expected _build/default/test/rewriter/errors/pat_integer_literal_octal.actual
# diff --git a/_build/default/test/rewriter/errors/pat_integer_literal_octal.expected b/_build/default/test/rewriter/errors/pat_integer_literal_octal.actual
# index be23b49..e9d6d2a 100644
# --- a/_build/default/test/rewriter/errors/pat_integer_literal_octal.expected
# +++ b/_build/default/test/rewriter/errors/pat_integer_literal_octal.actual
# @@ -1,4 +1,2 @@
#  File "pat_integer_literal_octal.ml", line 2, characters 14-61:
# -2 |   | [%yojson? 0o777777777777777777777777777777777777777777777] -> false
# -                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#  Error: ppx_yojson: unsupported payload
# File "test/rewriter/errors/pat_too_many_record_field.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/pat_too_many_record_field.expected _build/default/test/rewriter/errors/pat_too_many_record_field.actual
# diff --git a/_build/default/test/rewriter/errors/pat_too_many_record_field.expected b/_build/default/test/rewriter/errors/pat_too_many_record_field.actual
# index e17d9e2..e826e7b 100644
# --- a/_build/default/test/rewriter/errors/pat_too_many_record_field.expected
# +++ b/_build/default/test/rewriter/errors/pat_too_many_record_field.actual
# @@ -1,4 +1,2 @@
#  File "pat_too_many_record_field.ml", line 2, characters 14-49:
# -2 |   | [%yojson? {a = 1; b = 2; c = 3; d = 4; e = 5}] -> false
# -                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#  Error: ppx_yojson: record patterns with more than 4 fields aren't supported. Consider using ppx_deriving_yojson to handle more complex json objects.
# File "test/rewriter/errors/pat_unsupported_payload.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/pat_unsupported_payload.expected _build/default/test/rewriter/errors/pat_unsupported_payload.actual
# diff --git a/_build/default/test/rewriter/errors/pat_unsupported_payload.expected b/_build/default/test/rewriter/errors/pat_unsupported_payload.actual
# index 83fdd52..4cdb066 100644
# --- a/_build/default/test/rewriter/errors/pat_unsupported_payload.expected
# +++ b/_build/default/test/rewriter/errors/pat_unsupported_payload.actual
# @@ -1,4 +1,2 @@
#  File "pat_unsupported_payload.ml", line 2, characters 14-19:
# -2 |   | [%yojson? Ok ()] -> false
# -                  ^^^^^
#  Error: ppx_yojson: unsupported payload
# File "test/rewriter/errors/pat_unsupported_record_field.expected", line 1, characters 0-0:
# /usr/bin/git --no-pager diff --no-index --color=always -u _build/default/test/rewriter/errors/pat_unsupported_record_field.expected _build/default/test/rewriter/errors/pat_unsupported_record_field.actual
# diff --git a/_build/default/test/rewriter/errors/pat_unsupported_record_field.expected b/_build/default/test/rewriter/errors/pat_unsupported_record_field.actual
# index 6ef63b6..b947acd 100644
# --- a/_build/default/test/rewriter/errors/pat_unsupported_record_field.expected
# +++ b/_build/default/test/rewriter/errors/pat_unsupported_record_field.actual
# @@ -1,4 +1,2 @@
#  File "pat_unsupported_record_field.ml", line 2, characters 15-22:
# -2 |   | [%yojson? {A.field = 0}] -> false
# -                   ^^^^^^^
#  Error: ppx_yojson: unsupported record field
# (cd _build/default/test/lib && ./test_ppx_yojson_lib.exe -e)
# Testing `ppx_yojson_lib'.
# This run has ID `9ABEOHI9'.
# 
#   [OK]          Integer_const          0   is_binary: "0b1".
#   [OK]          Integer_const          1   is_binary: "0B1".
#   [OK]          Integer_const          2   is_binary: "123".
#   [OK]          Integer_const          3   is_octal: "0o1".
#   [OK]          Integer_const          4   is_octal: "0O1".
#   [OK]          Integer_const          5   is_octal: "123".
#   [OK]          Integer_const          6   is_hexadecimal: "0x1".
#   [OK]          Integer_const          7   is_hexadecimal: "0X1".
#   [OK]          Integer_const          8   is_hexadecimal: "123".
#   [OK]          Utils                  0   remove: Empty.
#   [OK]          Utils                  1   remove: First.
#   [OK]          Utils                  2   remove: Last.
#   [OK]          Utils                  3   remove: Some.
#   [OK]          Utils                  4   permutations: Empty.
#   [OK]          Utils                  5   permutations: One.
#   [OK]          Utils                  6   permutations: Two.
#   [OK]          Utils                  7   permutations: Three.
# 
# Full test results in `~/.opam/5.2/.opam-switch/build/ppx_yojson.1.3.0/_build/default/test/lib/_build/_tests/ppx_yojson_lib'.
# Test Successful in 0.001s. 17 tests run.

I'm not sure if it's a ppxlib issue or a ppx_yojson issue but given you're currently maintaining both I trust you'll know where to move this if this needs to be moved.

Empty objects disallowed

This usage:

[%yojson {}]

yields Error: Syntax error. Likewise for any empty object within a %yojson block. Adding any member to the object yields an `Assoc value as expected.

Support for field renaming to workaround eg. OCaml keywords.

AFAICT, there is currently no way to create a JSON object with a field name that collides with an OCaml keyword - for instance one cannot have a type key, since this is not valid OCaml:

let _ = [%ezjsonm { type = "whatever" }]

Two ideas come to mind, which I'm happy to implement myself if I get a greenlight. Other ideas welcome, of course.

Automatic field renaming

Automatically trim underscores at the start of identifiers, so one can write

let _ = [%ezjsonm { _type = "whatever" }]

Js_of_ocaml does this, for instance. Trimming the underscore at the beginning instead of the end unlocks capitalized fields, which are not valid record field names in OCaml.

Pros: Fast and easy.
Cons: Harder to have JSON field names that actually end with underscores (although those are rather rare), not backward compatible.

An additional idea would be to do this iif the word before _ actually is a keyword.

Manual field renaming with an annotation

For instance:

let _ = [%ezjsonm { type_ = "whatever" [@as "type"]  }];

Pros: Backward compatible, handles every case.
Cons: Verbose.

Remove unnecessary metaquot extensions

ppx_yojson makes quite an heavy use of metaquot as it simplifies the code quite a lot.

That's possible thanks to metaquot "anti-quotation" which allows injecting ast fragments into a metaquot payload.

When browsing through the source recently I stumbled to a few occurences of useless metaquot extensions where the payload simply was an antiquoted expression building the appropriate ast fragment, i.e. something of the form:

[%expr [%e Ast_builder.Default.estring ~loc some_string]]

which can be simplified to

Ast_builder.Default.estring ~loc some_string

It would be nice to get rid of those.

Handle int64 and IntLit

let json = [%yojson 1]

is rewritten as

let json = `Int 1

which is fine but in some case we would like to be able to have it rewritten to:

let json = `Intlit "1"

because when using ppx_deriving_yojson for instance, some small int values will still be converted to Intlit, as would [%to_yojson: int64] 1L.

A good first step is probably to accept such litterals and the payload and rewrite them as Intlit, ie:

let json = [%yojson 1L]

gets rewritten as the above example.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.