1
votes

I have a XSL-T which is looping through all the child elements of an XML, and pushing those attributes into a single outputted element. However, there are duplicate attributes in these child elements (same attribute in multiple children), and the XSL is only providing one result for each (only one of the values in that attribute in the family). I also do not believe it is looking for all of the children of the children, and properly pushing these into the resulting attribute set.

I have tried using a single dynamic XSL-T, and also by using N# XSL templates, with each specifically focusing on a single child element, and then hard-coding the child element's name into the outputted attribute -- the second approach is only giving results for the first template, so did not solve my problem. If my second approach can be fixed to work, that is fine. If there is a way to programatically do this in a dynamic manner (the first option), then really cool! XSL is powerful, so hopefully it can do this, and you all can assist!

Below is an example shaped like my XML document:

<JSON offset="0" total_rows="1337" millis="987">
<rows Attribute1="" Attribute2="" oid="0000001">
<Name Attribute1="FirstName" Attribute2="LastName">
<_id oid="1337"/>
</Name>
<Occupation oid="12345"/>
</rows>
</JSON>

Note that there are duplicate attributes between the parent, and the child elements. I can only get one set of these attributes using my dynamic code or hard-coded approach. Also, I am not sure if either approach is successfully trying to get the child of the child of rows-- since they are not working, I have not been able to confirm that.

My two XSL approaches:

Dynamic (Only returns one value for the attributes which are duplicated):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="JSON">
<DATAPARENT>
<xsl:apply-templates/>
</DATAPARENT>
</xsl:template>
<xsl:template match="//rows">

        <xsl:element name="DataRow">
        <xsl:for-each select="*">                   

                    <xsl:for-each select="@*">  

                    <xsl:attribute name="{name()}">  

                        <xsl:value-of select="." />  

                    </xsl:attribute>

                    </xsl:for-each> 

        </xsl:for-each>
        </xsl:element>
        <xsl:text>&#10;</xsl:text>


</xsl:template>
</xsl:stylesheet>

Hard-Coded (Less flexible, which is fine, but also not giving me anything but the first set of results with the rows_{name()}):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="JSON">
<DATAPARENT>
<xsl:apply-templates/>
</DATAPARENT>
</xsl:template>

<xsl:template match="/rows">

        <xsl:element name="DataRow">            

                    <xsl:for-each select="@*">  

                    <xsl:attribute name="rows_{name()}">  

                        <xsl:value-of select="." />  


                    </xsl:attribute>

                    </xsl:for-each> 

        </xsl:element>  

</xsl:template>

<xsl:template match="//Name">

        <xsl:element name="DataRow">            

                    <xsl:for-each select="@*">  

                    <xsl:attribute name="Name_{name()}">  

                        <xsl:value-of select="." />  


                    </xsl:attribute>

                    </xsl:for-each> 

        </xsl:element>  

</xsl:template>

<xsl:template match="//Occupation">

        <xsl:element name="DataRow">            

                    <xsl:for-each select="@*">  

                    <xsl:attribute name="Occupation_{name()}">  

                        <xsl:value-of select="." />  


                    </xsl:attribute>

                    </xsl:for-each> 

        </xsl:element>  

</xsl:template>

</xsl:stylesheet>

Thank you all for your suggestions and insights that can be provided. Please look at the XSL that I have already provided before suggesting basic approaches which just do element attribute looping; I am already using them, and it is the duplicate attribute issue I haven't resolved. Thanks!!

**EDIT: The desired output would be:

<DATAPARENT>
<DataRow rows_Attribute1="" rows_Attribute2="" rows_oid="0000001" Name_Attribute1="FirstName" Name_Attribute2="LastName" Name__id_oid="1337" Occupation_oid="12345" />
<DataRow rows_Attribute1="1" rows_Attribute2="2" rows_oid="0000002" Name_Attribute1="FirstName" Name_Attribute2="LastName" Name__id_oid="1254" Occupation_oid="99231" />
</DATAPARENT>**

I am aware that I can't have duplicate named attributes in XML, but I don't know how to programatically create the attribute name with a unique combination referencing the elements in the source.

2

2 Answers

0
votes

First: In your first sample, you're writing out all the attributes of all the children of rows as attributes on one DataRow element, with the same name. You're only getting one attribute with any given name (even if more than one attribute with that name occurs among the children) because XML only allows one attribute with any given name on an element. Your output DataRows element can have one oid attribute, but it cannot have two, if the stylesheet is to produce XML output.

If you want one name-value pair for each attribute-value pair in the subtree, write them out as children of DataRow, not attributes.

Then: You're not getting any attributes from grandchildren of rows (or their descendants) because your xsl:for-each iterates over children, not descendants. You could rewrite the for-each as an apply-templates, possibly with a mode (which would make it easy to recur and which some XSLT programmers would find more idiomatic), or you could change select="*" to "select="descendant::*".

0
votes

Yo haven't shown your desired output, but it looks to me as if you are trying to create an element which has several attributes with the same name. That's not allowed in XML, so XSLT doesn't permit it.

If you try to add two attributes with the same name to the same element, XSLT takes the last one added; XQuery reports an error.