【问题标题】:Return element name and value as key value pair将元素名称和值作为键值对返回
【发布时间】:2014-06-09 20:55:40
【问题描述】:

我正在处理一个要求,我需要使用 XQUERY 将元素名称和值作为键值对返回,如下所示。

[code=123,px_last=第一个数据的属性值,last_update=第二个数据的属性值,以此类推]

里面有7个数据元素的属性值应该像上面那样读取,第一个字段映射到第一个数据,第二个映射到第二个数据值属性。 等等。

在您的帮助下,我能够生成输出,但在需要将第一个字段元素映射到第一个数据属性值等等的地方遇到了问题。

提前致谢

XML 文件:

<root>
<fields>
    <field>PX_LAST</field>
    <field>LAST_UPDATE</field>
    <field>LAST_UPDATE_DT</field>
    <field>SECURITY_DES</field>
    <field>FUT_CUR_GEN_TICKER</field>
    <field>YLD_CNV_BID</field>
    <field>YLD_CNV_ASK</field>
</fields>
<Datas>
    <Data>
        <code>0</code>
        <ins>
            <id>CT30</id>
            <key>Govt</key>
        <ins/>
        <data value="98.843750"/>
        <data value="16:14:45">
        </data>
        <data value="06/03/2014"/>
        <data value="T 3 3/8 05/15/44"/>
        <data value=""/>
        <data value="3.439"/>
        <data value="3.437"/>
    </Data>
    <Data>
        <code>0</code>
        <ins>
            <id>US0001W</id>
            <key>Index</key>
        <ins/>
        <data value=".119000"/>
        <data value="06:46"/>
        <data value="06/03/2014"/>
        <data value="ICE LIBOR USD 1 Week"/>
        <data value=""/>
        <data value="N.A."/>
        <datavalue=".11900"></data>
    </Data>
</Datas>
</root>

XQuery:

declare function xf:strip-namespace($e as element())
as element()
{
element { xs:QName(local-name($e)) }
{
  for $child in $e/(@*,node())
   return
 if ($child instance of element())
 then
   xf:strip-namespace($child)
 else 
   $child
 }
};

let $nl := "&#10;"

let $count := 0

for $x in       doc("test.xml")/soap:Envelope/soap:Body/dlws:retrieveGetDataResponse/dlws:instrumentDatas//*
let $y:=xf:strip-namespace($x)
return
if($y/name() = 'instrumentData')
then
concat($nl,'[','')
else if($y/name()='data')
then
  concat($y/name(),'=',$y/data(@value),',')
else if($y/name() != 'instrument')
then
  concat($y/name(),'=',$y/text(),',')
else
()

立即输出:

[code=123,data=werr,data="qwe",data="wer",......,] [code=456,data=rty,data="tyuu",data="uuu",......,]

【问题讨论】:

    标签: xquery xquery-3.0


    【解决方案1】:

    通常,如果您可以将问题分解成更小的部分,这将有助于更简单的解决方案,更接近问题本身。

    declare function local:make-pair(
      $e as element()
    ) as xs:string?
    {
      typeswitch($e)
        case element(data) return concat(local-name($e), '=', $e/@value)
        default return concat(local-name($e), '=', $e)
    };
    
    let $idatas :=
    <idatas>
     <idata>
        <code>123</code>
        <data value="wer"></data>
        <data value="sdf"></data>
        <data value="zxc"></data>
        <data value="asd"></data>
        <data value="jgh"></data>
        <data value="cvb"></data>
        <data value="bsz"></data>
     </idata>
    
     <idata>
        <code>345</code>
        <data value="ff"></data>
        <data value="zxd"></data>
        <data value="wvver"></data>
        <data value="wencvr"></data>
        <data value="wzxcer"></data>
        <data value="wmmer"></data>
        <data value="wuuer"></data>
     </idata>
    </idatas>
    for $idata in $idatas/idata
    let $pairs := 
      for $p in $idata/*
      return local:make-pair($p)
    return concat('[', string-join($pairs, ','), ']')
    

    【讨论】:

      【解决方案2】:

      在答案的以下部分中,我完全忽略了strip-namespace 部分,无论如何这都是个坏主意。 Eighter 将其声明为默认命名空间,不用再操心了,或者使用local-name() 代替名称,或者使用通配符命名空间mather*:elementname*


      在更新问题期间修改了输入。直到下一个水平条的所有内容均指问题的第一次修订。

      您可以使用 XQuery 3.0 的一些特性用很少的代码完成所有“字符串操作 foo”,尤其是在轴步中调用函数和字符串连接运算符||

      //idata/(                      (: for all idata elements :)
        "[" ||
        string-join((                (: combine all key/value pairs with commata :)
          "code=" || code/data(),    (: code header :)
          data/("data=" || @value)), (: data fields :)
        ',') ||
        ']')
      

      而且它完全适合 Stack Overflow 上的一行(如果你真的想要的话)!

      //idata/("["||string-join(("code="||code/data(),data/("data="||@value)),',')||']')
      

      输出是

      [code=123,data=wer,data=sdf,data=zxc,data=asd,data=jgh,data=cvb,data=bsz] [code=345,data=ff,data=zxd,data =wvver,data=wencvr,data=wzxcer,data=wmmer,data=wuuer]

      一个可能更易读的版本,带有显式循环,仍然使用连接运算符(我认为这增强了可读性):

      for $idata in $xml//idata
      return
        "[" || string-join((
          "code=" || $idata/code/data(),
          for $data in $idata/data
          return
            "data=" || $data/@value),
        ',') || ']'
      

      对于更新的问题,单行可能会变得难以阅读。最后修改的代码只是与数据元素的索引连接:

      for $dataset in /root/Datas/Data
      return
        "[" || string-join((
          "code=" || $dataset/code/data(),
          for $data at $position in $dataset/data 
          let $field := /root/fields/field[$position]
          return
            $field || "=" || $data/@value),
        ',') || ']'
      

      【讨论】:

      • 非常感谢。我会听从您的建议的。
      • 我能够按照上面的要求获取数据,并感谢你们,但现在变得越来越复杂,因为要求如下映射字段 [code=123,px_last=abc, last_update=...] [code=4656,px_last=ttr,last_update=...] 属性值为 insde 的数据元素有 7 个,应读取为第一个字段名称=第一个数据属性值,依此类推7 个文件,即每个数据的 px_last=wer,last_update=sdf 等等。再次感谢您的帮助
      • 请发布实际代表您的问题的数据(通过编辑您的问题)以及该输入的预期输出。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-07
      • 1970-01-01
      • 2014-08-19
      • 1970-01-01
      • 2020-08-17
      • 1970-01-01
      相关资源
      最近更新 更多