如何使用 XSL 将几个新元素插入到另一个元素中,同时保持其他一切不变

How to insert several new elements into another element with XSL, keeping everything else unchanged

我编写了一个 XSLT,应该在父 rdf:Description 元素中添加一个或多个 skos:narrower 元素。下面是一段首发xml:

<rdf:Description rdf:about="http://schema.xxx.com/ns/vocabulary-structure/AAA">
        <rdf:type rdf:resource="http://www.w3.org/2004/02/skos/core#Concept"/>
        <skos:prefLabel xml:lang="en">AAA</skos:prefLabel>
        <skos:broader rdf:resource="http://schema.xxx.com/ns/vocabulary-structure/AA"/>
    </rdf:Description>

我想为 xml 中每个较窄的元素添加一个元素:

   <rdf:Description rdf:about="http://schema.xxx.com/ns/vocabulary-structure/AAA">
      <skos:narrower rdf:resource="http://schema.xxx.com/ns/vocabulary-structure/AAAA"/>
      <rdf:type rdf:resource="http://www.w3.org/2004/02/skos/core#Concept"/>
      <skos:prefLabel xml:lang="en">AAA</skos:prefLabel>
      <skos:broader rdf:resource="http://schema.xxx.com/ns/vocabulary-structure/AA"/>   
   </rdf:Description>

我需要这样做才能指定 skos:broader 和 skos:narrower 关系(我使用的词汇工具仅存储 skos:broader)。

我写的xsl就是这样做的。然而,虽然结果接近我想要的,但它有一个缺陷。如果元素 AAA 除了 AAAA 之外还有一个更窄的元素 AAAB,对于这种情况,我得到一个 separate rdf:Description 元素:

   <rdf:Description rdf:about="http://schema.xxx.com/ns/vocabulary-structure/AAA">
      <skos:narrower rdf:resource="http://schema.xxx.com/ns/vocabulary-structure/AAAB"/>        
      <rdf:type rdf:resource="http://www.w3.org/2004/02/skos/core#Concept"/>
      <skos:prefLabel xml:lang="en">AAA</skos:prefLabel>
      <skos:broader rdf:resource="http://schema.xxx.com/ns/vocabulary-structure/AA"/>
   </rdf:Description>

我想要的是让指向 AAAA 的 skos:narrower 和指向 AAAB 的 skos:narrower 都在同一个 AAA 元素中,而不是两个单独的元素。

这是 xslt 的(我希望)相关部分。谁能指出我做错了什么?

<xsl:template match="rdf:RDF/rdf:Description" mode="apply-skos-narrower">
    <xsl:param name="this-concept-id"/>
    <xsl:param name="narrower-concept-id"/>
        <xsl:choose>
            <xsl:when test="@rdf:about=$this-concept-id">
                <xsl:copy>
                    <xsl:copy-of select="@*|node()"/>
                    <skos:narrower>
                        <xsl:attribute name="rdf:resource">
                            <xsl:value-of select="$narrower-concept-id"/>
                        </xsl:attribute>
                    </skos:narrower>
                    <xsl:copy-of select="node()"/>
                </xsl:copy>         
            </xsl:when>
        </xsl:choose>       
</xsl:template>

感谢您的评论。以下是要求的完整 xslt。我确定我在这里误解了递归的工作原理,因此非常感谢任何指导。

    <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl" version="1.0"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:skos="http://www.w3.org/2004/02/skos/core#"
>
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <!-- Store the vocabulary identifier as a variable -->
    <xsl:variable name="vocabulary-id" select = "//rdf:Description[child::rdf:type[@rdf:resource='http://www.w3.org/2002/07/owl#Ontology']]/@rdf:about" />

