123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743(** {3 Type of the JSON tree} *)typet=[|`Null|`Boolofbool|`Intofint|`Intlitofstring|`Floatoffloat|`Floatlitofstring|`Stringofstring|`Stringlitofstring|`Assocof(string*t)list|`Listoftlist|`Tupleoftlist|`Variantof(string*toption)](**
All possible cases defined in Yojson:
- `Null: JSON null
- `Bool of bool: JSON boolean
- `Int of int: JSON number without decimal point or exponent.
- `Intlit of string: JSON number without decimal point or exponent,
preserved as a string.
- `Float of float: JSON number, Infinity, -Infinity or NaN.
- `Floatlit of string: JSON number, Infinity, -Infinity or NaN,
preserved as a string.
- `String of string: JSON string. Bytes in the range 128-255 are preserved
as-is without encoding validation for both reading
and writing.
- `Stringlit of string: JSON string literal including the double quotes.
- `Assoc of (string * json) list: JSON object.
- `List of json list: JSON array.
- `Tuple of json list: Tuple (non-standard extension of JSON).
Syntax: [("abc", 123)].
- `Variant of (string * json option): Variant (non-standard extension of JSON).
Syntax: [<"Foo">] or [<"Bar":123>].
*)(*
Note to adventurers: ocamldoc does not support inline comments
on each polymorphic variant, and cppo doesn't allow to concatenate
comments, so it would be complicated to document only the
cases that are preserved by cppo in the type definition.
*)lethexn=Char.chr(ifn<10thenn+48elsen+87)letwrite_specialsrcstartstopobstr=Buffer.add_substringobsrc!start(stop-!start);Buffer.add_stringobstr;start:=stop+1letwrite_control_charsrcstartstopobc=Buffer.add_substringobsrc!start(stop-!start);Buffer.add_stringob"\\u00";Buffer.add_charob(hex(Char.codeclsr4));Buffer.add_charob(hex(Char.codecland0xf));start:=stop+1letfinish_stringsrcstartob=tryBuffer.add_substringobsrc!start(String.lengthsrc-!start)withexc->Printf.eprintf"src=%S start=%i len=%i\n%!"src!start(String.lengthsrc-!start);raiseexcletwrite_string_bodyobs=letstart=ref0infori=0toString.lengths-1domatchs.[i]with'"'->write_specialsstartiob"\\\""|'\\'->write_specialsstartiob"\\\\"|'\b'->write_specialsstartiob"\\b"|'\012'->write_specialsstartiob"\\f"|'\n'->write_specialsstartiob"\\n"|'\r'->write_specialsstartiob"\\r"|'\t'->write_specialsstartiob"\\t"|'\x00'..'\x1F'|'\x7F'asc->write_control_charsstartiobc|_->()done;finish_stringsstartobletwrite_stringobs=Buffer.add_charob'"';write_string_bodyobs;Buffer.add_charob'"'letjson_string_of_strings=letob=Buffer.create10inwrite_stringobs;Buffer.contentsobletwrite_nullob()=Buffer.add_stringob"null"letwrite_boolobx=Buffer.add_stringob(ifxthen"true"else"false")letdecn=Char.chr(n+48)letrecwrite_digitssx=ifx=0then()elseletd=xmod10inwrite_digitss(x/10);Buffer.add_chars(dec(absd))letwrite_intobx=ifx>0thenwrite_digitsobxelseifx<0then(Buffer.add_charob'-';write_digitsobx)elseBuffer.add_charob'0'letjson_string_of_inti=string_of_inti(*
Ensure that the float is not printed as an int.
This is not required by JSON, but useful in order to guarantee
reversibility.
*)letfloat_needs_periods=tryfori=0toString.lengths-1domatchs.[i]with'0'..'9'|'-'->()|_->raiseExitdone;truewithExit->false(*
Guarantees that a sufficient number of digits are printed in order to allow
reversibility.
*)letwrite_floatobx=matchclassify_floatxwithFP_nan->Buffer.add_stringob"NaN"|FP_infinite->Buffer.add_stringob(ifx>0.then"Infinity"else"-Infinity")|_->lets1=Printf.sprintf"%.16g"xinlets=iffloat_of_strings1=xthens1elsePrintf.sprintf"%.17g"xinBuffer.add_stringobs;iffloat_needs_periodsthenBuffer.add_stringob".0"letwrite_normal_float_precsignificant_figuresobx=letsprintf=Printf.sprintfinlets=matchsignificant_figureswith1->sprintf"%.1g"x|2->sprintf"%.2g"x|3->sprintf"%.3g"x|4->sprintf"%.4g"x|5->sprintf"%.5g"x|6->sprintf"%.6g"x|7->sprintf"%.7g"x|8->sprintf"%.8g"x|9->sprintf"%.9g"x|10->sprintf"%.10g"x|11->sprintf"%.11g"x|12->sprintf"%.12g"x|13->sprintf"%.13g"x|14->sprintf"%.14g"x|15->sprintf"%.15g"x|16->sprintf"%.16g"x|_->sprintf"%.17g"xinBuffer.add_stringobs;iffloat_needs_periodsthenBuffer.add_stringob".0"(* used by atdgen *)letwrite_float_precsignificant_figuresobx=matchclassify_floatxwithFP_nan->Buffer.add_stringob"NaN"|FP_infinite->Buffer.add_stringob(ifx>0.then"Infinity"else"-Infinity")|_->write_normal_float_precsignificant_figuresobxletjson_string_of_floatx=letob=Buffer.create20inwrite_floatobx;Buffer.contentsobletwrite_std_floatobx=matchclassify_floatxwithFP_nan->Common.json_error"NaN value not allowed in standard JSON"|FP_infinite->Common.json_error(ifx>0.then"Infinity value not allowed in standard JSON"else"-Infinity value not allowed in standard JSON")|_->lets1=Printf.sprintf"%.16g"xinlets=iffloat_of_strings1=xthens1elsePrintf.sprintf"%.17g"xinBuffer.add_stringobs;iffloat_needs_periodsthenBuffer.add_stringob".0"(* used by atdgen *)letwrite_std_float_precsignificant_figuresobx=matchclassify_floatxwithFP_nan->Common.json_error"NaN value not allowed in standard JSON"|FP_infinite->Common.json_error(ifx>0.then"Infinity value not allowed in standard JSON"else"-Infinity value not allowed in standard JSON")|_->write_normal_float_precsignificant_figuresobxletstd_json_string_of_floatx=letob=Buffer.create20inwrite_std_floatobx;Buffer.contentsobletwrite_intlit=Buffer.add_stringletwrite_floatlit=Buffer.add_stringletwrite_stringlit=Buffer.add_stringletreciter2_auxf_eltf_sepx=function[]->()|y::l->f_sepx;f_eltxy;iter2_auxf_eltf_sepxlletiter2f_eltf_sepx=function[]->()|y::l->f_eltxy;iter2_auxf_eltf_sepxlletf_sepob=Buffer.add_charob','letrecwrite_jsonob(x:t)=matchxwith`Null->write_nullob()|`Boolb->write_boolobb|`Inti->write_intobi|`Intlits->Buffer.add_stringobs|`Floatf->write_floatobf|`Floatlits->Buffer.add_stringobs|`Strings->write_stringobs|`Stringlits->Buffer.add_stringobs|`Assocl->write_assocobl|`Listl->write_listobl|`Tuplel->write_tupleobl|`Variant(s,o)->write_variantobsoandwrite_assocobl=letf_eltob(s,x)=write_stringobs;Buffer.add_charob':';write_jsonobxinBuffer.add_charob'{';iter2f_eltf_sepobl;Buffer.add_charob'}';andwrite_listobl=Buffer.add_charob'[';iter2write_jsonf_sepobl;Buffer.add_charob']'andwrite_tupleobl=Buffer.add_charob'(';iter2write_jsonf_sepobl;Buffer.add_charob')'andwrite_variantobso=Buffer.add_charob'<';write_stringobs;(matchowithNone->()|Somex->Buffer.add_charob':';write_jsonobx);Buffer.add_charob'>'letwrite_t=write_jsonletrecwrite_std_jsonob(x:t)=matchxwith`Null->write_nullob()|`Boolb->write_boolobb|`Inti->write_intobi|`Intlits->Buffer.add_stringobs|`Floatf->write_std_floatobf|`Floatlits->Buffer.add_stringobs|`Strings->write_stringobs|`Stringlits->Buffer.add_stringobs|`Assocl->write_std_assocobl|`Listl->write_std_listobl|`Tuplel->write_std_tupleobl|`Variant(s,o)->write_std_variantobsoandwrite_std_assocobl=letf_eltob(s,x)=write_stringobs;Buffer.add_charob':';write_std_jsonobxinBuffer.add_charob'{';iter2f_eltf_sepobl;Buffer.add_charob'}';andwrite_std_listobl=Buffer.add_charob'[';iter2write_std_jsonf_sepobl;Buffer.add_charob']'andwrite_std_tupleobl=Buffer.add_charob'[';iter2write_std_jsonf_sepobl;Buffer.add_charob']'andwrite_std_variantobso=matchowithNone->write_stringobs|Somex->Buffer.add_charob'[';write_stringobs;Buffer.add_charob',';write_std_jsonobx;Buffer.add_charob']'letto_buffer?(suf="")?(std=false)obx=ifstdthenwrite_std_jsonobxelsewrite_jsonobx;Buffer.add_stringobsufletto_string?buf?(len=256)?(suf="")?stdx=letob=matchbufwithNone->Buffer.createlen|Someob->Buffer.clearob;obinto_buffer~suf?stdobx;lets=Buffer.contentsobinBuffer.clearob;sletto_channel?buf?(len=4096)?(suf="")?stdocx=letob=matchbufwithNone->Buffer.createlen|Someob->Buffer.clearob;obinto_buffer~suf?stdobx;Buffer.output_bufferocob;Buffer.clearobletto_output?buf?(len=4096)?(suf="")?stdoutx=letob=matchbufwithNone->Buffer.createlen|Someob->Buffer.clearob;obinto_buffer~suf?stdobx;(* this requires an int and never uses it. This is done to preserve
backward compatibility to not break the signatur but can safely
be changed to require unit in a future compatibility-breaking
release *)let_:int=out#output(Buffer.contentsob)0(Buffer.lengthob)inBuffer.clearobletto_file?len?std?(suf="\n")filex=letoc=open_outfileintryto_channel?len~suf?stdocx;close_outocwithe->close_out_noerroc;raiseeletseq_to_buffer?(suf="\n")?stdobst=Seq.iter(to_buffer~suf?stdob)stletseq_to_string?buf?(len=256)?(suf="\n")?stdst=letob=matchbufwithNone->Buffer.createlen|Someob->Buffer.clearob;obinseq_to_buffer~suf?stdobst;lets=Buffer.contentsobinBuffer.clearob;sletseq_to_channel?buf?(len=2096)?(suf="\n")?stdocseq=letob=matchbufwithNone->Buffer.createlen|Someob->Buffer.clearob;obinSeq.iter(funjson->to_buffer~suf?stdobjson;Buffer.output_bufferocob;Buffer.clearob;)seqletseq_to_file?len?(suf="\n")?stdfilest=letoc=open_outfileintryseq_to_channel?len~suf?stdocst;close_outocwithe->close_out_noerroc;raiseeletrecsort=function|`Assocl->letl=List.rev(List.rev_map(fun(k,v)->(k,sortv))l)in`Assoc(List.stable_sort(fun(a,_)(b,_)->String.compareab)l)|`Listl->`List(List.rev(List.rev_mapsortl))|`Tuplel->`Tuple(List.rev(List.rev_mapsortl))|`Variant(k,Somev)asx->letv'=sortvinifv==v'thenxelse`Variant(k,Somev')|x->xletrecppfmt=function|`Null->Format.pp_print_stringfmt"`Null"|`Boolx->Format.fprintffmt"`Bool (@[<hov>";Format.fprintffmt"%B"x;Format.fprintffmt"@])"|`Intx->Format.fprintffmt"`Int (@[<hov>";Format.fprintffmt"%d"x;Format.fprintffmt"@])"|`Intlitx->Format.fprintffmt"`Intlit (@[<hov>";Format.fprintffmt"%S"x;Format.fprintffmt"@])"|`Floatx->Format.fprintffmt"`Float (@[<hov>";Format.fprintffmt"%F"x;Format.fprintffmt"@])"|`Floatlitx->Format.fprintffmt"`Floatlit (@[<hov>";Format.fprintffmt"%S"x;Format.fprintffmt"@])"|`Stringx->Format.fprintffmt"`String (@[<hov>";Format.fprintffmt"%S"x;Format.fprintffmt"@])"|`Stringlitx->Format.fprintffmt"`Stringlit (@[<hov>";Format.fprintffmt"%S"x;Format.fprintffmt"@])"|`Assocxs->Format.fprintffmt"`Assoc (@[<hov>";Format.fprintffmt"@[<2>[";ignore(List.fold_left(funsep(key,value)->ifsepthenFormat.fprintffmt";@ ";Format.fprintffmt"(@[";Format.fprintffmt"%S"key;Format.fprintffmt",@ ";ppfmtvalue;Format.fprintffmt"@])";true)falsexs);Format.fprintffmt"@,]@]";Format.fprintffmt"@])"|`Listxs->Format.fprintffmt"`List (@[<hov>";Format.fprintffmt"@[<2>[";ignore(List.fold_left(funsepx->ifsepthenFormat.fprintffmt";@ ";ppfmtx;true)falsexs);Format.fprintffmt"@,]@]";Format.fprintffmt"@])"|`Tupletup->Format.fprintffmt"`Tuple (@[<hov>";Format.fprintffmt"@[<2>[";ignore(List.fold_left(funsepe->ifsepthenFormat.fprintffmt";@ ";ppfmte;true)falsetup);Format.fprintffmt"@,]@]";Format.fprintffmt"@])"|`Variant(name,value)->Format.fprintffmt"`Variant (@[<hov>";Format.fprintffmt"(@[";Format.fprintffmt"%S"name;Format.fprintffmt",@ ";(matchvaluewith|None->Format.pp_print_stringfmt"None"|Somex->Format.pp_print_stringfmt"(Some ";ppfmtx;Format.pp_print_stringfmt")");Format.fprintffmt"@])";Format.fprintffmt"@])"letshowx=Format.asprintf"%a"ppxletrecequalab=matcha,bwith|`Null,`Null->true|`Boola,`Boolb->a=b|`Inta,`Intb->a=b|`Intlita,`Intlitb->a=b|`Floata,`Floatb->a=b|`Floatlita,`Floatlitb->a=b|`Stringa,`Stringb->a=b|`Stringlita,`Stringlitb->a=b|`Assocxs,`Assocys->letcompare_keys=fun(key,_)(key',_)->String.comparekeykey'inletxs=List.stable_sortcompare_keysxsinletys=List.stable_sortcompare_keysysin(matchList.for_all2(fun(key,value)(key',value')->matchkey=key'with|false->false|true->equalvaluevalue')xsyswith|result->result|exceptionInvalid_argument_->(* the lists were of different lengths, thus unequal *)false)|`Tuplexs,`Tupleys|`Listxs,`Listys->(matchList.for_all2equalxsyswith|result->result|exceptionInvalid_argument_->(* the lists were of different lengths, thus unequal *)false)|`Variant(name,value),`Variant(name',value')->(matchname=name'with|false->false|true->matchvalue,value'with|None,None->true|Somex,Somey->equalxy|_->false)|_->falsemodulePretty=struct(*
Pretty-print JSON data in an attempt to maximize readability.
1. What fits on one line stays on one line.
2. What doesn't fit on one line gets printed more vertically so as to not
exceed a reasonable page width, if possible.
Arrays containing only simple elements ("atoms") are pretty-printed with
end-of-line wrapping like ordinary text:
[
"hello", "hello", "hello", "hello", "hello", "hello", "hello", "hello",
"hello", "hello", "hello", "hello", "hello", "hello", "hello", "hello"
]
Other arrays are printed either horizontally or vertically depending
on whether they fit on a single line:
[ { "hello": "world" }, { "hello": "world" }, { "hello": "world" } ]
or
[
{ "hello": "world" },
{ "hello": "world" },
{ "hello": "world" },
{ "hello": "world" }
]
*)letpp_listsepppxoutl=letpp_sepout()=Format.fprintfout"%s@ "sepinFormat.pp_print_list~pp_sepppxoutlletis_atom(x:[>t])=matchxwith|`Null|`Bool_|`Int_|`Float_|`String_|`Intlit_|`Floatlit_|`Stringlit_|`List[]|`Assoc[]|`Tuple[]|`Variant(_,None)->true|`List_|`Assoc_|`Tuple_|`Variant(_,Some_)->falseletis_atom_listl=List.for_allis_atoml(*
inside_box: indicates that we're already within a box that imposes
a certain style and we shouldn't create a new one. This is used for
printing field values like this:
foo: [
bar
]
rather than something else like
foo:
[
bar
]
*)letrecformat~inside_boxstd(out:Format.formatter)(x:t):unit=matchxwith|`Null->Format.pp_print_stringout"null"|`Boolx->Format.pp_print_booloutx|`Intx->Format.pp_print_stringout(json_string_of_intx)|`Floatx->lets=ifstdthenstd_json_string_of_floatxelsejson_string_of_floatxinFormat.pp_print_stringouts|`Strings->Format.pp_print_stringout(json_string_of_strings)|`Intlits->Format.pp_print_stringouts|`Floatlits->Format.pp_print_stringouts|`Stringlits->Format.pp_print_stringouts|`List[]->Format.pp_print_stringout"[]"|`Listl->ifnotinside_boxthenFormat.fprintfout"@[<hv2>";ifis_atom_listlthen(* use line wrapping like we would do for a paragraph of text *)Format.fprintfout"[@;<1 0>@[<hov>%a@]@;<1 -2>]"(pp_list","(format~inside_box:falsestd))lelse(* print the elements horizontally if they fit on the line,
otherwise print them in a column *)Format.fprintfout"[@;<1 0>@[<hv>%a@]@;<1 -2>]"(pp_list","(format~inside_box:falsestd))l;ifnotinside_boxthenFormat.fprintfout"@]";|`Assoc[]->Format.pp_print_stringout"{}"|`Assocl->ifnotinside_boxthenFormat.fprintfout"@[<hv2>";Format.fprintfout"{@;<1 0>%a@;<1 -2>}"(pp_list","(format_fieldstd))l;ifnotinside_boxthenFormat.fprintfout"@]";|`Tuplel->ifstdthenformat~inside_boxstdout(`Listl)elseifl=[]thenFormat.pp_print_stringout"()"else(ifnotinside_boxthenFormat.fprintfout"@[<hov2>";Format.fprintfout"(@,%a@;<0 -2>)"(pp_list","(format~inside_box:falsestd))l;ifnotinside_boxthenFormat.fprintfout"@]";)|`Variant(s,None)->ifstdthenletrepresentation=`Stringsinformat~inside_boxstdoutrepresentationelseFormat.fprintfout"<%s>"(json_string_of_strings)|`Variant(s,Somex)->ifstdthenletrepresentation=`Stringsinformat~inside_boxstdout(`List[representation;x])elseletop=json_string_of_stringsinFormat.fprintfout"<@[<hv2>%s: %a@]>"op(format~inside_box:truestd)xandformat_fieldstdout(name,x)=Format.fprintfout"@[<hv2>%s: %a@]"(json_string_of_stringname)(format~inside_box:truestd)xletpp?(std=false)outx=Format.fprintfout"@[<hv2>%a@]"(format~inside_box:truestd)(x:>t)letto_string?stdx=Format.asprintf"%a"(pp?std)xletto_channel?stdocx=letfmt=Format.formatter_of_out_channelocinFormat.fprintffmt"%a@?"(pp?std)xendletpretty_print?stdoutx=Pretty.pp?stdoutxletpretty_to_string?stdx=Pretty.to_string?stdxletpretty_to_channel?stdocx=Pretty.to_channel?stdocx