You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决XML属性转元素时XQuery报错与XSLT输出位置异常问题

问题概述

需要将XML中的id属性转换为元素,现有转换代码遇到报错、输出不符合预期的问题。

示例XML

one-attr.xml

<requestConfirmation xmlns="http://example/confirmation">
    <trade>
        <amount>
            <currency id="settlementCurrency">USD</currency>
            <referenceAmount>StandardISDA</referenceAmount>
            <cashSettlement>true</cashSettlement>
        </amount>
    </trade>
</requestConfirmation>

two-attr.xml

<requestConfirmation xmlns="http://example/confirmation">
    <trade>
        <cal>
          <c>PRECEDING</c>
            <bcs id="businessCenters">
              <bc>USNY</bc>
              <bc>GBLO</bc>
            </bcs>
        </cal>  
        <amount>
            <currency id="settlementCurrency" currencyScheme="http://example/iso4">USD</currency>
            <referenceAmount>StandardISDA</referenceAmount>
            <cashSettlement>true</cashSettlement>
        </amount>
    </trade>
</requestConfirmation>

报错信息

转换two-attr.xml时遇到如下错误:

XDMP-ATTRSEQ: (err:XQTY0024) $node/@*[fn:local-name(.) = $attr] -- Attribute node cannot follow non-attribute node in element content

期望输出

<executionReport xmlns="schema://fc.fasset/execution">
    <trade>
    <cal>
        <c>PRECEDING</c>
        <bcs>
            <id>businessCenters</id>
            <bc>USNY</bc>
            <bc>GBLO</bc>
        </bcs>
    </cal>
    <amount>
        <currency>USD</currency>        
        <id>settlementCurrency</id>
        <referenceAmount>StandardISDA</referenceAmount>
        <cashSettlement>true</cashSettlement>
    </amount>
    </trade>
</executionReport>
修正方案

XQuery修正

原XQuery报错的核心原因是remove-attr-except函数逻辑混乱,属性处理和子节点返回的顺序、位置不符合XQuery元素构造规则,同时没有区分简单文本元素和复杂元素的id放置位置。修正后代码如下:

declare namespace hof = "http://fc.fasset/function";
declare function hof:process-element
  ( $node as element(),
    $newNs as xs:string,
    $keepAttr as xs:string*
  ) as node()*
  {
    let $keep-attrs := $node/@*[local-name(.) = $keepAttr]
    let $other-attrs := $node/@*[not(local-name(.) = $keepAttr)]
    let $has-child-elements := exists($node/*)
    let $transformed-children := hof:transform-ns-root-flatten($node/node(), $newNs, $keepAttr)
    let $current-element := element {fn:QName($newNs, local-name($node))} {
        (: 非保留属性直接保留,必须放在最前面 :)
        $other-attrs,
        (: 复杂元素把保留属性转成元素放在内部最前面 :)
        if ($has-child-elements) then (
            for $attr in $keep-attrs
            return element {fn:QName($newNs, local-name($attr))} {data($attr)},
            $transformed-children
        ) else (
            $transformed-children
        )
    }
    return (
        $current-element,
        (: 简单文本元素把保留属性转成元素放在当前元素同级后面 :)
        if (not($has-child-elements)) then (
            for $attr in $keep-attrs
            return element {fn:QName($newNs, local-name($attr))} {data($attr)}
        ) else ()
    )
  };
declare function hof:transform-ns-root-flatten
  ( $nodes as node()*,
    $newNs as xs:string,
    $keepAttr as xs:string*
  ) as node()* 
  {
    for $node in $nodes
    return 
        typeswitch($node)
            case $node as element()
                return hof:process-element($node, $newNs, $keepAttr)
            case $node as document-node()
                return hof:transform-ns-root-flatten($node/node(), $newNs, fn:normalize-space($keepAttr))          
            default return $node 
  };
let $inXML := 
<requestConfirmation xmlns="http://example/confirmation">
    <trade>
      <cal>
         <c>PRECEDING</c>
            <bcs id="businessCenters">
              <bc>USNY</bc>
              <bc>GBLO</bc>
            </bcs>
        </cal>  
        <amount>
            <currency id="settlementCurrency" currencyScheme="http://example/iso4">USD</currency>
            <referenceAmount>StandardISDA</referenceAmount>
            <cashSettlement>true</cashSettlement>
        </amount>
    </trade>
</requestConfirmation>
let $input := $inXML/*/* 
let $ns := "schema://fc.fasset/execution"
let $root := "executionReport"
let $keep := "id"
return 
element { fn:QName ($ns, $root)  }
        { hof:transform-ns-root-flatten($input, $ns, $keep) }

XSLT修正

原XSLT没有区分元素类型,id一律放在当前元素内部,修正后按规则放置id即可:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
    <xsl:param name="ns" as="xs:string">schema://fc.fasset/execution</xsl:param>
    <xsl:param name="attr" static="yes" as="xs:string*" select="'href', 'id'"/>
    
    <!-- 复制节点,修改命名空间 -->
    <xsl:template match="*">
        <xsl:element name="{local-name()}" namespace="{$ns}">
            <xsl:copy-of select="@*[not(local-name() = $attr)]"/>
            <!-- 复杂元素:id放在内部最前面 -->
            <xsl:if test="exists(*)">
                <xsl:apply-templates select="@*[local-name() = $attr]"/>
            </xsl:if>
            <xsl:apply-templates/>
        </xsl:element>
        <!-- 简单文本元素:id放在同级后面 -->
        <xsl:if test="not(exists(*))">
            <xsl:apply-templates select="@*[local-name() = $attr]"/>
        </xsl:if>
    </xsl:template>
    
    <!-- 属性转元素模板 -->
    <xsl:template match="@*">
        <xsl:element name="{local-name()}" namespace="{$ns}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>
    
    <!-- 根节点处理 -->
    <xsl:template match="/requestConfirmation">
        <xsl:element name="executionReport" namespace="{$ns}">
            <xsl:apply-templates select="trade"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

内容的提问来源于stack exchange,提问作者Fiona Chen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.20 00:15:04