<!-- Replace the massive collection of namespace declarations provided by EVN with a more sensible minimal set, then call the first processing template. -->
    <xsl:template match="/">
        <rdf:RDF xmlns:opencyc="http://sw.opencyc.org/concept/"
            xmlns:freebase="http://rdf.freebase.com/ns/" 
            xmlns:owl="http://www.w3.org/2002/07/owl#"
            xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
            xmlns:dcterms="http://purl.org/dc/terms/" 
            xmlns:skos="http://www.w3.org/2004/02/skos/core#"
            xmlns:dbpedia="http://dbpedia.org/resource/"
            xmlns:sesame="http://www.openrdf.org/schema/sesame#"
            xmlns:cycAnnot="http://sw.cyc.com/CycAnnotations_v1#" 
            xmlns:ctag="http://commontag.org/ns#"
            xmlns:foaf="http://xmlns.com/foaf/0.1/" 
            xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
            xmlns:cyc="http://sw.cyc.com/concept/" 
            xmlns:dc="http://purl.org/dc/elements/1.1/"
            xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
            <!-- Copy the header and Concept Scheme sections - these are fine -->
            <xsl:apply-templates select="/rdf:RDF/rdf:Description" mode="process-vocabulary-header"/>
            <xsl:apply-templates select="/rdf:RDF/rdf:Description" mode="process-concept-scheme"/>
            <!-- Now process all of the other concepts -->
            <xsl:apply-templates select="/rdf:RDF/rdf:Description" mode="process-other-concepts"/>
        </rdf:RDF>
    </xsl:template>

    <!-- Process the vocabulary header information -->
    <xsl:template match="rdf:RDF/rdf:Description" mode="process-vocabulary-header">
        <xsl:if test="rdf:type[@rdf:resource='http://www.w3.org/2002/07/owl#Ontology']">
            <xsl:copy-of select="."/>
        </xsl:if>
    </xsl:template>

    <!-- Process the ConceptScheme element -->
    <xsl:template match="rdf:Description" mode="process-concept-scheme">
        <xsl:if test="rdf:type[@rdf:resource='http://www.w3.org/2004/02/skos/core#ConceptScheme']">
            <xsl:copy-of select="."/>
        </xsl:if>
    </xsl:template>

    <xsl:template match="rdf:RDF/rdf:Description" mode="process-other-concepts">
        <xsl:choose>
            <!-- When we are in the ConceptScheme element, we can get the concepts that have hasTopConcept elements and set the complementary topConceptOf elements -->
            <xsl:when test="rdf:type[@rdf:resource='http://www.w3.org/2004/02/skos/core#ConceptScheme']">
                <xsl:for-each select="skos:hasTopConcept">
                    <xsl:apply-templates select="/rdf:RDF/rdf:Description" mode="apply-top-concepts">
                        <xsl:with-param name="top-concept-id" select="@rdf:resource"/>
                    </xsl:apply-templates>
                </xsl:for-each>
            <!-- Finally, find the elements with skos:broader elements and set their complementary skos:narrower elements -->
            </xsl:when>
            <xsl:when test="skos:broader">
                <xsl:apply-templates select="/rdf:RDF/rdf:Description" mode="apply-skos-narrower">
                    <xsl:with-param name="this-concept-id" select="skos:broader/@rdf:resource"/>
                    <xsl:with-param name="narrower-concept-id" select="./@rdf:about"></xsl:with-param>
                </xsl:apply-templates>
            </xsl:when>
        </xsl:choose>
    </xsl:template>

    <!-- Add the topConceptOf element to the right concepts -->
    <xsl:template match="rdf:RDF/rdf:Description" mode="apply-top-concepts">
        <xsl:param name="top-concept-id"/>
        <xsl:choose>
            <xsl:when test="@rdf:about=$top-concept-id">
                <xsl:copy>
                    <xsl:copy-of select="@*"/>
                    <skos:topConceptOf>
                        <xsl:attribute name="rdf:resource">
                            <xsl:value-of select="$vocabulary-id"/>
                        </xsl:attribute>
                    </skos:topConceptOf>
                    <xsl:copy-of select="node()"/>
                </xsl:copy>
            </xsl:when>
        </xsl:choose>
    </xsl:template>

    <!-- Add the skos:narrower element to the right concepts -->    
    <xsl:template match="rdf:RDF/rdf:Description" mode="apply-skos-narrower">
        <xsl:param name="this-concept-id"/>
        <xsl:param name="narrower-concept-id"/>
            <xsl:choose>
                <xsl:when test="@rdf:about=$this-concept-id">
                    <xsl:copy>
                        <xsl:copy-of select="@*|node()"/>
                        <skos:narrower>
                            <xsl:attribute name="rdf:resource">
                                <xsl:value-of select="$narrower-concept-id"/>
                            </xsl:attribute>
                        </skos:narrower>
                        <xsl:copy-of select="node()"/>
                    </xsl:copy>         
                </xsl:when>
            </xsl:choose>       
    </xsl:template>
</xsl:stylesheet>

首先警告:您不应该使用像 XSLT 这样的普通 XML 处理工具来处理 RDF-XML 除非真的别无选择。相同的底层 RDF 图有许多等效的 XML 表示,最好使用理解图结构的适当 RDF 工具。

也就是说...我认为您可以使用一个键以一种非常简单的方式实现您想要的,该键可以让您通过 rdf:resource 查找 skos:broader 个元素。然后在你处理 rdf:Description X 的时候,以 X 作为对象查找更广泛的三元组并创建相应的 "narrower" 逆元是微不足道的。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl" version="1.0"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:skos="http://www.w3.org/2004/02/skos/core#"
>
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:key name="broaderByObject" match="skos:broader" use="@rdf:resource" />

    <!-- identity template - copy everything as-is except for overrides -->
    <xsl:template match="@*|node()">
        <xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
    </xsl:template>

    <xsl:template match="rdf:Description">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:for-each select="key('broaderByObject', @rdf:about)">
                <!-- the object of the narrower is the subject from the broader -->
                <skos:narrower rdf:resource="{../@rdf:about}" />
            </xsl:for-each>
            <xsl:apply-templates select="node()" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

相同的原则适用于顶级概念 - 添加一个键

<xsl:key name="topConByObject" match="skos:hasTopConcept" use="@rdf:resource" />

并查找创建逆的关键

<xsl:if test="key('topConByObject', @rdf:about)">
    <skos:topConceptOf rdf:resource="{$vocabulary-id}" />
</xsl:if>