diff --git a/src/json/parseinto.cpp b/src/json/parseinto.cpp index 99d381b..00bb25c 100644 --- a/src/json/parseinto.cpp +++ b/src/json/parseinto.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1) @@ -22,18 +23,26 @@ struct context { std::string msg; - typedef std::vector stack_t; + struct frame { + pvd::PVFieldPtr fld; + pvd::BitSet *assigned; + frame(const pvd::PVFieldPtr& fld, pvd::BitSet *assigned) + :fld(fld), assigned(assigned) + {} + }; + + typedef std::vector stack_t; stack_t stack; - context(const pvd::PVFieldPtr& root) + context(const pvd::PVFieldPtr& root, pvd::BitSet *assigned) { - stack.push_back(root); + stack.push_back(frame(root, assigned)); } }; #define TRY context *self = (context*)ctx; assert(!self->stack.empty()); try -#define CATCH() catch(std::exception& e) { self->msg = e.what(); return 0; } +#define CATCH() catch(std::exception& e) { if(self->msg.empty()) self->msg = e.what(); return 0; } int jtree_null(void * ctx) { @@ -46,18 +55,20 @@ int jtree_null(void * ctx) template void valueAssign(context *self, typename PVScalarT::value_type val) { - pvd::Type type(self->stack.back()->getField()->getType()); + assert(!self->stack.empty()); + context::frame& back = self->stack.back(); + pvd::Type type(back.fld->getField()->getType()); if(type==pvd::scalar) { - pvd::PVScalar* fld(static_cast(self->stack.back().get())); + pvd::PVScalar* fld(static_cast(back.fld.get())); fld->putFrom(val); + if(back.assigned) + back.assigned->set(fld->getFieldOffset()); self->stack.pop_back(); - // structure back at the top of the stack + // structure at the top of the stack } else if(type==pvd::scalarArray) { - pvd::PVScalarArray* fld(static_cast(self->stack.back().get())); - - PVArrayT* arrfld(dynamic_cast(fld)); + PVArrayT* arrfld(dynamic_cast(back.fld.get())); if(!arrfld) throw std::invalid_argument("wrong type for scalar array"); @@ -73,7 +84,7 @@ void valueAssign(context *self, typename PVScalarT::value_type val) // leave array field at top of stack } else if(type==pvd::union_) { - pvd::PVUnion* fld(static_cast(self->stack.back().get())); + pvd::PVUnion* fld(static_cast(back.fld.get())); pvd::UnionConstPtr utype(fld->getUnion()); if(utype->isVariant()) { @@ -109,6 +120,8 @@ void valueAssign(context *self, typename PVScalarT::value_type val) if(!assigned) throw std::runtime_error("Unable to select union member"); } + if(back.assigned) + back.assigned->set(fld->getFieldOffset()); self->stack.pop_back(); // structure back at the top of the stack @@ -154,22 +167,24 @@ int jtree_string(void * ctx, const unsigned char * stringVal, int jtree_start_map(void * ctx) { TRY { - pvd::PVFieldPtr& back(self->stack.back()); - pvd::Type type = back->getField()->getType(); + assert(!self->stack.empty()); + + context::frame& back = self->stack.back(); + pvd::Type type = back.fld->getField()->getType(); if(type==pvd::structure) { // will fill in } else if(type==pvd::structureArray) { // starting new element in structure array - pvd::PVStructureArrayPtr sarr(std::tr1::static_pointer_cast(back)); + pvd::PVStructureArray* sarr(static_cast(back.fld.get())); pvd::PVStructurePtr elem(pvd::getPVDataCreate()->createPVStructure(sarr->getStructureArray()->getStructure())); - self->stack.push_back(elem); + self->stack.push_back(context::frame(elem, 0)); } else { throw std::runtime_error("Can't map (sub)structure"); } - assert(self->stack.back()->getField()->getType()==pvd::structure); + assert(self->stack.back().fld->getField()->getType()==pvd::structure); return 1; }CATCH() } @@ -178,13 +193,14 @@ int jtree_map_key(void * ctx, const unsigned char * key, unsigned int stringLen) { TRY { + assert(!self->stack.empty()); std::string name((const char*)key, stringLen); // start_map() ensures we have a structure at the top of the stack - pvd::PVStructure *fld = static_cast(self->stack.back().get()); + pvd::PVStructure *fld = static_cast(self->stack.back().fld.get()); try { - self->stack.push_back(fld->getSubFieldT(name)); + self->stack.push_back(context::frame(fld->getSubFieldT(name), self->stack.back().assigned)); }catch(std::runtime_error& e){ std::ostringstream strm; strm<<"At "<getFullName()<<" : "<stack.back()->getField()->getType()==pvd::structure); + assert(!self->stack.empty()); + assert(self->stack.back().fld->getField()->getType()==pvd::structure); - pvd::PVStructurePtr elem(std::tr1::static_pointer_cast(self->stack.back())); + context::frame elem(self->stack.back()); self->stack.pop_back(); - if(!self->stack.empty() && self->stack.back()->getField()->getType()==pvd::structureArray) { + if(!self->stack.empty() && self->stack.back().fld->getField()->getType()==pvd::structureArray) { // append element to struct array - pvd::PVStructureArray *sarr = static_cast(self->stack.back().get()); + pvd::PVStructureArray *sarr = static_cast(self->stack.back().fld.get()); pvd::PVStructureArray::const_svector cval; sarr->swap(cval); pvd::PVStructureArray::svector val(pvd::thaw(cval)); - val.push_back(elem); + val.push_back(std::tr1::static_pointer_cast(elem.fld)); sarr->replace(pvd::freeze(val)); } @@ -224,7 +241,8 @@ int jtree_end_map(void * ctx) int jtree_start_array(void * ctx) { TRY { - pvd::PVFieldPtr& back(self->stack.back()); + assert(!self->stack.empty()); + pvd::PVFieldPtr& back(self->stack.back().fld); pvd::Type type = back->getField()->getType(); if(type!=pvd::structureArray && type!=pvd::scalarArray) throw std::runtime_error("Can't assign array"); @@ -235,6 +253,10 @@ int jtree_start_array(void * ctx) int jtree_end_array(void * ctx) { TRY { + assert(!self->stack.empty()); + + if(self->stack.back().assigned) + self->stack.back().assigned->set(self->stack.back().fld->getFieldOffset()); self->stack.pop_back(); return 1; }CATCH() @@ -274,14 +296,15 @@ namespace epics{namespace pvData{ epicsShareFunc void parseJSON(std::istream& strm, - const PVField::shared_pointer& dest) + const PVField::shared_pointer& dest, + BitSet *assigned) { yajl_parser_config conf; memset(&conf, 0, sizeof(conf)); conf.allowComments = 1; conf.checkUTF8 = 1; - context ctxt(dest); + context ctxt(dest, assigned); handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt)); diff --git a/src/json/pv/json.h b/src/json/pv/json.h index a2c626f..5455fe8 100644 --- a/src/json/pv/json.h +++ b/src/json/pv/json.h @@ -31,6 +31,8 @@ namespace epics{namespace pvData{ +class BitSet; + /** @defgroup pvjson JSON print/parse * * Printing PVField as JSON and parsing JSON into PVField. @@ -73,12 +75,18 @@ PVStructure::shared_pointer parseJSON(std::istream& strm); * * Restrictions: * - * - array of union not permitted + * - array of union not supported * - Only scalar value assigned to union + * + * @param strm Read JSON text from stream + * @param dest Store in fields of this structure + * @param assigned Which fields of _dest_ were assigned. (Optional) + * @throws std::runtime_error on failure. dest and assigned may be modified. */ epicsShareFunc void parseJSON(std::istream& strm, - const PVField::shared_pointer& dest); + const PVField::shared_pointer& dest, + BitSet *assigned=0); /** Wrapper around yajl_parse() diff --git a/testApp/misc/testjson.cpp b/testApp/misc/testjson.cpp index b93d347..afa9eef 100644 --- a/testApp/misc/testjson.cpp +++ b/testApp/misc/testjson.cpp @@ -10,6 +10,7 @@ #if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1) #include +#include #include #include @@ -54,10 +55,12 @@ void testparsebare() std::istringstream strm("5"); pvd::PVIntPtr fld(pvd::getPVDataCreate()->createPVScalar()); + pvd::BitSet bits; - pvd::parseJSON(strm, fld); + pvd::parseJSON(strm, fld, &bits); testOk1(fld->get()==5); + testEqual(bits, pvd::BitSet().set(0)); } { std::istringstream strm("\"5\""); @@ -120,6 +123,7 @@ const char bigtest[] = " \"any\": \"4.2\",\n" " \"almost\": \"hello\"\n" "}"; +// intentionally not setting "extra" pvd::StructureConstPtr bigtype(pvd::getFieldCreate()->createFieldBuilder() ->add("scalar", pvd::pvInt) @@ -130,6 +134,7 @@ pvd::StructureConstPtr bigtype(pvd::getFieldCreate()->createFieldBuilder() ->add("y", pvd::pvInt) ->endNested() ->endNested() + ->add("extra", pvd::pvInt) ->addNestedStructureArray("sarr") ->add("a", pvd::pvInt) ->add("b", pvd::pvInt) @@ -149,10 +154,21 @@ void testInto() pvd::PVStructurePtr val(pvd::getPVDataCreate()->createPVStructure(bigtype)); std::istringstream strm(bigtest); + pvd::BitSet assigned; std::cout<getSubField("scalar")->getFieldOffset()) + .set(val->getSubField("ivec")->getFieldOffset()) + .set(val->getSubField("svec")->getFieldOffset()) + .set(val->getSubField("sub.x.y")->getFieldOffset()) + // "extra" not set + .set(val->getSubField("sarr")->getFieldOffset()) + .set(val->getSubField("any")->getFieldOffset()) + .set(val->getSubField("almost")->getFieldOffset())); std::cout<