anol
anol

Reputation: 9073

Getting position information from JSON token in source

I'm using yojson to parse a JSON file in OCaml, and while I'm validating the input file, I'd like to emit error messages containing source code information for the user.

For instance, if I found that a given value at line 5492 is not in the allowed range, I'd like to tell the user: "Invalid value in line source.json:5492".

How can I get this information?

(If yojson cannot give this kind of information, another OCaml JSON library that does have this information might be useful.)

Upvotes: 3

Views: 156

Answers (1)

Pierre Boutillier
Pierre Boutillier

Reputation: 276

In between efficiency and interactivity, Yojson has chosen the first (More accurately, Yojson is really designed for data (de)serialisation, so computer generated JSON where error reporting makes no sense).

Its lever file: yojson/lib/read.mll starts by the exact comment:

(*
  We override Lexing.engine in order to avoid creating a new position
  record each time a rule is matched.
  This reduces total parsing time by about 31%.
*)

These positions are exactly what you need to report errors!

Jsonm has what you need:

val decoded_range : decoder -> (int * int) * (int * int)
decoded_range d is the range of characters spanning the last `Lexeme or `Error (or `White or `Comment for an Jsonm.Uncut.decode) decoded by d. A pair of line and column numbers respectively one and zero based.

but Jsonm is a library for experts.

Sadly, its wrapper Ezjsonm does nearly the good thing but fails at the last second and raise Ezjsonm.Parse_error with no range!

You can have a look at the source code as inspiration to do the right thing at https://github.com/mirage/ezjsonm/blob/master/lib/ezjsonm.ml : json_of_src does exactly what you need but then ... for unknown reason ... from_src defined as

let from_src src =
  match json_of_src src with
  | `JSON t      -> t
  | `Error (_,e) -> parse_error `Null "JSON.of_buffer %s" (string_of_error e)

let from_string str = from_src (`String str)

drops the range!

Upvotes: 5

Related Questions