OCaml中如何获取JSON Token位置信息以生成精准错误提示?
获取JSON解析时的源码位置(OCaml)
首先说下你用的yojson库:默认情况下,yojson解析后的AST是不携带源码位置信息的,不过如果只是处理解析阶段的语法错误,你可以手动调用它的底层Lexer和Parser模块来捕获位置:
- 你需要手动构建解析流程,给lexbuf设置文件名,这样出错时就能拿到包含文件名、行号、列号的位置信息。比如这段示例代码:
open Yojson.Safe let parse_with_error_pos filename = let ic = open_in filename in let lexbuf = Lexing.from_channel ic in Lexing.set_filename lexbuf filename; (* 让位置信息带上文件名 *) try let json = Parser.json Lexer.token lexbuf in close_in ic; Ok json with | Lexer.Error msg -> let pos = lexbuf.Lexing.lex_curr_p in close_in ic; Error (Printf.sprintf "Lex error at %s:%d:%d: %s" pos.pos_fname pos.pos_lnum (pos.pos_cnum - pos.pos_bol + 1) msg) | Parser.Error -> let pos = lexbuf.Lexing.lex_curr_p in close_in ic; Error (Printf.sprintf "Parse error at %s:%d:%d" pos.pos_fname pos.pos_lnum (pos.pos_cnum - pos.pos_bol + 1))
但如果是自定义验证逻辑(比如检查某个值是否在允许范围内),yojson的AST本身不记录每个节点的位置,这时候用yojson就会很麻烦——你没法知道哪个节点对应源码里的哪一行。
这时候推荐几个支持位置追踪的OCaml JSON库:
1. yojson-located
这是yojson的官方扩展,它会在解析后的AST节点中嵌入位置信息。安装后,你可以用Yojson_located.Safe.from_file来解析文件,得到的每个JSON节点都带有loc字段,包含文件名、起始/结束行号、列号等信息。验证的时候,直接读取这个字段就能输出精确的错误提示,完美适配你原来的yojson使用习惯。
2. Jsonm
这是一个流式、事件驱动的JSON解析库,它在解析每个JSON元素(比如对象、数组、字符串、数字)时,都能实时获取当前的源码位置。你可以在遍历元素的同时做验证,一旦发现非法值,立刻输出对应的位置信息。示例代码大概是这样:
open Jsonm let validate_with_pos filename = let ic = open_in filename in let src = `Channel ic in let dec = decoder src in let rec process_elements () = match decode dec with | `Lexeme lexeme -> let pos = decoder_pos dec in (* 这里写你的验证逻辑,比如检查整数范围 *) (match lexeme with | `Int n when n < 0 || n > 100 -> Printf.printf "Invalid value in %s:%d:%d: integer %d is out of allowed range\n" filename pos.pos_lnum (pos.pos_cnum - pos.pos_bol + 1) n | _ -> ()); process_elements () | `End -> close_in ic; Ok () | `Error err -> let pos = decoder_pos dec in close_in ic; Error (Printf.sprintf "Parsing error at %s:%d:%d: %s" filename pos.pos_lnum (pos.pos_cnum - pos.pos_bol + 1) (error_message err)) in process_elements ()
3. Json-wheel
这个库也支持在AST中保存位置信息,它的Json.t类型每个节点都包含位置数据,解析时默认就会追踪这些信息,验证时直接提取即可。
如果只是想在yojson的基础上扩展,优先选yojson-located,不需要改太多代码;如果需要更灵活的流式处理,Jsonm是不错的选择。
内容的提问来源于stack exchange,提问作者anol
相关产品推荐
相关产品推荐

