浏览代码

gh-300 CResultSetCursor::getXmlText not respecting xpath

Getting XML from workunit did not conform to format specified by
XPATH specifiers in ECL.  Updated fileview2 to make use of the
XPATH information when building xml from results.

Fixes gh-300

Signed-off-by: Anthony Fishbeck <Anthony.Fishbeck@lexisnexis.com>
Anthony Fishbeck 13 年之前
父节点
当前提交
4acf16b2e6

+ 1 - 0
common/fileview2/fileview.hpp

@@ -157,6 +157,7 @@ interface IResultSetCursor : extends IInterface
     virtual IStringVal & getDisplayText(IStringVal & ret, int columnIndex) = 0;
     virtual IStringVal & getXml(IStringVal & ret, int columnIndex) = 0;
     virtual IStringVal & getXmlRow(IStringVal & ret) = 0;
+    virtual IStringVal & getXmlItem(IStringVal & ret) = 0;
     virtual __int64 getNumRows() const = 0;
 };
 

+ 3 - 0
common/fileview2/fvdatasource.hpp

@@ -49,6 +49,9 @@ interface IFvDataSourceMetaData : extends IInterface
     virtual unsigned numKeyedColumns() const = 0;
     
     inline bool isVirtual(unsigned column) const { return queryFieldFlags(column) == FVFFvirtual; }
+    virtual const char *queryXmlTag(unsigned column) const = 0;
+    virtual const char *queryXmlTag() const = 0;
+    virtual const IntArray &queryAttrList() = 0;
 };
 
 IFvDataSourceMetaData * deserializeDataSourceMeta(MemoryBuffer & in);

+ 156 - 13
common/fileview2/fvresultset.cpp

@@ -1508,12 +1508,12 @@ IStringVal & CResultSetCursor::getDisplayText(IStringVal &ret, int columnIndex)
 }
 
 
-void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex)
+void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex, const char *tag)
 {
     if (!isValid())
         return;
 
-    const char * name = meta.meta->queryName(columnIndex);
+    const char * name = (tag) ? tag : meta.meta->queryXmlTag(columnIndex);
     CResultSetColumnInfo & column = meta.columns.item(columnIndex);
     unsigned flags = column.flag;
     switch (flags)
@@ -1522,10 +1522,12 @@ void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex)
     case FVFFendif:
         return;
     case FVFFbeginrecord:
-        out.append("<").append(name).append(">");
+        if (name && *name)
+            out.append("<").append(name).append(">");
         return;
     case FVFFendrecord:
-        out.append("</").append(name).append(">");
+        if (name && *name)
+            out.append("</").append(name).append(">");
         return;
     }
 
@@ -1621,7 +1623,8 @@ void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex)
     case type_table:
     case type_groupedtable:
         {
-            out.append("<").append(name).append(">");
+            if (name && *name)
+                out.append("<").append(name).append(">");
             Owned<IResultSetCursor> childCursor = getChildren(columnIndex);
             
             ForEach(*childCursor)
@@ -1630,12 +1633,14 @@ void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex)
                 childCursor->getXmlRow(adaptor);
             }
 
-            out.append("</").append(name).append(">");
+            if (name && *name)
+                out.append("</").append(name).append(">");
             break;
         }
     case type_set:
         {
-            out.append("<").append(name).append(">");
+            if (name && *name)
+                out.append("<").append(name).append(">");
             if (getIsAll(columnIndex))
                 outputXmlSetAll(out);
             else
@@ -1644,16 +1649,132 @@ void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex)
                 
                 ForEach(*childCursor)
                 {
-                    out.append("<Item>");
                     StringBufferAdaptor adaptor(out);
-                    childCursor->getDisplayText(adaptor, 0);
-                    out.append("</Item>");
+                    childCursor->getXmlItem(adaptor);
                 }
             }
+            if (name && *name)
+                out.append("</").append(name).append(">");
+            break;
+        }
+    default:
+        UNIMPLEMENTED;
+    }
+    rtlFree(resultStr);
+}
 
