How to XSLT transform XML nested attribute elements and concatenate name

1.3k views Asked by At

I am not savvy with XML/XSLT at all. I'm just trying to learn enough to transform this XML file into something that my Access database can use. The XML data was output in an attribute format and Access only likes the element format. I have the basic transformations down, but I can't seem to elementize the nested data.

I would like the final result to be all elements. At the moment my XSLT script is trying to include the parental name attribute in the child element's name, but that may not be necessary. It'd be awesome if I could have the list elements numbered within their names. Such as "AuthorList1", "AuthorList2" or "Author1", "Author2".

Here's the kind of XML that I'm dealing with (abbreviated):

XML

<Result>
<DocSum>
<Id>25587056</Id>
<Item Name="PubDate" Type="Date">2015 Jan 13</Item>
<Item Name="EPubDate" Type="Date">2015 Jan 13</Item>
<Item Name="Source" Type="String">Invest Ophthalmol Vis Sci</Item>
<Item Name="AuthorList" Type="List">
    <Item Name="Author" Type="String">Wang Q</Item>
    <Item Name="Author" Type="String">Tuten WS</Item>
    <Item Name="Author" Type="String">Lujan BJ</Item>
</Item>
<Item Name="Title" Type="String">Adaptive optics microperimetry</Item>
<Item Name="Volume" Type="String">56</Item>
<Item Name="ArticleIds" Type="List">
    <Item Name="pubmed" Type="String">25587056</Item>
    <Item Name="pii" Type="String">iovs.14-15576</Item>
    <Item Name="doi" Type="String">10.1167/iovs.14-15576</Item>
</Item>
<Item Name="References" Type="List"></Item>
<Item Name="HasAbstract" Type="Integer">1</Item>
</DocSum>
</Result

I've gotten pretty far with the XSLT, but I'm not all the way there. One issue that keeps cropping up is the empty elements (like References) get null names that are not allowed. That occurred when I attempted an command using a match pattern of "Item[@Type='List']". Also, I can't seem to get the child nodes (elements?) of the lists to become their own elements.

XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="@*" >
    <xsl:element name="{name()}">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

<xsl:template match="node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="Item">
    <xsl:element name="{@Name}">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

<xsl:template match="child::Item">
    <xsl:variable name="Parent" select="TEST"/>
        <xsl:element name="{$Parent}{@Name}">
            <xsl:value-of select="."/>
        </xsl:element>
</xsl:template>
</xsl:stylesheet>

I hope that this made some sort of sense. I'm really not looking to become an expert in this since I doubt that I'll be dealing with XML transformations again for a real long time. I would appreciate ANY help that is forthcoming.

1

There are 1 answers

1
Tim C On BEST ANSWER

One issue you have is the template that matches Item

<xsl:template match="Item">
    <xsl:element name="{@Name}">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

By using xsl:value-of here, it will not carry on processing any child nodes, so you won't get any matching done on any child Item elements. You need to use xsl:apply-templates here, to allow templates to apply to the children

<xsl:template match="Item">
    <xsl:element name="{@Name}">
        <xsl:apply-templates />
    </xsl:element>
</xsl:template>

Another issue is with the other template that matches child::Item. I think this will also match the parent Item elements too, so effectively has the same priority as the other Item template. You probably need to replace the match with this:

<xsl:template match="Item[@Type='List']/Item">

Finally, you define the Parent variable in this template like so:

<xsl:variable name="Parent" select="TEST"/>

This will set Parent to the value of the element named TEST, when perhaps you wanted just the literal value of "Test". So, you should define it like so

<xsl:variable name="Parent" select="'TEST'"/>

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="@*" >
    <xsl:element name="{name()}">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

<xsl:template match="node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="Item">
    <xsl:element name="{@Name}">
        <xsl:apply-templates />
    </xsl:element>
</xsl:template>

<xsl:template match="Item[@Type='List']/Item">
    <xsl:variable name="Parent" select="'TEST'"/>
        <xsl:element name="{$Parent}{@Name}">
            <xsl:value-of select="."/>
        </xsl:element>
</xsl:template>
</xsl:stylesheet>