XSLT 中的依赖图遍历,用于复制 XML 模型的相关元素

dependency graph traversal in XSLT for copying related elements of an XML model

我想通过解决以下问题来展示 XSL 在数据探索方面的强大功能: 给定一个描述某种 "entity-relashionship" 模型的 xml 文件,并且对于该模型中由名称给出的一个实体(假设 XML 模式的属性用作标识符),我想要一个生成新 XML 模型的转换,该模型包含给定实体,以及根据该给定实体的 "Transitive closure of the dependencies relationship" 的所有亲属。

例如输入XML模型为

<root>
    <!-- my model is made of 3 entities : leaf, composite and object -->
    <!-- the xml elements are <leaves>, <composites> and <objects> are just placeholders for these entities -->
    <!-- These placeholders are exepected to be in that order in the output as well as in the input (Schema constraints) -->
    <leaves>
        <!-- A, B, C are 3 types of different leaf nodes with their proper semantic in the model -->
        <A name="f1" others="oooo"/>
        <A name="f2" others="xxxx"/>
        <B name="f3" others="ssss"/>
        <C name="f4" others="gggg"/>    
    </leaves>
    <composites>
        <!-- composites containes only struct and union element -->
        <struct name="structB" others="yyyy">
            <!-- composite pattern, struct can embed struct in a tree-ish fashion -->
            <sRef name="s6" nameRef="structA"/>
            <!-- order of declaration does not matter !!! here in the XML, structA is not yet declared but file is valid -->
            <uRef name="u7" nameRef="unionX"/>
        </struct>
        <!-- union is another kind of composition -->
        <union name="unionX" others="rrrr">
            <vRef name="u3" nameRef="f3" others="jjjj">
            <vRef name="u4" nameRef="f2" others="pppp">
        </union>
        <struct name="structA" others="hhhh">
            <vRef name="v1" nameRef="f1" others="jjjj">
            <vRef name="v2" nameRef="f4" others="pppp">
        </struct>
    </composites>
    <objects>
        <object name="objB" others="tttt">
            <field name="field1" nameRef="unionX" others="qqqq"/>
            <field name="field2" nameRef="f2" others="cccc"/>
        </object>
        <object name="objC" others="nnnn">
            <field name="fieldX" nameRef="structB" others="uuuu"/>
            <field name="fieldY" nameRef="" others="mmmm"/>
        </object>
        <object name="objMain" others="nnnn">
            <field name="fieldY" nameRef="structA" others="mmmm"/>
            <field name="fieldY" nameRef="f3" others="mmmm"/>
            <field name="object4" nameRef="objB" others="wwwww"/>
        </object>
    </objects>
<root>

我想要一个转换,对于给定的名称,创建模型的副本,其中仅包含与此名称的元素相关的信息,以及 nameRef 属性描述的依赖项。

所以对于元素 "field1" 输出将是

<root>
    <leaves>
        <A name="f1" others="oooo"/>
    </leaves>
    <!-- composites and objects placeholders shall be copied even when no elements in the graph traversal -->
    <composites/>
    <objects/>
<root>

而对于 "objB",预期输出将是

<root>
    <leaves>
        <!-- element "f2" shall be copied only once in the output, althought the node is encountered twice in the traversal of "objB" tree :
            - "f2" is referenced under "field2" of "obj2"
            - "f2" is referenced under "u4" of "unionX" that is referencd under "field1" of "obj2"      
        -->
        <A name="f2" others="xxxx"/>
        <B name="f3" others="ssss"/>
    </leaves>
    <composites>
        <union name="unionX" others="rrrr">
            <vRef name="u3" nameRef="f3" others="jjjj">
            <vRef name="u4" nameRef="f2" others="pppp">
        </union>
    <composites>
    <objects>
        <object name="objB" others="tttt">
            <field name="field1" nameRef="unionX" others="qqqq"/>
            <field name="field2" nameRef="f2" others="cccc"/>
        </object>
    </objects>
<root>

依此类推。

从现在开始,我在基本的 XSL 上锻炼但不是很满意,原因如下:

我只有一些感受和"intuitions"关于好的和优雅的方法。

为简化起见,您可以做出以下假设:

"empty" xml 输出模型应如下所示(由于模式限制)

<root>
    <leaves/>
    <composites/>
    <objects/>
<root> 

待完成:我目前使用的 xslt 处理器是 Saxon XSLT proc,XSLT 的版本是 2.0 感谢您的帮助... 我不给你我不引以为豪的xsl,但如果它看起来有帮助,我会...

我试图在 https://xsltfiddle.liberty-development.net/gWEamLs/6 实施 "a transformation that,for a given name, creates a copy of the model with only information related to the element of this name, and of its dependencies described by the nameRef attributes":

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:param name="start-name" as="xs:string">objB</xsl:param>

  <xsl:key name="name-ref" match="*[@name]" use="@name"/>

  <xsl:function name="mf:traverse" as="element()*">
      <xsl:param name="start" as="element()?"/>
      <xsl:sequence select="$start, $start/*, $start/*[@nameRef]!key('name-ref', @nameRef, root(.))!mf:traverse(.)"/>
  </xsl:function>

  <xsl:param name="start-element" as="element()?" select="key('name-ref', $start-name)"/>

  <xsl:variable name="named-elements" select="mf:traverse($start-element)"/>

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

  <xsl:template match="*[@name and not(. intersect $named-elements)]"/>

</xsl:stylesheet>

基于键和递归函数,代码 "first" 将相关元素计算为全局变量中的元素节点序列,并且 "then" 由 [=11 以声明方式设置的身份转换=] 只是通过一个空模板扩展那些具有 name 属性但递归函数未发现与起始元素相关的元素,确保任何不相关的元素不会被复制到输出。