“属性节点不能跟随元素内容中的非属性节点”告诉我什么
What “Attribute node cannot follow non-attribute node in element content” tells me
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>
I use XQuery to transform the id
attribute into element. There are only two documents like two-attr.xml
out of 70K documents.
Apparently, the currency
element already has value USD
. I got below error in the ML QConsole when transforming two-attr.xml
. I got very similar error in Oxygen.
XDMP-ATTRSEQ: (err:XQTY0024) $node/@*[fn:local-name(.) = $attr] -- Attribute node cannot follow non-attribute node in element content
My XQuery module:
declare namespace hof = "http://fc.fasset/function";
declare function hof:remove-attr-except
( $node as node()* ,
$newNs as xs:string ,
$keepAttr as xs:string*
) as node()*
{
for $attr in $node/@*
return
if (local-name($attr) = $keepAttr)
then (element {fn:QName ($newNs, name($attr))} {data($attr)})
else
$node/@*[name() = $keepAttr], hof:transform-ns-root-flatten($node/node(), $newNs, $keepAttr)
};
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 (element { fn:QName ($newNs, local-name($node)) }
{ hof:remove-attr-except($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 := doc("/fasset/bug/two-attr.xml") :)
let $inXML :=
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/*[name() = name($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) }
Then I switch XSLT to transform two-attr.xml
. Surprisingly, the XSLT transform is a success.
<xsl:param name="ns" as="xs:string">schema://fc.fasset/product</xsl:param>
<xsl:param name="attr" static="yes" as="xs:string*" select="'href', 'id'"/>
=================================
<xsl:template match="@*">
<xsl:choose>
<xsl:when test="local-name() = $attr">
<xsl:element name="{local-name()}" namespace="{$ns}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:template>
The collective successful underlying transform is against the one-attr.xml
model. Java|ML API, Oxygen, XSLT returns the same result:
<amount>
<currency>
<id>settlementCurrency</id>USD</currency>
<referenceAmount>StandardISDA</referenceAmount>
<cashSettlement>true</cashSettlement>
</amount>
问题来了:它看起来不像一个有效的 XML。因为虽然我可以获得 currency
文本值
doc("/product/eqd/a7c1db2d.xml")//prod:trade//prod:amount/prod:currency/text()
,我希望以下结果有助于搜索引擎:
<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>
Among the following solutions, the latest result is as below:
<executionReport xmlns="schema://fc.fasset/execution">
<cal>
<c>PRECEDING</c>
<bcs>
<bc>USNY</bc>
<bc>GBLO</bc>
</bcs>
<!-- Line9: id is out of <bcs> element and its context is completed lost! -->
<id>businessCenters</id>
</cal>
<amount>
<currency>USD</currency>
<!-- Line14: id is in the correct position! -->
<id>settlementCurrency</id>
<referenceAmount>StandardISDA</referenceAmount>
<cashSettlement>true</cashSettlement>
</amount>
</executionReport>
How can I get my XQuery and XSLT module work?
您不能在开始创建子节点后创建属性。因此,如果您要将 @id
转换为 <id>
,那么您必须在复制其他属性后执行此操作。
避免该问题的最短且最简单的方法是对属性进行排序,确保先处理将向前复制的属性,然后再处理将转换为元素的属性。
您可以通过对从 hof:remove-attr-except()
函数返回的项目序列进行排序来实现这一点,确保该序列具有属性,然后是元素:
element { fn:QName ($newNs, local-name($node)) }
{ for $item in (hof:remove-attr-except($node, $newNs, $keepAttr))
order by $item instance of attribute() descending
return $item }
您也可以只使用两个带有 where
子句的单独 FLWOR,该子句处理 $keepAttr
,然后那些将被转换为元素的元素:
declare function hof:remove-attr-except
( $node as node()* ,
$newNs as xs:string ,
$keepAttr as xs:string*
) as node()*
{
for $attr in $node/@*
where not(local-name($attr) = $keepAttr)
return
$node/@*[name() = $keepAttr], hof:transform-ns-root-flatten($node/node(), $newNs, $keepAttr)
,
for $attr in $node/@*
where local-name($attr) = $keepAttr
return
element {fn:QName ($newNs, name($attr))} {data($attr)}
};
但是如果你希望那些新元素在原始元素之外,并且你不想保留属性那么我会改变你typeswitch
中元素的处理,这样你调用将这些属性转换为元素构造函数之外的元素的函数:
declare namespace hof = "http://fc.fasset/function";
declare function hof:attr-to-element
( $node as node()* ,
$newNs as xs:string ,
$keepAttr as xs:string*
) as node()*
{
for $attr in $node/@*
where local-name($attr) = $keepAttr
return
element {fn:QName ($newNs, name($attr))} {data($attr)}
};
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 (element { fn:QName ($newNs, local-name($node)) }
{ hof:transform-ns-root-flatten($node/node(), $newNs, $keepAttr) }
,
hof:attr-to-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
};
上面的代码根据提供的输入生成以下输出 XML:
<executionReport xmlns="schema://fc.fasset/execution">
<amount>
<currency>USD</currency>
<id>settlementCurrency</id>
<referenceAmount>StandardISDA</referenceAmount>
<cashSettlement>true</cashSettlement>
</amount>
</executionReport>
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>
I use XQuery to transform the
id
attribute into element. There are only two documents liketwo-attr.xml
out of 70K documents. Apparently, thecurrency
element already has valueUSD
. I got below error in the ML QConsole when transformingtwo-attr.xml
. I got very similar error in Oxygen.
XDMP-ATTRSEQ: (err:XQTY0024) $node/@*[fn:local-name(.) = $attr] -- Attribute node cannot follow non-attribute node in element content
My XQuery module:
declare namespace hof = "http://fc.fasset/function";
declare function hof:remove-attr-except
( $node as node()* ,
$newNs as xs:string ,
$keepAttr as xs:string*
) as node()*
{
for $attr in $node/@*
return
if (local-name($attr) = $keepAttr)
then (element {fn:QName ($newNs, name($attr))} {data($attr)})
else
$node/@*[name() = $keepAttr], hof:transform-ns-root-flatten($node/node(), $newNs, $keepAttr)
};
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 (element { fn:QName ($newNs, local-name($node)) }
{ hof:remove-attr-except($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 := doc("/fasset/bug/two-attr.xml") :)
let $inXML :=
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/*[name() = name($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) }
Then I switch XSLT to transform
two-attr.xml
. Surprisingly, the XSLT transform is a success.
<xsl:param name="ns" as="xs:string">schema://fc.fasset/product</xsl:param>
<xsl:param name="attr" static="yes" as="xs:string*" select="'href', 'id'"/>
=================================
<xsl:template match="@*">
<xsl:choose>
<xsl:when test="local-name() = $attr">
<xsl:element name="{local-name()}" namespace="{$ns}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:template>
The collective successful underlying transform is against the
one-attr.xml
model. Java|ML API, Oxygen, XSLT returns the same result:
<amount>
<currency>
<id>settlementCurrency</id>USD</currency>
<referenceAmount>StandardISDA</referenceAmount>
<cashSettlement>true</cashSettlement>
</amount>
问题来了:它看起来不像一个有效的 XML。因为虽然我可以获得 currency
文本值
doc("/product/eqd/a7c1db2d.xml")//prod:trade//prod:amount/prod:currency/text()
,我希望以下结果有助于搜索引擎:
<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>
Among the following solutions, the latest result is as below:
<executionReport xmlns="schema://fc.fasset/execution">
<cal>
<c>PRECEDING</c>
<bcs>
<bc>USNY</bc>
<bc>GBLO</bc>
</bcs>
<!-- Line9: id is out of <bcs> element and its context is completed lost! -->
<id>businessCenters</id>
</cal>
<amount>
<currency>USD</currency>
<!-- Line14: id is in the correct position! -->
<id>settlementCurrency</id>
<referenceAmount>StandardISDA</referenceAmount>
<cashSettlement>true</cashSettlement>
</amount>
</executionReport>
How can I get my XQuery and XSLT module work?
您不能在开始创建子节点后创建属性。因此,如果您要将 @id
转换为 <id>
,那么您必须在复制其他属性后执行此操作。
避免该问题的最短且最简单的方法是对属性进行排序,确保先处理将向前复制的属性,然后再处理将转换为元素的属性。
您可以通过对从 hof:remove-attr-except()
函数返回的项目序列进行排序来实现这一点,确保该序列具有属性,然后是元素:
element { fn:QName ($newNs, local-name($node)) }
{ for $item in (hof:remove-attr-except($node, $newNs, $keepAttr))
order by $item instance of attribute() descending
return $item }
您也可以只使用两个带有 where
子句的单独 FLWOR,该子句处理 $keepAttr
,然后那些将被转换为元素的元素:
declare function hof:remove-attr-except
( $node as node()* ,
$newNs as xs:string ,
$keepAttr as xs:string*
) as node()*
{
for $attr in $node/@*
where not(local-name($attr) = $keepAttr)
return
$node/@*[name() = $keepAttr], hof:transform-ns-root-flatten($node/node(), $newNs, $keepAttr)
,
for $attr in $node/@*
where local-name($attr) = $keepAttr
return
element {fn:QName ($newNs, name($attr))} {data($attr)}
};
但是如果你希望那些新元素在原始元素之外,并且你不想保留属性那么我会改变你typeswitch
中元素的处理,这样你调用将这些属性转换为元素构造函数之外的元素的函数:
declare namespace hof = "http://fc.fasset/function";
declare function hof:attr-to-element
( $node as node()* ,
$newNs as xs:string ,
$keepAttr as xs:string*
) as node()*
{
for $attr in $node/@*
where local-name($attr) = $keepAttr
return
element {fn:QName ($newNs, name($attr))} {data($attr)}
};
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 (element { fn:QName ($newNs, local-name($node)) }
{ hof:transform-ns-root-flatten($node/node(), $newNs, $keepAttr) }
,
hof:attr-to-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
};
上面的代码根据提供的输入生成以下输出 XML:
<executionReport xmlns="schema://fc.fasset/execution">
<amount>
<currency>USD</currency>
<id>settlementCurrency</id>
<referenceAmount>StandardISDA</referenceAmount>
<cashSettlement>true</cashSettlement>
</amount>
</executionReport>