XSLT - 在复杂的图形表示中只能复制一次元素

时间:2016-03-30 00:19:22

标签: xml xslt xpath converter graphml

我需要为我的应用程序转换XML数据(GraphML)。 XML表示一个图形,其中包含标签“User”和“Item”的节点,以及标签“HAS_HOBBY”和“FRIEND_OF”的边缘。

鉴于一个特定的用户,我想要改变所有与他分享至少一个爱好的朋友和那些爱好(由项目代表)。 “朋友”由“FRIEND_OF”边缘元素表示,爱好由“HAS_HOBBY”表示。

我有我的XSLT(我有点新手)可以找到所需的物品和朋友,但是在我的逻辑中,我不能设法复制一个朋友一次 - 它与他分享的每一个爱好都做了一次原始用户。 我通过浏览每个朋友的爱好来做每个用户的爱好,并且当有匹配时 - 我打印项目(爱好)(这是可以的)和朋友 - 但是每次匹配时都会打印这个朋友被发现,导致这位朋友多次出现,这是不受欢迎的。

我试图寻找避免这种情况的方法,但我认为我的整个逻辑在实施这个解决方案时存在缺陷。不过,我没有其他想法。

这是我的XSL:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ns="http://graphml.graphdrawing.org/xmlns"
    xmlns="http://graphml.graphdrawing.org/xmlns"
    exclude-result-prefixes="ns #default">
  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>



  <!--Identity template: default copy all content into the output -->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <!-- Don't copy tags called 'node or edge' -->
  <xsl:template match="ns:node" />
  <xsl:template match="ns:edge" />



  <xsl:template match="ns:node[ns:data[@key='username' and . = 'c']]">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>

    <xsl:variable name="USERID" select="@id"/>

    <xsl:for-each select="//ns:edge"> 

      <xsl:if test="@source=$USERID">

        <xsl:variable name="TARGET" select="@target"/>
        <xsl:for-each select="//ns:node[@id=$TARGET]">
          <!-- finds USERNAME's hobbies -->

          <xsl:for-each select="//ns:edge[@source=$USERID and @label='HAS_HOBBY']">
            <xsl:variable name="HOBBYTARGET" select="@target"/>
            <xsl:for-each select="//ns:edge[@source=$TARGET and @label='HAS_HOBBY']">
              <xsl:if test="@target=$HOBBYTARGET">
                <!-- Shared hobby with friend -->
                <xsl:for-each select="//ns:node[@id=$HOBBYTARGET]">
                  <xsl:copy>
                    <xsl:apply-templates select="node()|@*"/>
                  </xsl:copy>
                </xsl:for-each>


              </xsl:if>
            </xsl:for-each>  
          </xsl:for-each>
        </xsl:for-each>
      </xsl:if>

      <xsl:if test="@target=$USERID">

        <xsl:variable name="SOURCE" select="@source"/>
        <xsl:for-each select="//ns:node[@id=$SOURCE]">
          <!-- finds USERNAME's hobbies -->

          <xsl:for-each select="//ns:edge[@source=$USERID and @label='HAS_HOBBY']">
            <xsl:variable name="HOBBYTARGET" select="@target"/>
            <xsl:for-each select="//ns:edge[@source=$SOURCE and @label='HAS_HOBBY']">
              <xsl:if test="@target=$HOBBYTARGET">
                <!-- Shared hobby with friend -->
                <xsl:for-each select="//ns:node[@id=$HOBBYTARGET]">
                  <xsl:copy>
                    <xsl:apply-templates select="node()|@*"/>
                  </xsl:copy>
                </xsl:for-each>


              </xsl:if>
            </xsl:for-each>  
          </xsl:for-each>

        </xsl:for-each>
      </xsl:if>
    </xsl:for-each>

  </xsl:template>

</xsl:stylesheet>

目前朋友的副本丢失了,但是在“与朋友共享爱好”评论之后。

我意识到我不能使用'flag'类型变量(因为它不可能......)并且没有办法拥有数组或类似的数据结构,所以我真的没有想法。

请帮助我让一个用户的朋友与他共享至少一个爱好(项目),以及自己的爱好。

编辑: 示例输入:我添加了图形可视化,因此很容易看到

enter image description here

<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<graph id="G" edgedefault="directed">

<node id="n2" labels=":Item"><data key="labels">:Item</data><data key="itemId">Q1</data></node>
<node id="n32" labels=":Item"><data key="labels">:Item</data><data key="itemId">Q8</data></node>
<node id="n51" labels=":Item"><data key="labels">:Item</data><data key="itemId">Q23</data></node>
<node id="n897" labels=":Item"><data key="labels">:Item</data><data key="itemId">Q55</data></node>

