I have some XML (over which I have no control) which has some "rdf" annotations included. I am trying to convert this into actual usable RDF/XML (using XSLT), but there is an issue I don't quite know how to resolve. First, in the XML there are long sections like this:
<rdf:Description>
<some:tag>
<rdf:Bag>
<rdf:li rdf:resource="resource1" />
</rdf:Bag>
</some:tag>
<some:other-tag>
<rdf:Bag>
<rdf:li rdf:resource="resource2" />
<rdf:li rdf:resource="resource3" />
</rdf:Bag>
</some:other-tag>
</rdf:Description>
the goal is to create RDF/XML like this:
<rdf:Description>
<some:tag>resource1</some:tag>
<some:other-tag>resource2<some:other-tag>
<some:other-tag>resource3<some:other-tag>
</rdf:Description>
The number of different tags however is potentially unbounded, so I can't go and enumerate different cases here. I need some generic way to use the tag surrounding the bag as enclosing tags for the individual resource attributes.
Unfortunately, I really don't know much XSLT, so I'm at a loss here.
What I've done so far unfortunately does not work for the case of some:other-tag
, because I only get one triple, where the objects are concatenated:
<some:other-tag>resource2resource3</some:other-tag>
<xsl:template match="rdf:Description/*">
<xsl:copy>
<xsl:for-each select="rdf:Bag/rdf:li/@rdf:resource">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
CodePudding user response:
I would start with the identity transformation template (e.g. in XSLT 3 simply by declaring <xsl:mode on-no-match="shallow-copy"/>
) and then add two templates
<xsl:template match="*[rdf:Bag]| rdf:Bag">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="rdf:Bag/rdf:*">
<xsl:element name="{name(../..)}" namespace="{namespace-uri(../..)}">{@rdf:resource}</xsl:element>
</xsl:template>
That should give e.g.
<rdf:Description xmlns:rdf="http://example.com/rdf" xmlns:some="http://example.com/some">
<some:tag>resource1</some:tag>
<some:other-tag>resource2</some:other-tag>
<some:other-tag>resource3</some:other-tag>
</rdf:Description>
for an input like
<rdf:Description xmlns:rdf="http://example.com/rdf" xmlns:some="http://example.com/some">
<some:tag>
<rdf:Bag>
<rdf:li rdf:resource="resource1" />
</rdf:Bag>
</some:tag>
<some:other-tag>
<rdf:Bag>
<rdf:li rdf:resource="resource2" />
<rdf:li rdf:resource="resource3" />
</rdf:Bag>
</some:other-tag>
</rdf:Description>
You haven't really shown the namespaces nor told us whether there can be other elements than the rdf:Bar
and if so, how they would need to be transformed.
CodePudding user response:
Okay, I figured out a way writing this as a recursive procedure. I'd still be happy if someone came along and told me there's a cleaner, more idiomatic version, but, on the off-chance this could help someone, here you go:
<xsl:template name="bonkers">
<xsl:param name="rs" required="yes"/>
<xsl:copy copy-namespaces="no">
<xsl:value-of select="subsequence($rs, 1, 1)"/>
</xsl:copy>
<xsl:if test="false() = empty($rs)">
<xsl:call-template name="bonkers">
<xsl:with-param name="rs" select="subsequence($rs, 2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="rdf:Description/*">
<xsl:variable name="resources" select="rdf:Bag/rdf:li/@rdf:resource"/>
<xsl:call-template name="bonkers">
<xsl:with-param name="rs" select="$resources"/>
</xsl:call-template>
</xsl:template>