123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882(*
Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2017 Anton Lavrik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*)(*
* Protobuf - JSON - XML - Piq data conversion
*)moduleC=Piqi_commonopenCexceptionEOF(* internal representation of an object being convert *)typeobj=|Piqtypeofstring|Typed_piqobjofPiqobj.obj|PiqobjofPiqobj.obj|PiqiofT.piqi(* the list of piqi objects loaded but not processed yet *)letunprocessed_piqi_list=ref[]letprocess_unprocessed_piqi()=List.iter(funmodname->letpiqi=Piqi_db.find_piqimodnamein(* process piqi if it hasn't been fully processed yet by this point *)ignore(Piqi.process_piqipiqi~cache:false);(* already cached *)Piqloc.preserve())(List.rev!unprocessed_piqi_list);(* reset the unprocessed piqi list *)unprocessed_piqi_list:=[]letpre_process_piqi~fname?astpiqi=piqi.P.is_embedded<-Sometrue;(* can't process it right away, because not all dependencies could be loaded
* already; this is especially ciritical in case of mutually-recursive
* includes; just do bare minimum so that we could add to Piqi_db and process
* it later *)letpiqi=Piqi.pre_process_piqipiqi~fname?astin(* so that we could find it by name later *)Piqi_db.add_piqipiqi;(* preserve location information so that existing location info for Piqi
* modules won't be discarded by subsequent Piqloc.reset() calls *)Piqloc.preserve();(* add to the list of unprocessed modules *)letmodname=some_ofpiqi.P.modnameinunprocessed_piqi_list:=modname::!unprocessed_piqi_list;piqiletdefault_piqtype=refNoneletcheck_piqtypen=ifnot(Piqi_name.is_valid_typenamen)thenerrorn("invalid type name: "^U.quoten)else()letfind_piqtype?(check=false)typename=ifcheckthencheck_piqtypetypename;(* in case typename is defined in one of the yet unprocessed piqi modules *)process_unprocessed_piqi();tryPiqi_db.find_piqtypetypenamewithNot_found->errortypename("unknown type: "^typename)letprocess_default_piqtype?checktypename=letpiqtype=find_piqtype?checktypenamein(* NOTE: silently overriding previous value *)default_piqtype:=Somepiqtype(* default piqtype taken from the stream overrides the user-specified
* one *)letget_current_piqtypeuser_piqtypelocref=match!default_piqtype,user_piqtypewith|Somex,_->x|None,Somex->x|None,None->errorlocref"type of object is unknown"letread_piq_ast~skip_trailing_commapiq_parseruser_piqtype:piq_ast=ifnot!Piqi_config.piq_frameless_input(* regular (framed) mode *)thenmatchPiq_parser.read_nextpiq_parser~skip_trailing_commawith|Someast->ast|None->raiseEOFelse(* frameless mode *)letis_scalar_typeast=matchastwith|`typename_|`typed_->true|_->letpiqtype=get_current_piqtypeuser_piqtypeastinnot(C.is_container_typepiqtype)inletast_list=Piq_parser.read_allpiq_parserin(* if there's more that one element or we're parsing value for a container
* type, wrap elements into a list *)matchast_listwith|[ast]whenis_scalar_typeast->ast|[]->letast=`list[]inletfname,_=piq_parserinletloc=(fname,0,0)inPiqloc.addlocretlocast|h::_->(* setting list location based on the location of the first element *)letast=`listast_listinPiqloc.addrefrethastletpiqi_of_piqfnameast=letpiqi=Piqi.parse_piqiastinpre_process_piqipiqi~fname~astletload_piq_from_ast(user_piqtype:T.piqtypeoption)ast:obj=letfname=""inmatchastwith|`typenametypename->(* (:typename) *)process_default_piqtypetypename;Piqtypetypename|`typed{Piq_ast.Typed.typename="piqi";Piq_ast.Typed.value=((`list_)asast)}->(* :piqi <piqi-lang> *)letpiqi=piqi_of_piqfnameastinPiqipiqi|`typed{Piq_ast.Typed.typename="piqi"}->errorast"invalid piqi specification"|`typed_->(* :typename ... *)(* in case typename is defined in one of the yet unprocessed piqi
* modules: *)process_unprocessed_piqi();letobj=Piqobj_of_piq.parse_typed_objastinTyped_piqobjobj|_->letpiqtype=get_current_piqtypeuser_piqtypeastinifpiqtype==!Piqi.piqi_lang_def(* XXX *)thenletpiqi=piqi_of_piqfnameastinPiqipiqielseletobj=Piqobj_of_piq.parse_objpiqtypeastinPiqobjobjletload_piq(user_piqtype:T.piqtypeoption)?(skip_trailing_comma=false)piq_parser:obj=letast=read_piq_astpiq_parseruser_piqtype~skip_trailing_commainletast=Piq_parser.expandastinload_piq_from_astuser_piqtypeastletoriginal_piqipiqi=letorig_piqi=some_ofpiqi.P.original_piqiin(* make sure that the module's name is set *)P.({orig_piqiwithmodname=piqi.P.modname})letpiqi_ast_to_piqpiqi_ast=letpiqi_ast=Piqi_pp.prettify_piqi_astpiqi_astin`typed{Piq_ast.Typed.typename="piqi";Piq_ast.Typed.value=piqi_ast;}letpiqi_to_piqpiqi=letpiqi_ast=Piqi.piqi_to_ast(original_piqipiqi)inpiqi_ast_to_piqpiqi_astletgen_piq?(preserve_loc=false)(obj:obj)=ifnotpreserve_locthenPiqloc.pause();(* no need to preserve location information here *)letf()=matchobjwith|Piqtypetypename->`typenametypename|Piqipiqi->piqi_to_piqpiqi|Typed_piqobjobj->Piqobj_to_piq.gen_typed_objobj|Piqobjobj->Piqobj_to_piq.gen_objobjinletres=U.with_boolPiqobj_to_piq.is_external_modetruefinifnotpreserve_locthenPiqloc.resume();res(* handling of piq_frameless_output *)letwrite_piq_astwriterast=matchastwith|`typed{Piq_ast.Typed.value=`listl}|`listlwhen!Piqi_config.piq_frameless_output->List.iterwriterl|_->writerastletto_piq_channelch(obj:obj)=letast=gen_piqobjinwrite_piq_ast(Piq_gen.to_channelch)ast;Pervasives.output_charch'\n'letto_piq_stringobj=letast=gen_piqobjinletbuf=Buffer.create256inwrite_piq_ast(Piq_gen.to_bufferbuf)ast;Buffer.contentsbufletread_pib_fieldbuf=(* TODO: handle runtime pib read errors *)matchPiqirun.parse_fieldbufwith|Somex->x|None->raiseEOFletpiqtypes=ref[]letadd_piqtypecodepiqtype=ifcode=1(* default piqtype *)then(* NOTE: silently overriding previous value *)default_piqtype:=Somepiqtypeelsepiqtypes:=(code,piqtype)::!piqtypesletfind_piqtype_by_codecode=trylet(_,piqtype)=List.find(function(code',_)whencode=code'->true|_->false)!piqtypesinpiqtypewithNot_found->(* TODO: add stream position info *)piqi_error("invalid field code when reading .pib: "^string_of_intcode)letpiqobj_of_protobufpiqtypebuf=(* don't store location references as we're loading from the binary object *)Piqloc.pause();letobj=Piqobj_of_protobuf.parse_objpiqtypebufinPiqloc.resume();objletpiqobj_to_protobufcodepiqobj=(* don't produce location references as don't care about it in general when
* generating data *)Piqloc.pause();(* force external mode during the conversion so that all piqi-any values are
* generated in external format *)letres=U.with_boolPiqobj_to_protobuf.is_external_modetrue(fun()->Piqobj_to_protobuf.gen_objcodepiqobj)inPiqloc.resume();resletprocess_pib_piqtypecodetypename=letpiqtype=iftypename="piqi"then!Piqi.piqi_lang_def(* return Piqi type from embedded self-definition *)elsefind_piqtypetypenameinadd_piqtypecodepiqtypeletpiqi_of_pibprotobuf=letpiqi=Piqi.piqi_of_pbprotobuf~process:falseinpre_process_piqipiqi~fname:"input"(* TODO, XXX: get the actual file name *)(* using max Protobuf wire code value for pib-typehint
*
* XXX: alternatively, we could use 0 or another value outside of the valid
* code range *)letpib_typehint_code=(1lsl29)-1letrecload_pib(user_piqtype:T.piqtypeoption)buf:obj=letfield_code,field_obj=read_pib_fieldbufiniffield_code=pib_typehint_code(* is this a typehint entry? *)then((* parse and process pib_typehint entry *)letopenT.Pib_typehintin(Piqloc.pause();lettypehint=T.parse_pib_typehintfield_objinPiqloc.resume();iftypehint.piqi_type="piqi-type"(* is this a valid piq typehint? *)then(process_pib_piqtypetypehint.codetypehint.typename;iftypehint.typename="piqi"thenload_pibuser_piqtypebufelsePiqtypetypehint.typename)else(* skipping invalid typehint entry; XXX: generate a warning? *)load_pibuser_piqtypebuf))else((* process a regular data entry *)letpiqtype=iffield_code=1then(* process a regular data entry for which a user-supplied type can be
* applied *)tryget_current_piqtypeuser_piqtype`fakewith_->(* TODO: add stream position info *)piqi_error"default type for pib object is unknown"else(* process a regular explicitly typed data entry *)find_piqtype_by_codefield_codeinifpiqtype==!Piqi.piqi_lang_def(* embedded Piqi spec *)thenletpiqi=piqi_of_pibfield_objinPiqipiqielseletobj=piqobj_of_protobufpiqtypefield_objiniffield_code=1thenPiqobjobjelseTyped_piqobjobj)letout_piqtypes=ref[]letnext_out_code=ref2letgen_pib_typehintcodetypename=letx=T.Pib_typehint.({piqi_type="piqi-type";typename=typename;code=code;})inPiqloc.pause();letres=T.gen__pib_typehintpib_typehint_codexinPiqloc.resume();resletfind_add_pib_typehintname=trylet(_,code)=List.find(function(name',_)whenname=name'->true|_->false)!out_piqtypesinNone,codewithNot_found->letcode=!next_out_codeinincrnext_out_code;out_piqtypes:=(name,code)::!out_piqtypes;lettypehint=gen_pib_typehintcodenameinSometypehint,codeletgen_pib(obj:obj)=letpib_typehint,data=matchobjwith|Piqipiqi->letpib_typehint,code=find_add_pib_typehint"piqi"inletdata=Piqi.piqi_to_pbpiqi~codeinpib_typehint,data|Piqtypetypename->letdata=gen_pib_typehint1typenameinNone,data|Piqobjobj->letdata=piqobj_to_protobuf1objinNone,data|Typed_piqobjobj->lettypename=Piqobj_common.full_typenameobjinletpib_typehint,code=find_add_pib_typehinttypenameinletdata=piqobj_to_protobufcodeobjinpib_typehint,datainmatchpib_typehintwith|None->data|Somex->(* add the pib_typehint entry before the data *)Piqirun.OBuf.iol[x;data]letto_pib_channelch(obj:obj)=letdata=gen_pibobjinPiqirun.to_channelchdataletto_pib_stringobj=letbuf=gen_pibobjinPiqirun.to_stringbufletload_pb(piqtype:T.piqtype)protobuf:obj=(* TODO: handle runtime protobuf read errors *)ifpiqtype==!Piqi.piqi_lang_def(* XXX *)thenletpiqi=Piqi.piqi_of_pbprotobufinPiqipiqielseletobj=piqobj_of_protobufpiqtypeprotobufinTyped_piqobjobjletgen_pb(obj:obj)=matchobjwith|Piqipiqi->Piqi.piqi_to_pbpiqi|Typed_piqobjobj|Piqobjobj->(* -1 is a special code meaning that key and length for blocks should
* not be generated. The resulting code is the same as generated by
* Piqi_to_wire.gen_binobj, but this way it is returned as an output
* buffer instead of a string in order to avoid extra memory copying *)piqobj_to_protobuf(-1)obj|Piqtype_->(* ignore default type names *)Piqirun.OBuf.iol[](* == empty output *)letto_pb_channelch(obj:obj)=letbuf=gen_pbobjinPiqirun.to_channelchbufletto_pb_stringobj=letbuf=gen_pbobjinPiqirun.to_stringbuf(*
* JSON reading and writing
*)letpiqobj_of_jsonpiqtypejson:Piqobj.obj=Piqobj_of_json.parse_objpiqtypejsonletpiqi_of_json?(process=true)json=letpiqtype=!Piqi.piqi_spec_defin(* don't resolve defaults when reading Json *)letpiqobj=C.with_resolve_defaultsfalse(fun()->Piqobj_of_json.parse_objpiqtypejson)in(* don't try to track location references as we don't preserve them yet in
* piqobj_of_json *)Piqloc.pause();letpiqi=Piqi.piqi_of_piqobjpiqobj~processinletpiqi=ifprocessthenpiqielsepre_process_piqipiqi~fname:"input"(* TODO, XXX: get the actual file name *)inPiqloc.resume();piqiletpiqi_to_jsonpiqi=letpiqobj=Piqi.piqi_to_piqobjpiqiinletjson=Piqobj_to_json.gen_objpiqobjin"piqi",jsonletgen_json_obj~plain(piqobj:Piqobj.obj)=letjson=Piqobj_to_json.gen_objpiqobjinletpiqtype=Piqobj_common.type_ofpiqobjinletpiqtype_name=C.full_piqi_typenamepiqtypein(* generating an associative array wrapper for primitive types because JSON
* doesn't support them as top-level objects, according to RFC 4627 that says:
* "A JSON text is a serialized object or array" *)(* optionally, wrapping arrays in a top-level object; it is the only
* reasonable way we can add "piqi_type" field to the serialized lists -- see
* below *)letjson=matchjsonwith|`Assoc_->json|`List_whenplain->json|_->`Assoc[("value",json)]inpiqtype_name,jsonletgen_json_common~plain(obj:obj)=matchobjwith|Typed_piqobjobj|Piqobjobj->gen_json_objobj~plain|Piqipiqi->(* output Piqi spec itself if we are converting .piqi *)piqi_to_jsonpiqi|Piqtype_->assertfalse(* type hints are not supported by Json encoding *)letgen_jsonobj=letpiqi_typename,json=gen_json_commonobj~plain:falsein(* adding "piqi_type": name as a first field of the serialized JSON object *)matchjsonwith|`Assoc(("piqi_type",_)::_)->(* already generated for piqi-any objects in --gen-extended-piqi-any mode *)json|`Assocl->letpiqi_type=("piqi_type",`Stringpiqi_typename)in`Assoc(piqi_type::l)|_->(* top-level json must be an object *)assertfalseletgen_plain_jsonobj=let_piqi_type,json=gen_json_commonobj~plain:trueinjsonletto_json_channelch(obj:obj)=letjson=gen_jsonobjinPiqi_json_gen.pretty_to_channelchjson;Pervasives.output_charch'\n'letto_json_string?(pretty_print=true)obj=letjson=gen_plain_jsonobjinifpretty_printthenPiqi_json_gen.pretty_to_stringjsonelsePiqi_json_gen.to_stringjsonletread_json_astjson_parser:Piqi_json_type.json=letres=Piqi_json.read_json_objjson_parserinmatchreswith|Someast->ast|None->raiseEOFletis_primitivepiqtype=matchC.unaliaspiqtypewith|`enum_->true|#T.typedef->false|_->trueletis_listpiqtype=matchC.unaliaspiqtypewith|`list_->true|_->falseletload_json_commonpiqtypeast=letast=ifis_primitivepiqtypethen(* expecting primitive types to be wrapped in associative array because JSON
* doesn't support them as top-level objects, according to RFC 4627 that
* says: "A JSON text is a serialized object or array" *)matchastwith|`Assoc["_",ast](* older pre- 0.6.0 format *)|`Assoc["value",ast]->ast|_->errorast"invalid toplevel value for primitive type: {\"value\": ...} expected"elseifis_listpiqtypethenmatchastwith|`Assoc["value",ast]->ast(* sometimes top-level arrays are embedded in objects *)|_->astelseastinifpiqtype==!Piqi.piqi_lang_def(* XXX *)thenletpiqi=piqi_of_jsonastinPiqipiqielseletobj=piqobj_of_jsonpiqtypeastinTyped_piqobjobjletload_json(user_piqtype:T.piqtypeoption)json_parser:obj=letast=read_json_astjson_parserin(* check typenames, as Json parser doesn't do it unlike the Piq parser *)letcheck=trueinmatchastwith|`Assoc(("piqi_type",`String"piqi")::fields)->letast=Piqloc.addrefretast(`Assocfields)in(* NOTE: caching the loaded module *)letpiqi=piqi_of_jsonast~process:falseinPiqipiqi|`Assoc(("piqi_type",`Stringtypename)::fields)->letpiqtype=find_piqtypetypename~checkinletast=Piqloc.addrefretast(`Assocfields)inload_json_commonpiqtypeast|`Assoc(("piqi_type",ast)::_)->errorast"invalid \"piqi_type\" format"|_->(* there's no first field that looks like "piqi_type": ... => using the
* user-supplied piqtype *)(matchuser_piqtypewith|Somepiqtype->load_json_commonpiqtypeast|None->C.errorast"default type for JSON object is unknown")(* load json while ignoring all embedded type hints *)letload_plain_json(piqtype:T.piqtype)json_parser:obj=letast=read_json_astjson_parserinletast=matchastwith|`Assoc(("piqi_type",`String_)::fields)->(* skip the "piqi_type" field whenever it is present *)Piqloc.addrefretast(`Assocfields)|_->astinload_json_commonpiqtypeast(*
* XML reading and writing
*)letpiqi_of_xmlxml=letpiqtype=!Piqi.piqi_spec_defin(* don't resolve defaults when reading xml *)letpiqobj=C.with_resolve_defaultsfalse(fun()->Piqobj_of_xml.parse_objpiqtypexml)in(* don't try to track location references as we don't preserve them yet in
* piqobj_of_xml *)Piqloc.pause();letpiqi=Piqi.piqi_of_piqobjpiqobjinPiqloc.resume();piqiletpiqi_to_xmlpiqi=letpiqobj=Piqi.piqi_to_piqobjpiqiinPiqobj_to_xml.gen_toplevel_objpiqobjletgen_xml(obj:obj):Piqi_xml.xml=matchobjwith|Typed_piqobjobj|Piqobjobj->Piqobj_to_xml.gen_toplevel_objobj|Piqipiqi->(* output Piqi spec itself if we are converting .piqi *)piqi_to_xmlpiqi|Piqtype_->assertfalse(* type hints are not supported by xml encoding *)letto_xml_channelch(obj:obj)=letxml=gen_xmlobjinPiqi_xml.xml_to_channelchxml;Pervasives.output_charch'\n'letto_xml_string?pretty_printobj=letxml=gen_xmlobjinPiqi_xml.xml_to_stringxml?pretty_printletread_xml_astxml_parser:Piqi_xml.xml=letres=Piqi_xml.read_xml_objxml_parserinmatchreswith|Someast->ast|None->raiseEOFletload_xml(piqtype:T.piqtype)xml_parser:obj=letast=read_xml_astxml_parserinifpiqtype==!Piqi.piqi_lang_def(* XXX *)thenletpiqi=piqi_of_xmlastinPiqipiqielseletobj=Piqobj_of_xml.parse_objpiqtypeastinTyped_piqobjobjletinit()=(* XXX: this is necessary when we convert to/from json, but now calling it
* regardless of whether we actually need it *)Piqi_json.init()(*
* The converter:
*)letfname="input"(* XXX *)letload_piq_or_pibget_next~is_piqi_input=letrecaux()=letobj=get_next()inmatchobjwith|Piqtype_->aux()(* skip default type *)|Piqi_whennotis_piqi_input->aux()(* skip embedded piqi *)|Piqobjobj->Typed_piqobjobj|_->obj(* Typed_piqobj or Piqi *)inaux()letfrom_piq_stringpiqtypes~is_piqi_input=letpiq_parser=Piq_parser.init_from_stringfnamesinletget_next()=load_piq(Somepiqtype)piq_parserinletobj=load_piq_or_pibget_next~is_piqi_inputin(* XXX: check eof? *)objletfrom_pib_stringpiqtypes~is_piqi_input=letbuf=Piqirun.IBuf.of_stringsinletget_next()=load_pib(Somepiqtype)bufinletobj=load_piq_or_pibget_next~is_piqi_inputin(* XXX: check eof? *)objletfrom_json_stringpiqtypes=letjson_parser=Piqi_json_parser.init_from_string~fnamesinletobj=load_plain_jsonpiqtypejson_parserin(* XXX: check eof? *)objletfrom_pb_stringpiqtypes=letbuf=Piqirun.init_from_stringsinletobj=load_pbpiqtypebufin(* XXX: check eof? *)objletfrom_xml_stringpiqtypes=letxml_parser=Piqi_xml.init_from_string~fnamesinletobj=load_xmlpiqtypexml_parserin(* XXX: check eof? *)objletparse_objpiqtypeinput_formatdata=(* XXX *)letis_piqi_input=(piqtype==!Piqi.piqi_lang_def)inletpiqobj=matchinput_formatwith|`piq->from_piq_stringpiqtypedata~is_piqi_input|`json->from_json_stringpiqtypedata|`pb->from_pb_stringpiqtypedata|`xml->from_xml_stringpiqtypedata|`pib->from_pib_stringpiqtypedata~is_piqi_inputinpiqobjletgen_obj~pretty_printoutput_formatpiqobj=matchoutput_formatwith|`piq->to_piq_stringpiqobj|`json->to_json_stringpiqobj~pretty_print|`pb->to_pb_stringpiqobj|`xml->to_xml_stringpiqobj~pretty_print|`pib->to_pib_stringpiqobjtypeoptions={mutablejson_omit_missing_fields:bool;mutablepretty_print:bool;mutableuse_strict_parsing:bool;mutablepiq_frameless_output:bool;mutablepiq_frameless_input:bool;mutablepiq_relaxed_parsing:bool;}letmake_options?(pretty_print=true)?(json_omit_missing_fields=true)?(json_omit_null_fields=true)(* deprecated: use json_omit_missing_fields instead *)?(use_strict_parsing=false)?(piq_frameless_output=false)?(piq_frameless_input=false)?(piq_relaxed_parsing=false)()={json_omit_missing_fields=json_omit_null_fields&&json_omit_missing_fields;pretty_print=pretty_print;use_strict_parsing=use_strict_parsing;piq_frameless_output=piq_frameless_output;piq_frameless_input=piq_frameless_input;piq_relaxed_parsing=piq_relaxed_parsing;}letset_optionsopts=Piqobj_to_json.omit_missing_fields:=opts.json_omit_missing_fields;Piqi_config.flag_strict:=opts.use_strict_parsing;Piqi_config.piq_frameless_output:=opts.piq_frameless_output;Piqi_config.piq_frameless_input:=opts.piq_frameless_input;Piqi_config.piq_relaxed_parsing:=opts.piq_relaxed_parsing;()(* called by Piqirun_ext and Piqi_server *)letfind_typetypename=ifnot(Piqi_name.is_valid_typenametypename)thenpiqi_error("invalid type name: "^typename);iftypename="piqi"(* special case *)then!Piqi.piqi_lang_def(* return Piqi type from embedded self-definition *)elsetryPiqi_db.find_piqtypetypenamewithNot_found->piqi_error("unknown type: "^typename)(* called by Piqirun_ext and Piqi_server *)letconvert~optspiqtypeinput_formatoutput_formatdata=(* apply some of the settings *)set_optionsopts;(* resetting source location tracking back to "enabled" state; we don't
* carefully call matching Piqloc.resume () for every Piqloc.pause () if we
* get exceptions in between *)Piqloc.is_paused:=0;(* perform the conversion
* NOTE: we need to resolve all defaults before converting to JSON or XML *)letpiqobj=C.with_resolve_defaults(output_format=`json||output_format=`xml)(fun()->parse_objpiqtypeinput_formatdata)in(* reset location db to allow GC to collect previously read objects *)Piqloc.reset();gen_objoutput_formatpiqobj~pretty_print:opts.pretty_print