XML 转换删除根节点名称空间声明的 MarkLogic SwitchCase 代码
MarkLogic SwitchCase code for XML transformation removing root node namespace declarations
我有如下 XML 结构(示例 XML)。我想更改 img 标签值并将它们更改为相对 path.Each XML 文档可以包含超过 100 个 img 标签。我的代码大部分都有效,除了我在根节点中的名称空间声明被添加到引用名称空间的各个节点,我不希望这种情况发生。
示例 XML:
<?xml version="1.0" encoding="UTF-8"?>
<test:document transformVersion="0.0.25" xmlns:test="test.data" xmlns:csp="test.csp" xmlns:cfg="test.cfg">
<data>
<table>
<tr>
<td>
<p class="DataboxTitle">Test Image</p>
</td>
<td>
<p>
<img src="https://www.google.com/images/document/1234.jpg" data-stl="height:107px;width:223px;" srcset="https://www.google.com/images/document/1234.jpg 320w" data-image-title="Test Image"/>
</p>
</td>
</tr>
</table>
<table>
<tr>
<td>
<p class="DataboxTitle">Test Image</p>
</td>
<td>
<p>
<img src="https://www.google.com/images/document/1234.jpg" data-stl="height:107px;width:223px;" srcset="https://www.google.com/images/document/1234.jpg 320w" data-image-title="Test Image"/>
</p>
</td>
</tr>
</table>
<table>
<tr>
<td>
<p class="DataboxTitle">Test Image</p>
</td>
<td>
<p>
<img src="https://www.google.com/images/document/1234.jpg" data-stl="height:107px;width:223px;" srcset="https://www.google.com/images/document/1234.jpg 320w" data-image-title="Test Image"/>
</p>
</td>
</tr>
</table>
<p>
<img src="https://www.google.com/images/document/1234.jpg" data-stl="height:107px;width:223px;" srcset="https://www.google.com/images/document/1234.jpg 320w" data-image-title="Test Image"/>
</p>
</data>
<test:volumes>
<test:test>test</test:test>
</test:volumes>
<csp:volumes>tested</csp:volumes>
</test:document>
在我下面的 SwitchCase 代码中,我知道由于 $doc 变量声明中的代码“/*”,命名空间被单独添加,但如果没有它,我将需要一个 for each 循环,这会搞砸其他逻辑.我只想更改 img 标签属性值而不更改输出中的任何其他内容 xml,请提出出路。
X查询代码:
xquery version "1.0-ml";
declare variable $doc := fn:doc("/c/temp/test.xml")/*;
declare variable $docId := '1928862612025434112';
declare function local:createTag($docId,$imagePath)
{
let $srcOld := fn:data($imagePath/@src)
let $imageName := fn:tokenize($srcOld,"/")[fn:last()]
return fn:concat("/",$docId,"/media/",$imageName)
};
declare function local:createSrcSetTag($docId,$imagePath)
{
let $srcSetsOld := fn:data($imagePath/@srcset)
for $srcSet in fn:tokenize($srcSetsOld, ",")
let $imageLength := fn:tokenize($srcSet," ")[fn:last()]
let $imageName := fn:tokenize($srcSet,"/")[fn:last()]
let $imageNewPath := fn:concat("/",$docId,"/media/",$imageName)
return fn:concat($imageNewPath,"," )
};
declare function local:change($node as node()*) as node()*
{
typeswitch($node)
case element(img) return
element { xs:QName(fn:local-name($node)) }
{
let $image := $node
let $path := xdmp:path($image)
let $data-stl := fn:data($image/@data-stl)
let $data-image-title := fn:data($image/@data-image-title)
let $srcOld := fn:data($image/@src)
let $srcSetsOld := fn:data($image/@srcset)
let $srcNew := local:createTag($docId,$image)
let $srcSetsNew := local:createSrcSetTag($docId,$image)
return (attribute {'src'}{$srcNew}, attribute {'data-stl'}{$data-stl},attribute {'srcset'}{$srcSetsNew}, attribute {'data-image-title'}{$data-image-title} )
}
case element() return
element {fn:node-name($node) } {
$node/@*,
$node/node() ! local:change(.)
}
default return $node
};
local:change(($doc))
这是我转换后的输出:
<test:document transformVersion="0.0.25" xmlns:test="test.data">
<data>
<table>
<tr>
<td>
<p class="DataboxTitle">Test Image</p>
</td>
<td>
<p>
<img src="/1928862612025434112/media/1234.jpg" data-stl="height:107px;width:223px;" srcset="/1928862612025434112/media/1234.jpg 320w," data-image-title="Test Image"/>
</p>
</td>
</tr>
</table>
<table>
<tr>
<td>
<p class="DataboxTitle">Test Image</p>
</td>
<td>
<p>
<img src="/1928862612025434112/media/1234.jpg" data-stl="height:107px;width:223px;" srcset="/1928862612025434112/media/1234.jpg 320w," data-image-title="Test Image"/>
</p>
</td>
</tr>
</table>
<table>
<tr>
<td>
<p class="DataboxTitle">Test Image</p>
</td>
<td>
<p>
<img src="/1928862612025434112/media/1234.jpg" data-stl="height:107px;width:223px;" srcset="/1928862612025434112/media/1234.jpg 320w," data-image-title="Test Image"/>
</p>
</td>
</tr>
</table>
<p>
<img src="/1928862612025434112/media/1234.jpg" data-stl="height:107px;width:223px;" srcset="/1928862612025434112/media/1234.jpg 320w," data-image-title="Test Image"/>
</p>
</data>
<test:volumes>
<test:test>test</test:test>
</test:volumes>
<csp:volumes xmlns:csp="test.csp">tested</csp:volumes>
在出现的元素上保留名称空间声明并不难,只需确保将它们与属性和元素一起复制。为此,您可以使用 $node/namespace::*
:
declare function local:change($node as node()*) as node()*
{
typeswitch($node)
case element(img) return
element { xs:QName(fn:local-name($node)) }
{
let $image := $node
let $path := xdmp:path($image)
let $data-stl := fn:data($image/@data-stl)
let $data-image-title := fn:data($image/@data-image-title)
let $srcOld := fn:data($image/@src)
let $srcSetsOld := fn:data($image/@srcset)
let $srcNew := local:createTag($docId,$image)
let $srcSetsNew := local:createSrcSetTag($docId,$image)
return (attribute {'src'}{$srcNew}, attribute {'data-stl'}{$data-stl},attribute {'srcset'}{$srcSetsNew}, attribute {'data-image-title'}{$data-image-title} )
}
case element() return
element {fn:node-name($node) } {
$node/namespace::*,
$node/@*,
$node/node() ! local:change(.)
}
default return $node
};
HTH!
您发布的实现也是测试其他代码的一个很好的例子 - 一般* XML 处理代码不应依赖于命名空间的特定声明样式,而应仅依赖于内部 XDM 中的命名空间属性模型。如果代码仅由于格式更改而中断,那将是一个很好的单元测试注入以查找和修复此类代码。
** 当然,代码明确依赖于格式或命名空间声明差异是有原因的,例如实现上述单元测试的代码——它需要明确控制结果格式以及语义检测相同的格式差异,与外部服务交互,或者特别乏味,从 XML 中提取子节点并保留所需的(并且仅保留所需的)名称空间,文本序列化优化等,或者 -
<expletive deleted>
-- 生成XHTML。
我有如下 XML 结构(示例 XML)。我想更改 img 标签值并将它们更改为相对 path.Each XML 文档可以包含超过 100 个 img 标签。我的代码大部分都有效,除了我在根节点中的名称空间声明被添加到引用名称空间的各个节点,我不希望这种情况发生。
示例 XML:
<?xml version="1.0" encoding="UTF-8"?>
<test:document transformVersion="0.0.25" xmlns:test="test.data" xmlns:csp="test.csp" xmlns:cfg="test.cfg">
<data>
<table>
<tr>
<td>
<p class="DataboxTitle">Test Image</p>
</td>
<td>
<p>
<img src="https://www.google.com/images/document/1234.jpg" data-stl="height:107px;width:223px;" srcset="https://www.google.com/images/document/1234.jpg 320w" data-image-title="Test Image"/>
</p>
</td>
</tr>
</table>
<table>
<tr>
<td>
<p class="DataboxTitle">Test Image</p>
</td>
<td>
<p>
<img src="https://www.google.com/images/document/1234.jpg" data-stl="height:107px;width:223px;" srcset="https://www.google.com/images/document/1234.jpg 320w" data-image-title="Test Image"/>
</p>
</td>
</tr>
</table>
<table>
<tr>
<td>
<p class="DataboxTitle">Test Image</p>
</td>
<td>
<p>
<img src="https://www.google.com/images/document/1234.jpg" data-stl="height:107px;width:223px;" srcset="https://www.google.com/images/document/1234.jpg 320w" data-image-title="Test Image"/>
</p>
</td>
</tr>
</table>
<p>
<img src="https://www.google.com/images/document/1234.jpg" data-stl="height:107px;width:223px;" srcset="https://www.google.com/images/document/1234.jpg 320w" data-image-title="Test Image"/>
</p>
</data>
<test:volumes>
<test:test>test</test:test>
</test:volumes>
<csp:volumes>tested</csp:volumes>
</test:document>
在我下面的 SwitchCase 代码中,我知道由于 $doc 变量声明中的代码“/*”,命名空间被单独添加,但如果没有它,我将需要一个 for each 循环,这会搞砸其他逻辑.我只想更改 img 标签属性值而不更改输出中的任何其他内容 xml,请提出出路。
X查询代码:
xquery version "1.0-ml";
declare variable $doc := fn:doc("/c/temp/test.xml")/*;
declare variable $docId := '1928862612025434112';
declare function local:createTag($docId,$imagePath)
{
let $srcOld := fn:data($imagePath/@src)
let $imageName := fn:tokenize($srcOld,"/")[fn:last()]
return fn:concat("/",$docId,"/media/",$imageName)
};
declare function local:createSrcSetTag($docId,$imagePath)
{
let $srcSetsOld := fn:data($imagePath/@srcset)
for $srcSet in fn:tokenize($srcSetsOld, ",")
let $imageLength := fn:tokenize($srcSet," ")[fn:last()]
let $imageName := fn:tokenize($srcSet,"/")[fn:last()]
let $imageNewPath := fn:concat("/",$docId,"/media/",$imageName)
return fn:concat($imageNewPath,"," )
};
declare function local:change($node as node()*) as node()*
{
typeswitch($node)
case element(img) return
element { xs:QName(fn:local-name($node)) }
{
let $image := $node
let $path := xdmp:path($image)
let $data-stl := fn:data($image/@data-stl)
let $data-image-title := fn:data($image/@data-image-title)
let $srcOld := fn:data($image/@src)
let $srcSetsOld := fn:data($image/@srcset)
let $srcNew := local:createTag($docId,$image)
let $srcSetsNew := local:createSrcSetTag($docId,$image)
return (attribute {'src'}{$srcNew}, attribute {'data-stl'}{$data-stl},attribute {'srcset'}{$srcSetsNew}, attribute {'data-image-title'}{$data-image-title} )
}
case element() return
element {fn:node-name($node) } {
$node/@*,
$node/node() ! local:change(.)
}
default return $node
};
local:change(($doc))
这是我转换后的输出:
<test:document transformVersion="0.0.25" xmlns:test="test.data">
<data>
<table>
<tr>
<td>
<p class="DataboxTitle">Test Image</p>
</td>
<td>
<p>
<img src="/1928862612025434112/media/1234.jpg" data-stl="height:107px;width:223px;" srcset="/1928862612025434112/media/1234.jpg 320w," data-image-title="Test Image"/>
</p>
</td>
</tr>
</table>
<table>
<tr>
<td>
<p class="DataboxTitle">Test Image</p>
</td>
<td>
<p>
<img src="/1928862612025434112/media/1234.jpg" data-stl="height:107px;width:223px;" srcset="/1928862612025434112/media/1234.jpg 320w," data-image-title="Test Image"/>
</p>
</td>
</tr>
</table>
<table>
<tr>
<td>
<p class="DataboxTitle">Test Image</p>
</td>
<td>
<p>
<img src="/1928862612025434112/media/1234.jpg" data-stl="height:107px;width:223px;" srcset="/1928862612025434112/media/1234.jpg 320w," data-image-title="Test Image"/>
</p>
</td>
</tr>
</table>
<p>
<img src="/1928862612025434112/media/1234.jpg" data-stl="height:107px;width:223px;" srcset="/1928862612025434112/media/1234.jpg 320w," data-image-title="Test Image"/>
</p>
</data>
<test:volumes>
<test:test>test</test:test>
</test:volumes>
<csp:volumes xmlns:csp="test.csp">tested</csp:volumes>
在出现的元素上保留名称空间声明并不难,只需确保将它们与属性和元素一起复制。为此,您可以使用 $node/namespace::*
:
declare function local:change($node as node()*) as node()*
{
typeswitch($node)
case element(img) return
element { xs:QName(fn:local-name($node)) }
{
let $image := $node
let $path := xdmp:path($image)
let $data-stl := fn:data($image/@data-stl)
let $data-image-title := fn:data($image/@data-image-title)
let $srcOld := fn:data($image/@src)
let $srcSetsOld := fn:data($image/@srcset)
let $srcNew := local:createTag($docId,$image)
let $srcSetsNew := local:createSrcSetTag($docId,$image)
return (attribute {'src'}{$srcNew}, attribute {'data-stl'}{$data-stl},attribute {'srcset'}{$srcSetsNew}, attribute {'data-image-title'}{$data-image-title} )
}
case element() return
element {fn:node-name($node) } {
$node/namespace::*,
$node/@*,
$node/node() ! local:change(.)
}
default return $node
};
HTH!
您发布的实现也是测试其他代码的一个很好的例子 - 一般* XML 处理代码不应依赖于命名空间的特定声明样式,而应仅依赖于内部 XDM 中的命名空间属性模型。如果代码仅由于格式更改而中断,那将是一个很好的单元测试注入以查找和修复此类代码。
** 当然,代码明确依赖于格式或命名空间声明差异是有原因的,例如实现上述单元测试的代码——它需要明确控制结果格式以及语义检测相同的格式差异,与外部服务交互,或者特别乏味,从 XML 中提取子节点并保留所需的(并且仅保留所需的)名称空间,文本序列化优化等,或者 -
<expletive deleted>
-- 生成XHTML。