XSL transforming XML to XML question

I am not incredibly familiar with XSL yet, and I have run into a snag. I have spent some time searching online and I cannot find a quick answer to this question and I have a dead-line coming up quick, so I figured I would just ask. Sorry if it seems pretty stupid.

The example I am going to give is really simple and it would seem to most people that I wouldnt need to use XSLT to make this change, but it is just a simplified version of what I am working on. For reference (incase you see something I am doing you would do a different way given the context) I am taking a large XML Schema which has a large quantity of references and converting those references to type declarations instead. In my example I am using a simple XML file, but in the application it will be a 18,500+ line XML Schema, so hardcoding attributes etc is not an option, nor is large case like statements. Everything needs to be as dynamic as possible.

For the example, lets say I am starting with this XML File:

<?xml version="1.0" encoding="UTF-8"?>
<SampleTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<TestElement name="Test1" id="TestElement" minOccurs="7"/>
<TestElement name="Test2" type="String" minOccurs="4"/>
<TestElement name="Test3"/>
</SampleTest>

Now, lets say that I want to use XSLT to rewrite this file except instead of minOccurs="X", I want to change it to maxOccurs="1". So the final output should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<SampleTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<TestElement name="Test1" id="TestElement" maxOccurs="1"/>
<TestElement name="Test2" type="String" maxOccurs="1"/>
<TestElement name="Test3"/>
</SampleTest>

Now, I think I have a good plan of execution, I am just getting stumped on how I am to output the actual nodes for the XML file.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="*">
<!-- Not sure how to create the "<[name]" part of the tag -->
<xsl:for-each select="./@*">
<xsl:when test="name()=minOccurs">
minOccurs="1"
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="name()"/>=<xsl:value-of select="."/>
</xsl:otherwise>
<!-- Not sure how to close the tag with "/>" -->
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

As expressed by the comments there are two specific questions. 1) how do I begin the element tag? and 2) how do I end the element tag? Another question would be is there going to be an issue with line feeds the way it is written above (i.e. each attribute would be outputted to its own line in the above example, I think)? If so, is there any way to fix that? I am pretty sure that if I opened the completed output in XMLSpy it would remove those line feeds and format the document correctly. Anyway, am I close with this? How can I get this completed? Again, I would continue researching myself but I have an aggressive dead-line.

Thanks,
Tim
[3453 byte] By [doctorofstyle] at [2007-11-19 2:42:44]
# 1 Re: XSL transforming XML to XML question
Hi!, It's easier to address all problems at once with a code example:

<xsl:element name={name()}>
<!--element contents-->
<xsl:for-each select="@*">
<xsl:choose>
<xsl:when test="name()='minOccurs'">
<xsl:attribute name="maxOccurs">1</xsl:attribute>
</xsl:when>
<xsl:otherwise><xsl:copy-of select="."/></xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:element>

1. Use {} braces to declare an element with a variable name.
2. The beginning "./" in an xpath is redundant, simplifies to "@*"
3. To declare internal attributes, when you declare an element like that, you have to use the <xsl:attribute> element
4. The attribute name value must be surrounded by single 'quotes' in a test statement. (really you could even pass this attribute, the result attribute, and default value in dynamically as a parameter)
5. <xsl:copy-of select="."/> will automatically copy the attributes as attributes.

good luck with the oracle project, (ironically I'm doing something similar with slightly different scope)
jkmyoung at 2007-11-10 3:27:47 >
# 2 Re: XSL transforming XML to XML question
Thank you so much, that saved me at least a couple of hours of head ache. I cant believe I forgot the <xsl:choice> tag, and even more suprised that XML Spy didnt even yell at me about it. Again, thank you for the quick response,

Tim
doctorofstyle at 2007-11-10 3:28:47 >
# 3 Re: XSL transforming XML to XML question
EDIT: Darn it, I guess I took so long to write my respone that jkmyoung beat me to the punch...

1) how do I begin the element tag? and 2) how do I end the element tag?

There are two ways to create elements in xsl. Either just write it directly where you want it to appear. Or using an xsl:element element. using xsl:element is more powerfull because it can be used to create elements with computed names.

I commend you for posting your code, it makes it much easier for me to see what you are having trouble with, very nice :)

That beeing said, your xsl code is not much use. So I I've taken the liberty of a complete rewrite to get you stated down the right path.

First the stylesheet decleration. Your source and desitnaton document are both in a schema namespace, so we need to declare the schema namespace in your xsl file

Something like this should do.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="xml"/>

Then there's the root template I prefer to do as little as possible here. If you need processing instructions in your output xml, I would recommend using the root template for this.

<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>

Then we need a template to drive the basic copying of elements from source to destination.
This should do

<xsl:template match="*"> <!-- Match anything -->
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:for-each select="@*">
<xsl:copy-of select="."/> <!-- copy attributes -->
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>

Finally we are ready to talk about the changes you need to make, You can use the copy template from above as a template for what the specialized template should do...

<xsl:template match="TestElement"> <!-- Match The element you want to change -->
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:for-each select="@*[not(name(.)='minOccurs')]">
<xsl:copy-of select="."/>
</xsl:for-each>
<xsl:attribute name="maxOccurs">
<xsl:text>1</xsl:text>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>

Another question would be is there going to be an issue with line feeds the way it is written above

Your code doesn't really work, so it's a rather moot point. But generally speaking linefeeds are easlly controlled if you always use xsl:text to create text nodes.
khp at 2007-11-10 3:29:51 >
# 4 Re: XSL transforming XML to XML question
Cool, thank you. Between the two examples I should be good to go. I was looking into the <xsl:apply-templates> tag, but I didnt want it to get too complicated and have to troubleshoot extra steps. 8)

Thanks for the quick response,
Tim
doctorofstyle at 2007-11-10 3:30:56 >