-            out.append("</").append(name).append(">");
+void CResultSetCursor::getXmlAttrText(StringBuffer & out, int columnIndex, const char *tag)
+{
+    if (!isValid())
+        return;
+
+    const char * name = (tag) ? tag : meta.meta->queryXmlTag(columnIndex);
+    if (name && *name=='@')
+        name++;
+    CResultSetColumnInfo & column = meta.columns.item(columnIndex);
+    unsigned flags = column.flag;
+    switch (flags)
+    {
+    case FVFFbeginif:
+    case FVFFendif:
+    case FVFFbeginrecord:
+    case FVFFendrecord:
+        return;
+    }
+
+    const byte * cur = getColumn(columnIndex);
+    unsigned resultLen;
+    char * resultStr = NULL;
+
+    ITypeInfo & type = *column.type;
+    unsigned size = type.getSize();
+    unsigned len = UNKNOWN_LENGTH;
+    switch (type.getTypeCode())
+    {
+    case type_boolean:
+        outputXmlAttrBool((*((byte *)cur)) != 0, name, out);
+        break;
+    case type_int:
+        {
+            __int64 value = getIntFromInt(type, cur, isMappedIndexField(columnIndex));
+            if (type.isSigned())
+                outputXmlAttrInt((__int64) value, name, out);
+            else
+                outputXmlAttrUInt((unsigned __int64) value, name, out);
             break;
         }
+    case type_swapint:
+        {
+            __int64 value = getIntFromSwapInt(type, cur, isMappedIndexField(columnIndex));
+            if (type.isSigned())
+                outputXmlAttrInt((__int64) value, name, out);
+            else
+                outputXmlAttrUInt((unsigned __int64) value, name, out);
+            break;
+        }
+    case type_packedint:
+        {
+            if (type.isSigned())
+                outputXmlAttrInt(rtlGetPackedSigned(cur), name, out);
+            else
+                outputXmlAttrUInt(rtlGetPackedUnsigned(cur), name, out);
+            break;
+        }
+    case type_decimal:
+        if (type.isSigned())
+            outputXmlAttrDecimal(cur, size, type.getPrecision(), name, out);
+        else
+            outputXmlAttrUDecimal(cur, size, type.getPrecision(), name, out);
+        break;
+    case type_real:
+        if (size == 4)
+            outputXmlAttrReal(*(float *)cur, name, out);
+        else
+            outputXmlAttrReal(*(double *)cur, name, out);
+        break;
+    case type_qstring:
+        len = getLength(type, cur);
+        rtlQStrToStrX(resultLen, resultStr, len, (const char *)cur);
+        outputXmlAttrString(resultLen, resultStr, name, out);
+        break;
+    case type_data:
+        len = getLength(type, cur);
+        outputXmlAttrData(len, cur, name, out);
+        break;
+    case type_string:
+        len = getLength(type, cur);
+        if (meta.isEBCDIC(columnIndex))
+        {
+            rtlStrToEStrX(resultLen, resultStr, len, (const char *)cur);
+            outputXmlAttrString(resultLen, resultStr, name, out);
+        }
+        else
+            outputXmlAttrString(len, (const char *)cur, name, out);
+        break;
+    case type_unicode:
+        len = getLength(type, cur);
+        outputXmlAttrUnicode(len, (UChar const *)cur, name, out);
+        break;
+    case type_varstring:
+        if (meta.isEBCDIC(columnIndex))
+        {
+            rtlStrToEStrX(resultLen, resultStr, strlen((const char *)cur), (const char *)cur);
+            outputXmlAttrString(resultLen, resultStr, name, out);
+        }
+        else
+            outputXmlAttrString(strlen((const char *)cur), (const char *)cur, name, out);
+        break;
+    case type_varunicode:
+        outputXmlAttrUnicode(rtlUnicodeStrlen((UChar const *)cur), (UChar const *)cur, name, out);
+        break;
+    case type_utf8:
+        len = getLength(type, cur);
+        outputXmlAttrUtf8(len, (const char *)cur, name, out);
+        break;
+    case type_table:
+    case type_groupedtable:
+    case type_set:
+        break;
     default:
         UNIMPLEMENTED;
     }
@@ -1668,16 +1789,33 @@ IStringVal & CResultSetCursor::getXml(IStringVal &ret, int columnIndex)
     return ret;
 }
 
+IStringVal & CResultSetCursor::getXmlItem(IStringVal & ret)
+{
+    StringBuffer temp;
+    getXmlText(temp, 0, meta.meta->queryXmlTag());
+    ret.set(temp.str());
+    return ret;
+}
+
 //More efficient than above...
 IStringVal & CResultSetCursor::getXmlRow(IStringVal &ret)
 {
     StringBuffer temp;
-    temp.append("<Row>");
+    const char *rowtag = meta.meta->queryXmlTag();
+    if (rowtag && *rowtag)
+        temp.append('<').append(rowtag);
+    const IntArray &attributes = meta.meta->queryAttrList();
+    ForEachItemIn(ac, attributes)
+        getXmlAttrText(temp, attributes.item(ac));
+    temp.append('>');
     unsigned numColumns = meta.getColumnCount();
     unsigned ignoreNesting = 0;
     for (unsigned col = 0; col < numColumns; col++)
     {
         unsigned flags = meta.columns.item(col).flag;
+        const char *tag = meta.meta->queryXmlTag(col);
+        if (tag && *tag=='@')
+            continue;
         switch (flags)
         {
         case FVFFbeginif:
@@ -1710,7 +1848,8 @@ IStringVal & CResultSetCursor::getXmlRow(IStringVal &ret)
         }
     }
     assertex(ignoreNesting == 0);
-    temp.append("</Row>");
+    if (rowtag && *rowtag)
+        temp.append("</").append(rowtag).append('>');
     ret.set(temp.str());
     return ret;
 }
