浏览代码

HPCC-9401 Update based on review and missing changes

Removed IPropTree::getIsArrayItem().  toJSON now looks ahead one child
item to determine when an element repeats.

Also reimplemented lost changes to only create a workunit if/when its
actually needed.

Also fixed various other items caught in code review.

 to delay creating workunit

Signed-off-by: Anthony Fishbeck <anthony.fishbeck@lexisnexis.com>
Anthony Fishbeck 11 年之前
父节点
当前提交
ac38c6c3cc

+ 23 - 3
common/wuwebview/wuwebview.cpp

@@ -127,6 +127,8 @@ public:
 
     void appendManifestSchemas(IPropertyTree &manifest, ILoadedDllEntry *dll)
     {
+        if (flags & WWV_OMIT_SCHEMAS)
+            return;
         assertex(!finalized);
         if (!dll)
             return;
@@ -655,15 +657,33 @@ void WuWebView::renderSingleResult(const char *viewName, const char *resultname,
     renderExpandedResults(viewName, buffer, out);
 }
 
-void WuWebView::expandResults(const char *xml, StringBuffer &out, unsigned flags)
+void expandWuXmlResults(StringBuffer &out, const char *name, const char *xml, unsigned flags, IPropertyTree *manifest, ILoadedDllEntry *dll)
 {
-    WuExpandedResultBuffer expander(name.str(), flags);
+    WuExpandedResultBuffer expander(name, flags);
     expander.appendDatasetsFromXML(xml);
-    expander.appendManifestSchemas(*ensureManifest(), loadDll());
+    if (!(flags & WWV_OMIT_SCHEMAS) && manifest && dll)
+        expander.appendManifestSchemas(*manifest, dll);
     expander.finalize();
     out.append(expander.buffer);
 }
 
+extern WUWEBVIEW_API void expandWuXmlResults(StringBuffer &out, const char *name, const char *xml, unsigned flags)
+{
+    expandWuXmlResults(out, name, xml, flags, NULL, NULL);
+}
+
+void WuWebView::expandResults(const char *xml, StringBuffer &out, unsigned flags)
+{
+    IPropertyTree *manifest = NULL;
+    ILoadedDllEntry *dll = NULL;
+    if (!(flags & WWV_OMIT_SCHEMAS))
+    {
+        manifest = ensureManifest();
+        dll = loadDll();
+    }
+    expandWuXmlResults(out, name.str(), xml, flags, manifest, dll);
+}
+
 void WuWebView::createWuidResponse(StringBuffer &out, unsigned flags)
 {
     flags &= ~WWV_ADD_RESULTS_TAG;

+ 1 - 0
common/wuwebview/wuwebview.hpp

@@ -64,6 +64,7 @@ extern WUWEBVIEW_API IWuWebView *createWuWebView(IConstWorkUnit &wu, const char
 extern WUWEBVIEW_API IWuWebView *createWuWebView(const char *wuid, const char *queryname, const char*dir, bool mapEspDir);
 
 extern WUWEBVIEW_API void getWuResourceByPath(const char *path, MemoryBuffer &mb, StringBuffer &mimetype);
+extern WUWEBVIEW_API void expandWuXmlResults(StringBuffer &out, const char *name, const char *xml, unsigned flags);
 
 static inline bool isPathSeparator(char sep)
 {

+ 50 - 113
esp/services/ws_ecl/ws_ecl_service.cpp

@@ -214,7 +214,7 @@ const char *nextParameterTag(StringBuffer &tag, const char *path)
 
 void ensureParameter(IPropertyTree *pt, StringBuffer &tag, const char *path, const char *value, const char *fullpath)
 {
-    if (!tag || !*tag)
+    if (!tag.length())
         return;
 
     unsigned idx = 1;
@@ -901,7 +901,7 @@ JSONField_Category xsdTypeToJSONFieldCategory(const char *xsdtype)
         return JSONField_Real;
     if (!strncmp(xsdtype, "decimal", 7)) //ecl creates derived types of the form decimal#_#
         return JSONField_Real;
-    if (!strncmp(xsdtype, "none", 4)) //maps to an eml schema element with no type.  set to true or don't add
+    if (streq(xsdtype, "none")) //maps to an eml schema element with no type.  set to true or don't add
         return JSONField_Present;
     return JSONField_String;
 }
@@ -1173,11 +1173,12 @@ int CWsEclBinding::getWsEcl2TabView(CHttpRequest* request, CHttpResponse* respon
     splitLookupInfo(request->queryParameters(), thepath, wuid, qs, qid);
 
     WsEclWuInfo wsinfo(wuid.str(), qs.str(), qid.str(), context->queryUserId(), context->queryPassword());
+    const char *w = wsinfo.ensureWuid();
 
     StringBuffer xml;
     xml.append("<tabview>");
     xml.append("<version>3</version>");
-    xml.appendf("<wuid>%s</wuid>", wsinfo.wuid.sget());
+    xml.appendf("<wuid>%s</wuid>", w);
     xml.appendf("<qset>%s</qset>", wsinfo.qsetname.sget());
     xml.appendf("<qname>%s</qname>", wsinfo.queryname.sget());
 
@@ -1624,6 +1625,7 @@ bool CWsEclBinding::getSchema(StringBuffer& schema, IEspContext &ctx, CHttpReque
 
 int CWsEclBinding::getGenForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wsinfo, bool box)
 {
+    IConstWorkUnit *wu = wsinfo.ensureWorkUnit();
     IProperties *parms = request->queryParameters();
 
     StringBuffer page;
@@ -1631,13 +1633,13 @@ int CWsEclBinding::getGenForm(IEspContext &context, CHttpRequest* request, CHttp
 
     StringBuffer v;
     StringBuffer formxml("<FormInfo>");
-    appendXMLTag(formxml, "WUID", wsinfo.wuid.sget());
+    appendXMLTag(formxml, "WUID", wsinfo.queryWuid());
     appendXMLTag(formxml, "QuerySet", wsinfo.qsetname.sget());
     appendXMLTag(formxml, "QueryName", wsinfo.queryname.sget());
     appendXMLTag(formxml, "ClientVersion", v.appendf("%g",context.getClientVersion()).str());
     appendXMLTag(formxml, "RequestElement", v.clear().append(wsinfo.queryname).append("Request").str());
 
-    Owned<IWuWebView> web = createWuWebView(*wsinfo.wu.get(), wsinfo.queryname.get(), getCFD(), true);
+    Owned<IWuWebView> web = createWuWebView(*wu, wsinfo.queryname.get(), getCFD(), true);
     if (web)
     {
         appendXMLTag(formxml, "Help", web->aggregateResources("HELP", v.clear()).str());
@@ -1816,7 +1818,7 @@ StringBuffer &appendJSONExceptions(StringBuffer &s, IMultiException *e, const ch
     return s;
 }
 
-void CWsEclBinding::getWsEclJsonRequest(StringBuffer& jsonmsg, IEspContext &context, CHttpRequest* request, WsEclWuInfo &wsinfo, const char *xmltype, const char *ns, unsigned flags)
+void CWsEclBinding::getWsEclJsonRequest(StringBuffer& jsonmsg, IEspContext &context, CHttpRequest* request, WsEclWuInfo &wsinfo, const char *xmltype, const char *ns, unsigned flags, bool validate)
 {
     size32_t start = jsonmsg.length();
     try
@@ -1824,6 +1826,14 @@ void CWsEclBinding::getWsEclJsonRequest(StringBuffer& jsonmsg, IEspContext &cont
         IProperties *parameters = context.queryRequestParameters();
         Owned<IPropertyTree> reqTree = createPTreeFromHttpParameters(wsinfo.queryname, parameters);
 
+        if (!validate)
+        {
+            jsonmsg.append('{');
+            appendJSONName(jsonmsg, wsinfo.queryname);
+            toJSON(reqTree, jsonmsg, 0, 0);
+            jsonmsg.append('}');
+            return;
+        }
         StringBuffer element;
         element.append(wsinfo.queryname.sget());
             element.append("Request");
@@ -1851,94 +1861,6 @@ void CWsEclBinding::getWsEclJsonRequest(StringBuffer& jsonmsg, IEspContext &cont
     }
 }
 
-void CWsEclBinding::getWsEclJsonResponse(StringBuffer& jsonmsg, IEspContext &context, CHttpRequest *request, const char *xml, WsEclWuInfo &wsinfo)
-{
-    size32_t start = jsonmsg.length();
-    const char *jsonp = context.queryRequestParameters()->queryProp("jsonp");
-    try
-    {
-        Owned<IPropertyTree> parmtree = createPTreeFromXMLString(xml, ipt_none, (PTreeReaderOptions)(ptr_ignoreWhiteSpace|ptr_ignoreNameSpaces));
-
-        StringBuffer element;
-        element.append(wsinfo.queryname.sget());
-        element.append("Response");
-
-        IPropertyTree *node = parmtree;
-        if (node->hasProp("Body"))
-            node = node->queryPropTree("Body");
-        if (node->hasProp(element))
-            node = node->queryPropTree(element);
-        const char *wuid = node->queryProp("Wuid");
-        if (node->hasProp("Results"))
-            node = node->queryPropTree("Results");
-        if (node->hasProp("Result"))
-            node = node->queryPropTree("Result");
-
-        if (jsonp && *jsonp)
-            jsonmsg.append(jsonp).append('(');
-
-        jsonmsg.appendf("{\"%s\": {", element.str());
-        Owned<IPropertyTreeIterator> exceptions = node->getElements("Exception");
-        Owned<IPropertyTreeIterator> datasets = node->getElements("Dataset");
-        if (wuid && *wuid)
-            appendJSONValue(jsonmsg, "Wuid", wuid);
-        if ((!exceptions || !exceptions->first()) && (!datasets || !datasets->first()))
-        {
-            jsonmsg.append("  }\n}");
-            return;
-        }
-
-        appendJSONName(jsonmsg, "Results").append("{\n");
-        if (exceptions && exceptions->first())
-        {
-            appendJSONName(jsonmsg, "Exceptions").append("{");
-            appendJSONName(jsonmsg, "Exception").append("[");
-            ForEach(*exceptions)
-                appendJSONExceptionItem(jsonmsg, exceptions->query().getPropInt("Code"), exceptions->query().queryProp("Message"), NULL, NULL);
-            jsonmsg.append("]}");
-        }
-
-        ForEach(*datasets)
-        {
-            IPropertyTree &ds = datasets->query();
-            const char *dsname = ds.queryProp("@name");
-            if (dsname && *dsname)
-            {
-                StringBuffer schemaResult;
-                wsinfo.getOutputSchema(schemaResult, dsname);
-                if (schemaResult.length())
-                {
-                    Owned<IXmlSchema> schema = createXmlSchemaFromString(schemaResult);
-                    if (schema.get())
-                    {
-                        IXmlType* type = schema->queryElementType("Dataset");
-                        if (type)
-                        {
-                            StringBuffer outname(dsname);
-                            delimitJSON(jsonmsg);
-                            StringArray parentTypes;
-                            buildJsonMsg(parentTypes, type, jsonmsg, outname.replace(' ', '_').str(), &ds, 0);
-                        }
-                    }
-                }
-            }
-        }
-        jsonmsg.append("}}}");
-        if (jsonp && *jsonp)
-            jsonmsg.append(");");
-    }
-    catch (IException *e)
-    {
-        jsonmsg.setLength(start);
-        if (jsonp && *jsonp)
-            jsonmsg.append(jsonp).append('(');
-        appendJSONException(jsonmsg.append('{'), e);
-        jsonmsg.append('}');
-        if (jsonp && *jsonp)
-            jsonmsg.append(");");
-    }
-}
-
 void CWsEclBinding::getSoapMessage(StringBuffer& soapmsg, IEspContext &context, CHttpRequest* request, WsEclWuInfo &wsinfo, unsigned flags, bool validate)
 {
     soapmsg.append(
@@ -1967,7 +1889,7 @@ inline StringBuffer &buildWsEclTargetUrl(StringBuffer &url, WsEclWuInfo &wsinfo,
     if (wsinfo.qsetname.length() && wsinfo.queryname.length())
         url.append("query/").append(wsinfo.qsetname.get()).append('/').append(wsinfo.queryname.get());
     else
-        url.append("wuid/").append(wsinfo.wuid.sget());
+        url.append("wuid/").append(wsinfo.queryWuid());
     if (params && *params)
         url.append('?').append(params);
     return url;
@@ -2018,7 +1940,7 @@ int CWsEclBinding::getXmlTestForm(IEspContext &context, CHttpRequest* request, C
     xform->setStringParameter("pageName", pageName.str());
     xform->setStringParameter("serviceName", wsinfo.qsetname.sget());
     xform->setStringParameter("methodName", wsinfo.queryname.sget());
-    xform->setStringParameter("wuid", wsinfo.wuid.sget());
+    xform->setStringParameter("wuid", wsinfo.queryWuid());
     xform->setStringParameter("header", header.str());
 
     ISecUser* user = context.queryUser();
@@ -2044,7 +1966,7 @@ int CWsEclBinding::getJsonTestForm(IEspContext &context, CHttpRequest* request,
     IProperties *parms = context.queryRequestParameters();
 
     StringBuffer jsonmsg, pageName;
-    getWsEclJsonRequest(jsonmsg, context, request, wsinfo, "json", NULL, 0);
+    getWsEclJsonRequest(jsonmsg, context, request, wsinfo, "json", NULL, 0, true);
 
     StringBuffer params;
     const char* excludes[] = {"soap_builder_",NULL};
@@ -2069,7 +1991,7 @@ int CWsEclBinding::getJsonTestForm(IEspContext &context, CHttpRequest* request,
     xform->setStringParameter("pageName", pageName.str());
     xform->setStringParameter("serviceName", wsinfo.qsetname.sget());
     xform->setStringParameter("methodName", wsinfo.queryname.sget());
-    xform->setStringParameter("wuid", wsinfo.wuid.sget());
+    xform->setStringParameter("wuid", wsinfo.queryWuid());
     xform->setStringParameter("header", header.str());
 
     ISecUser* user = context.queryUser();
@@ -2154,11 +2076,13 @@ int CWsEclBinding::getWsEcl2Form(CHttpRequest* request, CHttpResponse* response,
 
 int CWsEclBinding::submitWsEclWorkunit(IEspContext & context, WsEclWuInfo &wsinfo, IPropertyTree *reqTree, StringBuffer &out, unsigned flags, TextMarkupFormat fmt, const char *viewname, const char *xsltname)
 {
+    IConstWorkUnit *sourceWorkUnit = wsinfo.ensureWorkUnit();
+
     Owned <IWorkUnitFactory> factory = getSecWorkUnitFactory(*context.querySecManager(), *context.queryUser());
     Owned <IWorkUnit> workunit = factory->createWorkUnit(NULL, "wsecl", context.queryUserId());
 
     IExtendedWUInterface *ext = queryExtendedWU(workunit);
-    ext->copyWorkUnit(wsinfo.wu, false);
+    ext->copyWorkUnit(sourceWorkUnit, false);
 
     workunit->clearExceptions();
     workunit->resetWorkflow();
@@ -2181,9 +2105,9 @@ int CWsEclBinding::submitWsEclWorkunit(IEspContext & context, WsEclWuInfo &wsinf
     if (reqTree)
     {
         if (reqTree->hasProp("Envelope"))
-            reqTree=reqTree->queryPropTree("Envelope");
+            reqTree=reqTree->queryPropTree("Envelope[1]");
         if (reqTree->hasProp("Body"))
-            reqTree=reqTree->queryPropTree("Body/*[1]");
+            reqTree=reqTree->queryPropTree("Body[1]/*[1]");
         workunit->setXmlParams(LINK(reqTree));
     }
 
@@ -2290,7 +2214,7 @@ int CWsEclBinding::onSubmitQueryOutput(IEspContext &context, CHttpRequest* reque
     if (isRoxieReq && outputJSON)
     {
         StringBuffer jsonmsg;
-        getWsEclJsonRequest(jsonmsg, context, request, wsinfo, "json", NULL, 0);
+        getWsEclJsonRequest(jsonmsg, context, request, wsinfo, "json", NULL, 0, false);
         sendRoxieRequest(wsinfo.qsetname.get(), jsonmsg, output, status, wsinfo.queryname, "application/json");
     }
     else
@@ -2310,10 +2234,17 @@ int CWsEclBinding::onSubmitQueryOutput(IEspContext &context, CHttpRequest* reque
         else
         {
             StringBuffer roxieresp;
-            sendRoxieRequest(wsinfo.qsetname.get(), soapmsg, roxieresp, status, wsinfo.queryname);
-            Owned<IWuWebView> web = createWuWebView(*wsinfo.wu, wsinfo.queryname.get(), getCFD(), true);
-            if (web.get())
-                web->expandResults(roxieresp.str(), output, xmlflags);
+            sendRoxieRequest(wsinfo.qsetname, soapmsg, roxieresp, status, wsinfo.queryname);
+            if (xmlflags & WWV_OMIT_SCHEMAS)
+                expandWuXmlResults(output, wsinfo.queryname, roxieresp.str(), xmlflags);
+            else
+            {
+                IConstWorkUnit *wu = wsinfo.ensureWorkUnit();
+                Owned<IWuWebView> web = createWuWebView(*wu, wsinfo.queryname.get(), getCFD(), true);
+                if (web.get())
+                    web->expandResults(roxieresp.str(), output, xmlflags);
+            }
+
         }
     }
 
@@ -2327,8 +2258,9 @@ int CWsEclBinding::onSubmitQueryOutput(IEspContext &context, CHttpRequest* reque
 
 int CWsEclBinding::onSubmitQueryOutputView(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wsinfo)
 {
-    StringBuffer soapmsg;
+    IConstWorkUnit *wu = wsinfo.ensureWorkUnit();
 
+    StringBuffer soapmsg;
     getSoapMessage(soapmsg, context, request, wsinfo, REQSF_TRIM|REQSF_ROOT, false);
     if (getEspLogLevel()>LogNormal)
         DBGLOG("submitQuery soap: %s", soapmsg.str());
@@ -2340,7 +2272,7 @@ int CWsEclBinding::onSubmitQueryOutputView(IEspContext &context, CHttpRequest* r
     StringBuffer html;
 
     SCMStringBuffer clustertype;
-    wsinfo.wu->getDebugValue("targetclustertype", clustertype);
+    wu->getDebugValue("targetclustertype", clustertype);
 
     StringBuffer xsltfile(getCFD());
     xsltfile.append("xslt/wsecl3_result.xslt");
@@ -2348,7 +2280,7 @@ int CWsEclBinding::onSubmitQueryOutputView(IEspContext &context, CHttpRequest* r
     if (strieq(clustertype.str(), "roxie"))
     {
         sendRoxieRequest(wsinfo.qsetname.get(), soapmsg, output, status, wsinfo.queryname);
-        Owned<IWuWebView> web = createWuWebView(*wsinfo.wu, wsinfo.queryname.get(), getCFD(), true);
+        Owned<IWuWebView> web = createWuWebView(*wu, wsinfo.queryname.get(), getCFD(), true);
         if (!view)
             web->applyResultsXSLT(xsltfile.str(), output.str(), html);
         else
@@ -3008,10 +2940,15 @@ int CWsEclBinding::HandleSoapRequest(CHttpRequest* request, CHttpResponse* respo
             soapresp.swapWith(output);
         else
         {
-            WsEclWuInfo wsinfo(wuid.str(), target.str(), queryname.str(), ctx->queryUserId(), ctx->queryPassword());
-            Owned<IWuWebView> web = createWuWebView(*wsinfo.wu, wsinfo.queryname.get(), getCFD(), true);
-            if (web.get())
-                web->expandResults(output.str(), soapresp, xmlflags);
+            if (xmlflags & WWV_OMIT_SCHEMAS)
+                expandWuXmlResults(soapresp, queryname.str(), output.str(), xmlflags);
+            else
+            {
+                WsEclWuInfo wsinfo(wuid.str(), target.str(), queryname.str(), ctx->queryUserId(), ctx->queryPassword());
+                Owned<IWuWebView> web = createWuWebView(*wsinfo.ensureWorkUnit(), wsinfo.queryname.get(), getCFD(), true);
+                if (web.get())
+                    web->expandResults(output.str(), soapresp, xmlflags);
+            }
         }
     }
     else

+ 1 - 2
esp/services/ws_ecl/ws_ecl_service.hpp

@@ -191,8 +191,7 @@ public:
     int getWsEclExample(CHttpRequest* request, CHttpResponse* response, const char *thepath);
 
     int getJsonTestForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wsinfo, const char *formtype);
-    void getWsEclJsonRequest(StringBuffer& soapmsg, IEspContext &context, CHttpRequest* request, WsEclWuInfo &wsinfo, const char *xmltype, const char *ns, unsigned flags);
-    void getWsEclJsonResponse(StringBuffer& jsonmsg, IEspContext &context, CHttpRequest *request, const char *xml, WsEclWuInfo &wsinfo);
+    void getWsEclJsonRequest(StringBuffer& soapmsg, IEspContext &context, CHttpRequest* request, WsEclWuInfo &wsinfo, const char *xmltype, const char *ns, unsigned flags, bool validate);
     
     void sendRoxieRequest(const char *process, StringBuffer &req, StringBuffer &resp, StringBuffer &status, const char *query, const char *contentType="text/xml");
 };

+ 28 - 12
esp/services/ws_ecl/ws_ecl_wuinfo.cpp

@@ -5,28 +5,44 @@
 WsEclWuInfo::WsEclWuInfo(const char *wuid_, const char *qset, const char *qname, const char *user, const char *pw) :
     wuid(wuid_), username(user), password(pw), qsetname(qset), queryname(qname)
 {
-    Owned<IWorkUnitFactory> wf = getWorkUnitFactory();
-    if (!wuid.length() && qset && *qset && qname && *qname)
+}
+
+const char *WsEclWuInfo::ensureWuid()
+{
+    if (wuid.length())
+        return wuid.get();
+    if (qsetname.length() && queryname.length())
     {
-        Owned<IPropertyTree> qstree = getQueryRegistry(qset, true);
+        Owned<IPropertyTree> qstree = getQueryRegistry(qsetname, true);
         if (!qstree)
-            throw MakeStringException(-1, "QuerySet %s not found", qset);
+            throw MakeStringException(-1, "QuerySet %s not found", qsetname.get());
 
-        Owned<IPropertyTree> query = resolveQueryAlias(qstree, qname);
+        Owned<IPropertyTree> query = resolveQueryAlias(qstree, queryname);
         if (!query)
-            throw MakeStringException(-1, "Query %s/%s not found", qset, qname);
+            throw MakeStringException(-1, "Query %s/%s not found", qsetname.get(), queryname.get());
         if (query->getPropBool("@suspended"))
-            throw MakeStringException(-1, "Query %s/%s is currently suspended", qset, qname);
+            throw MakeStringException(-1, "Query %s/%s is currently suspended", qsetname.get(), queryname.get());
 
         wuid.set(query->queryProp("@wuid"));
     }
     if (!wuid.length())
         throw MakeStringException(-1, "Workunit not specified");
+    return wuid.get();
+}
+
+IConstWorkUnit *WsEclWuInfo::ensureWorkUnit()
+{
+    if (!wuid.length())
+        ensureWuid();
+    if (wu)
+        return wu;
+    Owned<IWorkUnitFactory> wf = getWorkUnitFactory();
     wu.setown(wf->openWorkUnit(wuid.sget(), false));
     if (!wu)
         throw MakeStringException(-1, "Could not open workunit: %s", wuid.sget());
     if (isLibrary(wu))
-        throw MakeStringException(-1, "%s/%s is a library", qset, qname);
+        throw MakeStringException(-1, "%s/%s %s is a library", qsetname.sget(), queryname.sget(), wuid.sget());
+    return wu;
 }
 
 bool WsEclWuInfo::getWsResource(const char *name, StringBuffer &out)
@@ -34,7 +50,7 @@ bool WsEclWuInfo::getWsResource(const char *name, StringBuffer &out)
     if (strieq(name, "SOAP"))
     {
         out.appendf("<message name=\"%s\">", queryname.sget());
-        IConstWUResultIterator &vars = wu->getVariables();
+        IConstWUResultIterator &vars = ensureWorkUnit()->getVariables();
         Owned<IResultSetFactory> resultSetFactory(getResultSetFactory(username, password));
         ForEach(vars)
         {
@@ -166,13 +182,13 @@ void WsEclWuInfo::getSchemaFromResult(StringBuffer &schema, IConstWUResult &res)
 
 void WsEclWuInfo::getInputSchema(StringBuffer &schema, const char *name)
 {
-    Owned<IConstWUResult> res =  wu->getResultByName(name);
+    Owned<IConstWUResult> res =  ensureWorkUnit()->getResultByName(name);
     getSchemaFromResult(schema, *res);
 }
 
 void WsEclWuInfo::getOutputSchema(StringBuffer &schema, const char *name)
 {
-    Owned<IConstWUResult> res =  wu->getResultByName(name);
+    Owned<IConstWUResult> res =  ensureWorkUnit()->getResultByName(name);
     getSchemaFromResult(schema, *res);
 }
 
@@ -183,7 +199,7 @@ void WsEclWuInfo::updateSchemaCache()
     {
         schemacache.append("<SCHEMA>");
 
-        Owned<IConstWUResultIterator> inputs = &wu->getVariables();
+        Owned<IConstWUResultIterator> inputs = &ensureWorkUnit()->getVariables();
         addInputSchemas(schemacache, inputs, "Input");
 
         Owned<IConstWUResultIterator> results = &wu->getResults();

+ 5 - 1
esp/services/ws_ecl/ws_ecl_wuinfo.hpp

@@ -23,13 +23,14 @@
 
 class WsEclWuInfo : public CInterface
 {
-public:
+private:
     Owned<IConstWorkUnit> wu;
     Owned<IPropertyTree> paraminfo;
     Owned<IPropertyTree> xsds;
     StringBuffer schemacache;
 
     StringAttr wuid;
+public:
     StringAttr username;
     StringAttr password;
     StringAttr qsetname;
@@ -43,6 +44,9 @@ public:
     IPropertyTreeIterator *getInputSchemas();
     IPropertyTreeIterator *getResultSchemas();
 
+    const char *ensureWuid();
+    const char *queryWuid(){return wuid.get();}
+    IConstWorkUnit *ensureWorkUnit();
     void getSchemas(StringBuffer &schemas);
     void getOutputSchema(StringBuffer &schema, const char *name);
     void getInputSchema(StringBuffer &schema, const char *name);

+ 1 - 3
esp/xslt/wsecl3_form.xsl

@@ -805,9 +805,7 @@ function switchInputForm()
                     <xsl:when test="$schemaRoot/xsd:complexType[@name=$bareType]/xsd:all">
                     </xsl:when>
                     <xsl:otherwise>
-                        <xsl:if test="$type">
-                            <xsl:value-of select="concat('WARNING[1]: unknown type: ', $type, ',fieldId=',$fieldId)"/>
-                        </xsl:if>
+                        <xsl:value-of select="concat('WARNING[1]: unknown type: ', $type, ',fieldId=',$fieldId)"/>
                     </xsl:otherwise>
                 </xsl:choose>
             </xsl:when>

+ 23 - 29
system/jlib/jptree.cpp

@@ -5507,34 +5507,15 @@ static void writeJSONBase64ValueToStream(IIOStream &out, const char *val, size32
     writeCharToStream(out, '"');
 }
 
-static void checkWriteJSONCloseArrayToStream(IIOStream &out, StringAttr &prevItem, const char *name, bool format, unsigned &indent, bool &delimit)
-{
-    if (prevItem.isEmpty())
-        return;
-    if (!name || !streq(prevItem, name))
-    {
-        if (format)
-        {
-            writeCharToStream(out, '\n');
-            writeCharsNToStream(out, ' ', indent);
-            indent--;
-        }
-        writeCharToStream(out, ']');
-        prevItem.clear();
-        delimit = true;
-    }
-}
-
-static void _toJSON(const IPropertyTree *tree, IIOStream &out, unsigned indent, byte flags, bool &delimit, bool root=false)
+static void _toJSON(const IPropertyTree *tree, IIOStream &out, unsigned indent, byte flags, bool &delimit, bool root=false, bool isArrayItem=false)
 {
     Owned<IAttributeIterator> it = tree->getAttributes(true);
     bool hasAttributes = it->first();
     bool complex = (hasAttributes || tree->hasChildren());
     bool isBinary = tree->isBinary(NULL);
-    bool isItem = tree->isArrayItem();
 
     const char *name = tree->queryName();
-    if (!root && !isItem)
+    if (!root && !isArrayItem)
     {
         if (!name || !*name)
             name = "__unnamed__";
@@ -5543,7 +5524,7 @@ static void _toJSON(const IPropertyTree *tree, IIOStream &out, unsigned indent,
 
     checkWriteJSONDelimiter(out, delimit);
 
-    if (isItem && (flags & JSON_Format))
+    if (isArrayItem && (flags & JSON_Format))
     {
         writeCharToStream(out, '\n');
         writeCharsNToStream(out, ' ', indent);
@@ -5609,25 +5590,38 @@ static void _toJSON(const IPropertyTree *tree, IIOStream &out, unsigned indent,
 
     Owned<IPropertyTreeIterator> sub = tree->getElements("*", 0 != (flags & JSON_SortTags) ? iptiter_sort : iptiter_null);
     StringAttr lastArrayItem;
-    ForEach(*sub)
+    bool repeatingElement = false;
+    sub->first();
+    while(sub->isValid())
     {
         IPropertyTree &element = sub->query();
         const char *name = element.queryName();
-        checkWriteJSONCloseArrayToStream(out, lastArrayItem, name, (flags & JSON_Format), indent, delimit);
-
-        if (element.isArrayItem() && lastArrayItem.isEmpty())
+        if (sub->next() && !repeatingElement && streq(name, sub->query().queryName()))
         {
             if (flags & JSON_Format)
                 indent++;
             writeJSONNameToStream(out, name, (flags & JSON_Format) ? indent : 0, delimit);
             writeCharToStream(out, '[');
-            lastArrayItem.set(name);
+            repeatingElement = true;
+            delimit = false;
         }
 
-        _toJSON(&sub->query(), out, indent+1, flags, delimit);
+        _toJSON(&element, out, indent+1, flags, delimit, false, repeatingElement);
+
+        if (repeatingElement && (!sub->isValid() || !streq(name, sub->query().queryName())))
+        {
+            if (flags & JSON_Format)
+            {
+                writeCharToStream(out, '\n');
+                writeCharsNToStream(out, ' ', indent);
+                indent--;
+            }
+            writeCharToStream(out, ']');
+            repeatingElement = false;
+            delimit = true;
+        }
     }
 
-    checkWriteJSONCloseArrayToStream(out, lastArrayItem, NULL, (flags & JSON_Format), indent, delimit);
 
     if (!empty)
     {

+ 0 - 1
system/jlib/jptree.hpp

@@ -115,7 +115,6 @@ interface jlib_decl IPropertyTree : extends serializable
     virtual bool IsShared() const = 0;
     virtual void localizeElements(const char *xpath, bool allTail=false) = 0;
     virtual unsigned getCount(const char *xpath) = 0;
-    virtual bool isArrayItem() const = 0;
     
 private:
     void setProp(const char *, int); // dummy to catch accidental use of setProp when setPropInt() intended

+ 0 - 1
system/jlib/jptree.ipp

@@ -385,7 +385,6 @@ public:
     virtual IPropertyTreeIterator *getElements(const char *xpath, IPTIteratorCodes flags = iptiter_null) const;
     virtual void localizeElements(const char *xpath, bool allTail=false);
     virtual bool hasChildren() const { return children && children->count()?true:false; }
-    virtual bool isArrayItem() const { return parent!=NULL; }
     virtual unsigned numUniq() { return checkChildren()?children->count():0; }  
     virtual unsigned numChildren();
     virtual bool isCaseInsensitive() { return isnocase(); }