tags:

views:

56

answers:

2

I'm trying to compose xml elements into each other, and the problem I am having is when there's the same IDs. Basically what I need to do is mangle all the IDs in an xml file, as well as the references to them. (I'm doing this with SVGs to add a little context)

Say I have:

<bar id="foo"/>
<baz ref="url(#foo)"/>
<bar id="abc"/>
<baz ref="asdf:url(#abc)"/>

I'd like a way to automatically turn that into something like:

<bar id="foo_1"/>
<baz ref="url(#foo_1)"/>
<bar id="abc_1"/>
<baz ref="asdf:url(#abc_1)"/>

or something similar.

I can probably write some XSL to do it, but was hoping there's an easier way.

Thanks!

A: 

Not a very elegant solution, but you could always use some regular expressions.

Match on id=(.*) and then replace all #$1's with whatever you want.

Tom Rudick
Yeah, I was considering that. sed is the solution for everything :). Since we're only doing this with SVGs we make, I guess we'd not have to worry about having xml like <foo id = "bar"/>or weird stuff like that.I'd prefer a more "correct" solution, but this definitely would work for most common cases.
mikelikespie
A: 

If you end up using XSLT, you may find the generate-id function useful for generating ids.

Here's a sort of dummy example using XSLT 1.0:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

  <xsl:key name="element-by-id" match="//*" use="@id"/>

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

  <!-- ... except for rewritten id's -->
  <xsl:template match="@id">
    <xsl:attribute name="id">
      <xsl:value-of select="generate-id(..)"/>
    </xsl:attribute>
  </xsl:template>

  <!-- ... and rewritten id references -->
  <xsl:template match="@ref">
    <xsl:variable name="head" select="substring-before(., 'url(#')"/>
    <xsl:variable name="tail" select="substring-after(., 'url(#')"/>
    <xsl:variable name="idref" select="substring-before($tail, ')')"/>
    <xsl:variable name="end" select="substring-after($tail, ')')"/>
    <xsl:attribute name="ref">
      <xsl:value-of select="concat($head, 'url(#', 
                            generate-id(key('element-by-id', $idref)), 
                            ')', $end)"/>
    </xsl:attribute>
  </xsl:template>

</xsl:stylesheet>

If you don't like the id's produced by generate-id (or if you cannot use it for other reasons -- to ensure that you get unique id's all the nodes need to be processed within the same transformation) you can replace the calls to it with some other logic, like adding a suffix.

Jukka Matilainen