@@ -1957,6 +2096,10 @@ IStringVal & IndirectResultSetCursor::getXmlRow(IStringVal &ret)
 {
     return queryBase()->getXmlRow(ret);
 }
+IStringVal & IndirectResultSetCursor::getXmlItem(IStringVal &ret)
+{
+    return queryBase()->getXmlItem(ret);
+}
 void IndirectResultSetCursor::noteRelatedFileChanged()
 {
     queryBase()->noteRelatedFileChanged();

+ 6 - 1
common/fileview2/fvresultset.ipp

@@ -368,6 +368,9 @@ public:
     virtual IStringVal & getDisplayText(IStringVal &ret, int columnIndex);
     virtual IStringVal & getXml(IStringVal & ret, int columnIndex);
     virtual IStringVal & getXmlRow(IStringVal &ret);
+    virtual IStringVal & getXmlItem(IStringVal & ret);
+
+
 
 //IResultSetCursor
     virtual void beginAccess();
@@ -381,7 +384,8 @@ protected:
     void init(IExtendedNewResultSet * _resultSet);
     bool isMappedIndexField(unsigned columnIndex) { return resultSet->isMappedIndexField(columnIndex); }
     const byte * getColumn(unsigned idx) const      { return (const byte *)curRowData.toByteArray() + offsets[idx]; }
-    void getXmlText(StringBuffer & out, int columnIndex);
+    void getXmlText(StringBuffer & out, int columnIndex, const char *tag=NULL);
+    void getXmlAttrText(StringBuffer & out, int columnIndex, const char *tag=NULL);
 
     virtual __int64 getCurRow() const;
     virtual __int64 translateRow(__int64 row) const;
@@ -436,6 +440,7 @@ public:
     virtual IStringVal & getDisplayText(IStringVal &ret, int columnIndex);
     virtual IStringVal & getXml(IStringVal & ret, int columnIndex);
     virtual IStringVal & getXmlRow(IStringVal &ret);
+    virtual IStringVal & getXmlItem(IStringVal &ret);
     virtual void noteRelatedFileChanged();
 
 protected:

+ 83 - 1
common/fileview2/fvsource.cpp

@@ -34,12 +34,48 @@
 
 #define MAX_RECORD_SIZE         4096
 
+inline void appendNextXpathName(StringBuffer &s, const char *xpath, const char *&next)
+{
+    while (*xpath && !strchr("*[/", *xpath))
+        s.append(*xpath++);
+    next = strchr(xpath, '/');
+}
+
+void splitXmlTagNamesFromXPath(const char *xpath, StringBuffer &inner, StringBuffer *outer=NULL)
+{
+    if (!xpath || !xpath)
+        return;
+
+    StringBuffer s1;
+    StringBuffer s2;
+
+    const char *next=xpath;
+    appendNextXpathName(s1, xpath, next);
+    if (outer && next)
+        appendNextXpathName(s2, ++next, next);
+    if (next) //xpath too deep
+        return;
+
+    if (!s2.length())
+        inner.swapWith(s1);
+    else
+    {
+        inner.swapWith(s2);
+        outer->swapWith(s1);
+    }
+    if (!inner.length())
+        inner.set("/");
+    if (outer && !outer->length())
+        outer->set("/");
+}
+
 DataSourceMetaItem::DataSourceMetaItem(unsigned _flags, const char * _name, const char * _xpath, ITypeInfo * _type)
 {
     flags = _flags;
     name.set(_name);
     type.set(_type);
     xpath.set(_xpath);
+    splitXmlTagNamesFromXPath(_xpath, tagname);
 }
 
 DataSourceMetaItem::DataSourceMetaItem(unsigned _flags, MemoryBuffer & in)
@@ -48,6 +84,7 @@ DataSourceMetaItem::DataSourceMetaItem(unsigned _flags, MemoryBuffer & in)
     in.read(name);
     in.read(xpath);
     type.setown(deserializeType(in));
+    splitXmlTagNamesFromXPath(xpath.get(), tagname);
 }
 
 
@@ -66,6 +103,9 @@ DataSourceDatasetItem::DataSourceDatasetItem(const char * _name, const char * _x
     type.setown(makeTableType(NULL, NULL, NULL, NULL));
     name.set(_name);
     xpath.set(_xpath);
+    splitXmlTagNamesFromXPath(_xpath, record.tagname, &tagname);
+    if (!record.tagname.length())
+        record.tagname.set("Row");
 }
 
 DataSourceDatasetItem::DataSourceDatasetItem(unsigned flags, MemoryBuffer & in) : DataSourceMetaItem(FVFFdataset, NULL, NULL, NULL), record(in)
