/* * Copyright information and license terms for this software can be * found in the file LICENSE that is included with the distribution */ #include #include #include #include #define epicsExportSharedSymbols #include #include #include #include #include "pv/json.h" namespace pvd = epics::pvData; namespace { using namespace pvd::yajl; void yg(yajl_gen_status sts) { const char *msg = "<\?\?\?>"; switch(sts) { case yajl_gen_status_ok: case yajl_gen_generation_complete: return; #define CASE(STS) case STS: msg = #STS; break CASE(yajl_gen_keys_must_be_strings); CASE(yajl_gen_in_error_state); CASE(yajl_gen_no_buf); CASE(yajl_gen_invalid_number); CASE(yajl_max_depth_exceeded); #ifdef EPICS_YAJL_VERSION CASE(yajl_gen_invalid_string); #endif #undef CASE } throw std::runtime_error(msg); } static void stream_printer(void * ctx, const char * str, size_arg len) { std::ostream *strm = (std::ostream*)ctx; strm->write(str, len); } struct args { yajl_gen handle; const pvd::JSONPrintOptions& opts; std::string indent; args(std::ostream& strm, const pvd::JSONPrintOptions& opts) :opts(opts) ,indent(opts.indent, ' ') { #ifndef EPICS_YAJL_VERSION yajl_gen_config conf; conf.beautify = opts.multiLine; conf.indentString = indent.c_str(); if(!(handle = yajl_gen_alloc2(stream_printer, NULL, NULL, &strm))) throw std::bad_alloc(); if(opts.json5) { static bool warned; if(!warned) { warned = true; errlogPrintf("Warning: Ignoring request to print JSON5. Update Base >= 7.0.6.1"); } } #else if(!(handle = yajl_gen_alloc(NULL))) throw std::bad_alloc(); if(opts.multiLine) { yajl_gen_config(handle, yajl_gen_beautify, 1); yajl_gen_config(handle, yajl_gen_indent_string, indent.c_str()); } else { yajl_gen_config(handle, yajl_gen_beautify, 0); } # if EPICS_VERSION_INT>=VERSION_INT(7,0,6,1) yajl_gen_config(handle, yajl_gen_json5, (int)opts.json5); # else if(opts.json5) { static bool warned; if(!warned) { warned = true; errlogPrintf("Warning: Ignoring request to print JSON5. Update Base >= 7.0.6.1"); } } # endif yajl_gen_config(handle, yajl_gen_print_callback, stream_printer, &strm); #endif } ~args() { yajl_gen_free(handle); } }; void yg_string(yajl_gen handle, const std::string& s) { yg(yajl_gen_string(handle, (const unsigned char*)s.c_str(), s.size())); } void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask); void show_struct(args& A, const pvd::PVStructure* fld, const pvd::BitSet *mask) { const pvd::StructureConstPtr& type = fld->getStructure(); const pvd::PVFieldPtrArray& children = fld->getPVFields(); const pvd::StringArray& names = type->getFieldNames(); yg(yajl_gen_map_open(A.handle)); for(size_t i=0, N=names.size(); iget(children[i]->getFieldOffset())) continue; yg_string(A.handle, names[i]); show_field(A, children[i].get(), mask); } yg(yajl_gen_map_close(A.handle)); } void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask) { switch(fld->getField()->getType()) { case pvd::scalar: { const pvd::PVScalar *scalar=static_cast(fld); switch(scalar->getScalar()->getScalarType()) { case pvd::pvString: yg_string(A.handle, scalar->getAs()); break; case pvd::pvBoolean: yg(yajl_gen_bool(A.handle, scalar->getAs())); break; case pvd::pvDouble: case pvd::pvFloat: yg(yajl_gen_double(A.handle, scalar->getAs())); break; // case pvd::pvULong: // can't always be exactly represented... default: yg(yajl_gen_integer(A.handle, scalar->getAs())); break; } } return; case pvd::scalarArray: { const pvd::PVScalarArray *scalar=static_cast(fld); pvd::shared_vector arr; scalar->getAs(arr); yg(yajl_gen_array_open(A.handle)); switch(arr.original_type()) { case pvd::pvString: { pvd::shared_vector sarr(pvd::shared_vector_convert(arr)); for(size_t i=0, N=sarr.size(); i sarr(pvd::shared_vector_convert(arr)); for(size_t i=0, N=sarr.size(); i sarr(pvd::shared_vector_convert(arr)); for(size_t i=0, N=sarr.size(); i sarr(pvd::shared_vector_convert(arr)); for(size_t i=0, N=sarr.size(); i(fld), mask); return; case pvd::structureArray: { pvd::PVStructureArray::const_svector arr(static_cast(fld)->view()); yg(yajl_gen_array_open(A.handle)); for(size_t i=0, N=arr.size(); i(fld); const pvd::PVField::const_shared_pointer& C(U->get()); if(!C) { yg(yajl_gen_null(A.handle)); } else { show_field(A, C.get(), 0); } } return; case pvd::unionArray: { const pvd::PVUnionArray *U=static_cast(fld); pvd::PVUnionArray::const_svector arr(U->view()); yg(yajl_gen_array_open(A.handle)); for(size_t i=0, N=arr.size(); i=0 && idxgetNextFieldOffset(); igetParent(); parent; parent = parent->getParent()) { mask.set(parent->getFieldOffset()); } } } } } } // namespace namespace epics{namespace pvData{ JSONPrintOptions::JSONPrintOptions() :multiLine(true) ,ignoreUnprintable(true) ,indent(0) ,json5(false) {} void printJSON(std::ostream& strm, const PVStructure& val, const BitSet& mask, const JSONPrintOptions& opts) { args A(strm, opts); pvd::BitSet emask(mask); expandBS(val, emask, true); if(!emask.get(0)) return; show_struct(A, &val, &emask); } void printJSON(std::ostream& strm, const PVField& val, const JSONPrintOptions& opts) { args A(strm, opts); show_field(A, &val, 0); } }} // namespace epics::pvData