diff --git a/src/json/parseinto.cpp b/src/json/parseinto.cpp index 66c5ad4..99d381b 100644 --- a/src/json/parseinto.cpp +++ b/src/json/parseinto.cpp @@ -43,14 +43,13 @@ int jtree_null(void * ctx) }CATCH() } -template -void valueAssign(context *self, typename PVD::value_type val) +template +void valueAssign(context *self, typename PVScalarT::value_type val) { pvd::Type type(self->stack.back()->getField()->getType()); if(type==pvd::scalar) { pvd::PVScalar* fld(static_cast(self->stack.back().get())); - if(!fld) - throw std::invalid_argument("Not a scalar field"); + fld->putFrom(val); self->stack.pop_back(); // structure back at the top of the stack @@ -58,20 +57,61 @@ void valueAssign(context *self, typename PVD::value_type val) } else if(type==pvd::scalarArray) { pvd::PVScalarArray* fld(static_cast(self->stack.back().get())); - PVD* arrfld(dynamic_cast(fld)); + PVArrayT* arrfld(dynamic_cast(fld)); if(!arrfld) throw std::invalid_argument("wrong type for scalar array"); - typename PVD::const_svector carr; + typename PVArrayT::const_svector carr; arrfld->swap(carr); - typename PVD::svector arr(pvd::thaw(carr)); + typename PVArrayT::svector arr(pvd::thaw(carr)); arr.push_back(val); arrfld->replace(pvd::freeze(arr)); // leave array field at top of stack + + } else if(type==pvd::union_) { + pvd::PVUnion* fld(static_cast(self->stack.back().get())); + pvd::UnionConstPtr utype(fld->getUnion()); + + if(utype->isVariant()) { + typename PVScalarT::shared_pointer elem(pvd::getPVDataCreate()->createPVScalar()); + + elem->put(val); + + fld->set(elem); + + } else { + // attempt automagic assignment + + const pvd::StringArray& names = utype->getFieldNames(); + const pvd::FieldConstPtrArray types = utype->getFields(); + assert(names.size()==types.size()); + + bool assigned = false; + for(size_t i=0, N=names.size(); igetType()!=pvd::scalar) continue; + + pvd::PVScalarPtr ufld(fld->select(i)); + try{ + ufld->putFrom(val); + assigned = true; + }catch(std::runtime_error&){ + if(i==N-1) + throw; + continue; + } + + break; + } + if(!assigned) + throw std::runtime_error("Unable to select union member"); + } + self->stack.pop_back(); + // structure back at the top of the stack + } else { throw std::invalid_argument("Can't assign value"); } @@ -80,7 +120,7 @@ void valueAssign(context *self, typename PVD::value_type val) int jtree_boolean(void * ctx, int boolVal) { TRY { - valueAssign(self, !!boolVal); + valueAssign(self, !!boolVal); return 1; }CATCH() } @@ -88,7 +128,7 @@ int jtree_boolean(void * ctx, int boolVal) int jtree_integer(void * ctx, long integerVal) { TRY { - valueAssign(self, integerVal); + valueAssign(self, integerVal); return 1; }CATCH() } @@ -96,7 +136,7 @@ int jtree_integer(void * ctx, long integerVal) int jtree_double(void * ctx, double doubleVal) { TRY { - valueAssign(self, doubleVal); + valueAssign(self, doubleVal); return 1; }CATCH() } @@ -106,7 +146,7 @@ int jtree_string(void * ctx, const unsigned char * stringVal, { TRY { std::string val((const char*)stringVal, stringLen); - valueAssign(self, val); + valueAssign(self, val); return 1; }CATCH() } diff --git a/src/json/print.cpp b/src/json/print.cpp index 27879af..1a9f450 100644 --- a/src/json/print.cpp +++ b/src/json/print.cpp @@ -127,6 +127,18 @@ void show_field(args& A, const pvd::PVField* fld) A.strm.put(']'); } break; + case pvd::union_: + { + const pvd::PVUnion *U=static_cast(fld); + pvd::PVFieldPtr C(U->get()); + + if(!C) { + A.strm<<"null"; + } else { + show_field(A, C.get()); + } + } + break; default: if(A.opts.ignoreUnprintable) A.strm<<"// unprintable field type"; diff --git a/src/json/pv/json.h b/src/json/pv/json.h index 22788e1..a2c626f 100644 --- a/src/json/pv/json.h +++ b/src/json/pv/json.h @@ -73,7 +73,8 @@ PVStructure::shared_pointer parseJSON(std::istream& strm); * * Restrictions: * - * - Union or array of union not permitted + * - array of union not permitted + * - Only scalar value assigned to union */ epicsShareFunc void parseJSON(std::istream& strm, diff --git a/src/misc/pv/pvUnitTest.h b/src/misc/pv/pvUnitTest.h index 8437f4e..6f413a7 100644 --- a/src/misc/pv/pvUnitTest.h +++ b/src/misc/pv/pvUnitTest.h @@ -130,6 +130,11 @@ testFieldEqual(const std::tr1::shared_ptr& val, cons return ::detail::testPassx(false)<<" null structure pointer"; } typename PVD::shared_pointer fval(val->getSubField(name)); + if(!fval) { + epics::pvData::PVUnionPtr uval(val->getSubField(name)); + if(uval) + fval = uval->get(); + } if(!fval) { return ::detail::testPassx(false)<<" field '"<(val, "hello", sarr); } +void testparsebare() +{ + testDiag("testparsebare()"); + { + std::istringstream strm("5"); + + pvd::PVIntPtr fld(pvd::getPVDataCreate()->createPVScalar()); + + pvd::parseJSON(strm, fld); + + testOk1(fld->get()==5); + } + { + std::istringstream strm("\"5\""); + + pvd::PVIntPtr fld(pvd::getPVDataCreate()->createPVScalar()); + + pvd::parseJSON(strm, fld); + + testOk1(fld->get()==5); + } + { + std::istringstream strm("\"hello\""); + + pvd::PVStringPtr fld(pvd::getPVDataCreate()->createPVScalar()); + + pvd::parseJSON(strm, fld); + + testOk1(fld->get()=="hello"); + } +} + void testparseanyjunk() { testDiag("testparseanyjunk()"); @@ -84,7 +116,9 @@ const char bigtest[] = " {\"a\":5, \"b\":6}\n" " ,{\"a\":7, \"b\":8}\n" " ,{\"a\":9, \"b\":10}\n" - " ]\n" + " ],\n" + " \"any\": \"4.2\",\n" + " \"almost\": \"hello\"\n" "}"; pvd::StructureConstPtr bigtype(pvd::getFieldCreate()->createFieldBuilder() @@ -100,6 +134,11 @@ pvd::StructureConstPtr bigtype(pvd::getFieldCreate()->createFieldBuilder() ->add("a", pvd::pvInt) ->add("b", pvd::pvInt) ->endNested() + ->add("any", pvd::getFieldCreate()->createVariantUnion()) + ->addNestedUnion("almost") + ->add("one", pvd::pvInt) + ->add("two", pvd::pvString) + ->endNested() ->createStructure() ); @@ -145,6 +184,9 @@ void testInto() testFieldEqual(elems[2], "a", 9); testFieldEqual(elems[2], "b", 10); } + + testFieldEqual(val, "any", "4.2"); + testFieldEqual(val, "almost", "hello"); } void testroundtrip() @@ -204,7 +246,9 @@ void testroundtrip() "}}," "\"sarr\": [{\"a\": 5,\"b\": 6}," "{\"a\": 7,\"b\": 8}," - "{\"a\": 9,\"b\": 10}]" + "{\"a\": 9,\"b\": 10}]," + "\"any\": \"4.2\"," + "\"almost\": \"hello\"" "}"); } @@ -212,10 +256,11 @@ void testroundtrip() MAIN(testjson) { - testPlan(22); + testPlan(27); try { testparseany(); testparseanyarray(); + testparsebare(); testparseanyjunk(); testInto(); testroundtrip();