Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Xml 在内存中的XQuery中多次编辑同一文档节点_Xml_Xpath_Functional Programming_Xquery_Saxon - Fatal编程技术网

Xml 在内存中的XQuery中多次编辑同一文档节点

Xml 在内存中的XQuery中多次编辑同一文档节点,xml,xpath,functional-programming,xquery,saxon,Xml,Xpath,Functional Programming,Xquery,Saxon,假设我有这样一个文档: <root> <content> <z>valZ</z> <a> <b> <c>valC</c> </b> <b> <c>valC</c>

假设我有这样一个文档:

    <root>
    <content>
        <z>valZ</z>
        <a>
            <b>
                <c>valC</c>
            </b>
            <b>
                <c>valC</c>
            </b>
        </a>
        <a>
            <d>valD</d>
        </a>
    </content>
</root>
   <root>
    <content>
        <z>valZ</z>
        <a>
            <b>
                <c>valC</c>
                <z>valZ</z>
            </b>
            <b>
                <c>valC</c>
                <z>valZ</z>
            </b>
        </a>
        <a>
            <d>valD</d>
            <b>             <!-- <b> was not present here before -->
                <z>valZ</z>
            </b>
        </a>
    </content>
</root>
如果我这样写InsertNodeInLevel和InsertNodeInLevel:

    declare %private function insertNodeInBLevel($rootDoc as document-node(), $value as text()) {
    mem:node-insert-child($rootDoc/*:root/*:content/*:a/*:b, createTagWithZField($value))
    };

    declare %private function insertNodeInALevel($rootDoc as document-node(), $value as text()) {
    mem:node-insert-child($rootDoc/*:root/*:content/*:a, createWholeBtagBlock($value))
    };
它返回多个rootDoc文档副本,并将值添加到不同的位置,而不是一个文档的所有位置都添加了节点

我尝试了许多解决方案,包括递归和循环:

   declare %private function insertNodeInBLevel($rootDoc as document-node(), $value as text()) {
    if(fn:exists($rootDoc/*:root/*:content/*:a/*:b)) then
        let $nodes := $rootDoc/*:root/*:content/*:a/*:b
        for $node at $index in $nodes
            let $rootDoc := exampleInsertWithIndex($rootDoc, $value, $index)

        return $rootDoc 
    };

    declare %private function exampleInsertWithIndex($rootDoc as document-node(), $value as text(), $index) {
    mem:node-insert-child($rootDoc/*:root/*:content/*:a/*:b[$index], createTagWithZField($value))
    };

但是,值是不可变的,所以不能再次保存到同一个rootDoc,等等。。。你知道如何解决这个问题吗,所以我将编辑同一文档的多个节点并只返回这个节点,而不是它的副本?我是面向对象语言的开发人员,函数式语言对我来说是非常新的,它们遵循不同的范式,因此可能我对解决方案的思考方式有缺陷…

如果您能够使用XQuery Update(我认为这需要Saxon的商业变体,但也有其他支持XQuery Update的实现),只需使用转换并将
节点插入每个
节点:

let $z := //z
return
  copy $result := /root
  modify
    for $node in $result//b
    return insert node $z into $node
  return $result
如果不是,递归遍历树并重建它,同时根据需要修改它是XQuery中的常见模式:

declare function local:insert-z($subtree as element(), $z as element()) as element() {
   element {node-name($subtree)}
      {$subtree/@*,
          for $node in $subtree/node()
              return
               if ($node instance of element())
                 then
                   (
                     local:insert-z($node, $z),
                     if ($subtree/self::b)
                     then $z
                     else ()
                   )
                 else $node
      }
};

local:insert-z(/root, //z)

中有一个如何将此模式用于不同用例的示例列表。

这可以通过为这些元素提供一个和一些自定义逻辑来实现

declare function local:transform($nodes as node()*) as item()* {
    for $node in $nodes
    return 
        typeswitch($node)
            case text() return $node
            case comment() return $node
            case processing-instruction() return $node
            case attribute() return $node
            case element(a) return local:transform-a($node)
            case element(b) return local:transform-b($node)
            default return local:identity($node)
};

declare function local:transform-a($a as element(a)) as element(a) {
    element a {
        local:transform($a/(@* | node())), 
        if(not(exists($a/b))) then
            element b { root($a[1])/content/z }
        else ()
    }
};

declare function local:transform-b($b as element(b)) as element(b) {
    element b {
        local:transform($b/(@* | node())), 
        if(not(exists($b/z))) then 
          root($b[1])/content/z
        else ()
    }
};

declare function local:identity($node as element()*) as item()* {
    element {name($node)} {($node/@*, local:transform($node/node()))}
};

这种事情在XSLT中要容易得多

<xsl:mode on-no-match="shallow-copy"/>

<xsl:template match="a[not(b)]">
  <a>
    <xsl:apply-templates/>
    <b><xsl:copy-of select="preceding-sibling::z"/></b>
  </a>
</xsl:template>

<xsl:template match="b">
  <b>
    <xsl:apply-templates/>
    <xsl:copy-of select="../preceding-sibling::z"/>
  </b>
</xsl:template>


由于您将问题标记为Saxon,我想知道您是否可以使用XSLT而不是XQuery?至于问题,单个
a
元素是否可以有
b
d
子元素?在这种情况下会发生什么情况,
d
始终保持不变?使用XSLT,您可以在模板中很好地分隔任务,请参见示例。但我相信有人也会在XQuery方面帮助您。谢谢您的建议。没错,XSLT可以解决这个问题。然而,如果我能用xQuery解决这个问题,那将是最好的,但我对这个问题感到时间紧迫,所以在最坏的情况下,我将回到XSLT。对于您在上述评论中提出的问题,“d”在任何时候都必须保持不变。谢谢您,我将尝试两种建议的解决方案!
declare function local:transform($nodes as node()*) as item()* {
    for $node in $nodes
    return 
        typeswitch($node)
            case text() return $node
            case comment() return $node
            case processing-instruction() return $node
            case attribute() return $node
            case element(a) return local:transform-a($node)
            case element(b) return local:transform-b($node)
            default return local:identity($node)
};

declare function local:transform-a($a as element(a)) as element(a) {
    element a {
        local:transform($a/(@* | node())), 
        if(not(exists($a/b))) then
            element b { root($a[1])/content/z }
        else ()
    }
};

declare function local:transform-b($b as element(b)) as element(b) {
    element b {
        local:transform($b/(@* | node())), 
        if(not(exists($b/z))) then 
          root($b[1])/content/z
        else ()
    }
};

declare function local:identity($node as element()*) as item()* {
    element {name($node)} {($node/@*, local:transform($node/node()))}
};
<xsl:mode on-no-match="shallow-copy"/>

<xsl:template match="a[not(b)]">
  <a>
    <xsl:apply-templates/>
    <b><xsl:copy-of select="preceding-sibling::z"/></b>
  </a>
</xsl:template>

<xsl:template match="b">
  <b>
    <xsl:apply-templates/>
    <xsl:copy-of select="../preceding-sibling::z"/>
  </b>
</xsl:template>