0

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

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

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

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

提前致谢

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>

查询:

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", ……,]

4

2 回答 2

2

一般来说,如果你能把问题分解成更小的部分,它会促进更简单的解决方案,更接近问题本身。

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, ','), ']')
于 2014-06-09T21:08:47.963 回答
2

在答案的以下部分中,我完全忽略了该strip-namespace部分,无论如何这是一个坏主意。Eighter 将其声明为默认命名空间,不再担心它,或者使用它local-name()来代替 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,数据=wencvr,数据=wzxcer,数据=wmmer,数据=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),
  ',') || ']'
于 2014-06-09T21:36:03.173 回答