<node id="n406727" labels=":User"><data key="labels">:User</data><data key="hobbies">[Ljava.lang.String;@78ba00a3</data><data key="firstName">a</data><data key="imgPath">/uploads/a.png</data><data key="surName">a</data><data key="username">a</data><data key="gender">Male</data><data key="relaStatus">Single</data></node>
<node id="n406729" labels=":User"><data key="labels">:User</data><data key="hobbies"></data><data key="firstName">b</data><data key="imgPath">/uploads/b.png</data><data key="surName">b</data><data key="username">b</data><data key="gender">Male</data><data key="relaStatus">Single</data></node>
<node id="n406731" labels=":User"><data key="labels">:User</data><data key="hobbies"></data><data key="blocked">[Ljava.lang.String;@7b800b40</data><data key="firstName">c</data><data key="imgPath">/uploads/c.png</data><data key="surName">c</data><data key="username">c</data><data key="gender">Male</data><data key="relaStatus">Single</data></node>
<node id="n406734" labels=":User"><data key="labels">:User</data><data key="hobbies"></data><data key="firstName">d</data><data key="imgPath">/uploads/d.png</data><data key="surName">d</data><data key="username">d</data><data key="gender">Male</data><data key="relaStatus">Single</data></node>

<edge id="e1223400" source="n406727" target="n406729" label="FRIEND_OF"><data key="label">FRIEND_OF</data></edge>
<edge id="e1223403" source="n406727" target="n406731" label="FRIEND_OF"><data key="label">FRIEND_OF</data></edge>
<edge id="e1223405" source="n406734" target="n406731" label="FRIEND_OF"><data key="label">FRIEND_OF</data></edge>
<edge id="e1223405" source="n406727" target="n406734" label="FRIEND_OF"><data key="label">FRIEND_OF</data></edge>

<edge id="e1223374" source="n406727" target="n2" label="HAS_HOBBY"><data key="label">HAS_HOBBY</data></edge>
<edge id="e1223385" source="n406727" target="n51" label="HAS_HOBBY"><data key="label">HAS_HOBBY</data></edge>
<edge id="e1223383" source="n406729" target="n2" label="HAS_HOBBY"><data key="label">HAS_HOBBY</data></edge>
<edge id="e1223384" source="n406731" target="n2" label="HAS_HOBBY"><data key="label">HAS_HOBBY</data></edge>
<edge id="e1223375" source="n406731" target="n51" label="HAS_HOBBY"><data key="label">HAS_HOBBY</data></edge>
<edge id="e1223371" source="n406734" target="n897" label="HAS_HOBBY"><data key="label">HAS_HOBBY</data></edge>

</graph>
</graphml>

这是示例输出。你可以看到只有c和b留在结果中,因为它们有一个共同的爱好(带Q的项目)。所以d,边缘a-d和Q51,Q8都消失了。

enter image description here

<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<graph id="G" edgedefault="directed">

<node id="n2" labels=":Item"><data key="labels">:Item</data><data key="itemId">Q1</data></node>
<node id="n51" labels=":Item"><data key="labels">:Item</data><data key="itemId">Q23</data></node>

<node id="n406727" labels=":User"><data key="labels">:User</data><data key="hobbies">[Ljava.lang.String;@78ba00a3</data><data key="firstName">a</data><data key="imgPath">/uploads/a.png</data><data key="surName">a</data><data key="username">a</data><data key="gender">Male</data><data key="relaStatus">Single</data></node>
<node id="n406729" labels=":User"><data key="labels">:User</data><data key="hobbies"></data><data key="firstName">b</data><data key="imgPath">/uploads/b.png</data><data key="surName">b</data><data key="username">b</data><data key="gender">Male</data><data key="relaStatus">Single</data></node>
<node id="n406731" labels=":User"><data key="labels">:User</data><data key="hobbies"></data><data key="blocked">[Ljava.lang.String;@7b800b40</data><data key="firstName">c</data><data key="imgPath">/uploads/c.png</data><data key="surName">c</data><data key="username">c</data><data key="gender">Male</data><data key="relaStatus">Single</data></node>

<edge id="e1223400" source="n406727" target="n406729" label="FRIEND_OF"><data key="label">FRIEND_OF</data></edge>
<edge id="e1223403" source="n406727" target="n406731" label="FRIEND_OF"><data key="label">FRIEND_OF</data></edge>
<edge id="e1223405" source="n406734" target="n406731" label="FRIEND_OF"><data key="label">FRIEND_OF</data></edge>

<edge id="e1223374" source="n406727" target="n2" label="HAS_HOBBY"><data key="label">HAS_HOBBY</data></edge>
<edge id="e1223385" source="n406727" target="n51" label="HAS_HOBBY"><data key="label">HAS_HOBBY</data></edge>
<edge id="e1223383" source="n406729" target="n2" label="HAS_HOBBY"><data key="label">HAS_HOBBY</data></edge>
<edge id="e1223384" source="n406731" target="n2" label="HAS_HOBBY"><data key="label">HAS_HOBBY</data></edge>
<edge id="e1223375" source="n406731" target="n51" label="HAS_HOBBY"><data key="label">HAS_HOBBY</data></edge>

</graph>
</graphml>

感谢您的时间。

编辑#2:添加了标签节点和hasLabel边缘的数据:

<node id="n3" labels=":Label"><data key="labels">:Label</data><data key="en-gb">Universe</data>
<edge id="e0" source="n2" target="n3" label="hasLabel"><data key="label">hasLabel</data></edge>

此边缘将具有Q1的itemId的节点n2连接到具有其标签“Universe”的节点n3。

2 个答案:

答案 0 :(得分:1)

问:*他和他分享至少一个爱好的所有朋友。*
这是第一种可能性。

为用户ID创建一个包含所有业余爱好边缘的变量:

<xsl:variable name="hobbies" select="//ns:edge[@source=$USERID and @label='HAS_HOBBY']"/>

所有朋友(边缘)相同:

<xsl:variable name="friends" select="//ns:edge[@target=$USERID and @label='FRIEND_OF']"/>

与拥有相同爱好的朋友相比:

<xsl:variable name="friends_with_bobby"
   select="$friends[ //ns:edge[ @label='HAS_HOBBY'  and 
         @target = $hobbies/@target]/@source=./@source   ]"/>

要测试此尝试:

<xsl:template match="ns:node[ns:data[@key='username' and . = 'c']]">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>

    <xsl:variable name="USERID" select="@id"/>

    <xsl:variable name="hobbies" select="//ns:edge[@source=$USERID and @label='HAS_HOBBY']"/>
    <xsl:variable name="friends" select="//ns:edge[@target=$USERID and @label='FRIEND_OF']"/>
    <xsl:variable name="friends_with_bobby" select="$friends[ //ns:edge[ @label='HAS_HOBBY'  and @target = $hobbies/@target]/@source=./@source   ]"/>
    <hobbies>
        <xsl:copy-of select="$hobbies" />
    </hobbies>
    <friends>
        <xsl:copy-of select="$friends" />
    </friends>
    <friends_with_bobby>
        <xsl:copy-of select="$friends_with_bobby" />
    </friends_with_bobby>
</xsl:template>

这只是边缘,但应该很容易适应您要求的输出。 (否则请告诉我)

更新: 要让所有用户拥有相同的爱好(不需要朋友),请尝试:

    <xsl:variable name="shared_hobby" select="//ns:edge[ @label='HAS_HOBBY'  and @target = $hobbies/@target]"/>
    <xsl:variable name="n_user_shared_hobby" select="//ns:node[ns:data[@key='username'] and @id=$shared_hobby/@source]"/>

答案 1 :(得分:1)

以下是使用XSLT 2.0(由Saxon 9,XmlPrime,Altova,Exselt支持)的示例,使用键引用项目,然后将intersect之类的操作设置为仅输出共享节点:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    xpath-default-namespace="http://graphml.graphdrawing.org/xmlns"
    version="2.0">

<xsl:param name="user-name" as="xs:string" select="'c'"/>

<xsl:output indent="yes"/>

<xsl:key name="user-name" match="node[@labels = ':User']" use="data[@key = 'username']"/>

<xsl:key name="node-id" match="node" use="@id"/>

<xsl:key name="source-friends" match="edge[@label = 'FRIEND_OF']" use="@source"/>
<xsl:key name="target-friends" match="edge[@label = 'FRIEND_OF']" use="@target"/>
<xsl:key name="source-hobbies" match="edge[@label = 'HAS_HOBBY']" use="@source"/>

<xsl:variable name="start-node" select="key('user-name', $user-name)"/>

<xsl:variable name="start-friends"
               select="key('node-id', key('source-friends', $start-node/@id)/@target) |
                       key('node-id', key('target-friends', $start-node/@id)/@source)"/>

<xsl:variable name="start-hobbies" select="key('node-id', key('source-hobbies', $start-node/@id)/@target)"/>

<xsl:variable name="friends-with-shared-hobby" select="$start-friends[key('node-id', key('source-hobbies', @id)/@target) intersect $start-hobbies]"/>

<xsl:variable name="shared-hobbies" select="$start-hobbies intersect key('node-id', key('source-hobbies', $friends-with-shared-hobby/@id)/@target)"/>

<xsl:template match="/*">
    <xsl:copy>
        <xsl:copy-of select="$start-node | $friends-with-shared-hobby | $shared-hobbies"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>