@@ -83,14 +123,23 @@ void DataSourceDatasetItem::serialize(MemoryBuffer & out) const
 
 //---------------------------------------------------------------------------
 
-DataSourceSetItem::DataSourceSetItem(const char * _name, const char * _xpath, ITypeInfo * _type) : DataSourceMetaItem(FVFFset, _name, _xpath, _type)
+DataSourceSetItem::DataSourceSetItem(const char * _name, const char * _xpath, ITypeInfo * _type) : DataSourceMetaItem(FVFFset, _name, NULL, _type)
 {
     createChild();
+    xpath.set(_xpath);
+    StringBuffer attr;
+    splitXmlTagNamesFromXPath(_xpath, record.tagname, &tagname);
+    if (!record.tagname.length())
+        record.tagname.set("Item");
 }
 
 DataSourceSetItem::DataSourceSetItem(unsigned flags, MemoryBuffer & in) : DataSourceMetaItem(flags, in)
 {
     createChild();
+    StringBuffer attr;
+    splitXmlTagNamesFromXPath(xpath.get(), record.tagname, &tagname.clear());
+    if (!record.tagname.length())
+        record.tagname.set("Item");
 }
 
 void DataSourceSetItem::createChild()
@@ -160,10 +209,12 @@ void DataSourceMetaData::init()
     isStoredFixedWidth = false;
     randomIsOk = false;
     numFieldsToIgnore = 0;
+    attrset = false;
 }
 
 DataSourceMetaData::DataSourceMetaData(MemoryBuffer & buffer)
 {
+    attrset = false;
     numVirtualFields = 0;
     buffer.read(numFieldsToIgnore);
     buffer.read(randomIsOk);
@@ -391,6 +442,37 @@ const char * DataSourceMetaData::queryXPath(unsigned column) const
     return fields.item(column).xpath;
 }
 
+const char * DataSourceMetaData::queryXmlTag(unsigned column) const
+{
+    DataSourceMetaItem &item = fields.item(column);
+    if (item.tagname.length())
+        return (*item.tagname.str()!='/') ? item.tagname.str() : NULL;
+    return item.name.get();
+}
+
+const char *DataSourceMetaData::queryXmlTag() const
+{
+    if (tagname.length())
+        return (*tagname.str()!='/') ? tagname.str() : NULL;
+    return "Row";
+}
+
+const IntArray &DataSourceMetaData::queryAttrList()
+{
+    if (!attrset)
+    {
+        ForEachItemIn(idx, fields)
+        {
+            DataSourceMetaItem &item = fields.item(idx);
+            if (*item.tagname.str()=='@')
+                attributes.append(idx);
+        }
+        attrset=true;
+    }
+    return attributes;
+}
+
+
 IFvDataSourceMetaData * DataSourceMetaData::queryChildMeta(unsigned column) const
 {
     return fields.item(column).queryChildMeta();

+ 10 - 0
common/fileview2/fvsource.ipp

@@ -79,6 +79,7 @@ public:
 public:
     StringAttr  name;
     StringAttr  xpath;
+    StringBuffer tagname;
     OwnedITypeInfo type;
     byte           flags;
 };
@@ -100,6 +101,10 @@ public:
     virtual bool supportsRandomSeek() const;
     virtual void serialize(MemoryBuffer & out) const;
     virtual unsigned queryFieldFlags(unsigned column) const;
+    virtual const char *queryXmlTag(unsigned column) const;
+    virtual const char *queryXmlTag() const;
+    virtual const IntArray &queryAttrList();
+
     virtual IFvDataSourceMetaData * queryChildMeta(unsigned column) const;
     virtual IFvDataSource * createChildDataSource(unsigned column, unsigned len, const void * data);
     virtual unsigned numKeyedColumns() const;
@@ -139,6 +144,11 @@ protected:
     bool isStoredFixedWidth;
     bool randomIsOk;
     byte numFieldsToIgnore;
+    IntArray attributes;
+    bool attrset;
+
+public:
+    StringBuffer tagname;
 };
 
 

+ 1 - 1
esp/services/ws_ecl/ws_ecl_wuinfo.cpp

@@ -181,7 +181,7 @@ void WsEclWuInfo::getSchemaFromResult(StringBuffer &schema, IConstWUResult &res)
         
         Owned<IResultSetFactory> resultSetFactory(getResultSetFactory(username, password));
         Owned<IResultSetMetaData> meta = resultSetFactory->createResultSetMeta(&res);
-        meta->getXmlSchema(s, true);
+        meta->getXmlXPathSchema(s, true);
         const char *finger=schema.str();
         while (finger && strncmp(finger, "<xs:schema", 10))
             finger++;