Question

I'm trying to prototype a transform to turn xsl:schema into a php interface. I'm having a little trouble matching xsd:simpleType elements that have a name attribute matching the type attribute of xsd:element elements. Suppose I have a schema like this:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="Foo" type="Bar"/>
  <xsd:simpleType name="Bar">
    <xsd:restriction base="xsd:string">
      <xsd:maxLength value="32"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:schema>

I'd like to have the following result.

<?php
interface Foo {
  public abstract function buildFooXmlString(Bar $a);
}

Here's the xslt I have so far:

<xsl:stylesheet version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:template match="*"/>
  <xsl:template match="/">&lt;?php <xsl:apply-templates select="xsd:schema"/></xsl:template>
  <xsl:template match="xsd:schema">interface FromXsd { <xsl:apply-templates select="xsd:element"/> }</xsl:template>
  <xsl:template match="xsd:element">
    <xsl:apply-templates select="xsd:annotation"/>
    abstract public function build<xsl:value-of select="normalize-space(@name)"/>XmlString(<xsl:apply-templates select="@type"/>);
  </xsl:template>
  <xsl:template match="xsd:annotation">/* <xsl:value-of select="normalize-space()"/> */</xsl:template>

  <xsl:template match="@type"><xsl:apply-templates select="//xsd:simpleType[@name=normalize-space()]"/></xsl:template>
  <xsl:template match="xsd:simpleType"><xsl:value-of select="local-name()"/> $a</xsl:template>
</xsl:stylesheet>

It produces most of the desired result, but doesn't include anything inside the parentheses:

<?php
interface Foo {
  public abstract function buildFooXmlString();
}

How can I select the simpleType node with the name attribute matching the type of the element? (Names have to be unique for xsd types.)

Was it helpful?

Solution

The simpleType node is never selected because in the template where you match xsd:schema, you only apply the templates to the xsd:element child subtree. The xsd:simpleType sibling will never be processed.

If you want to allow the processing of all children of a node, you should include an empty <xsl:apply-templates/> inside the xsd:schema template.

That still won't generate the result you want. It's actually much simpler. To generate the code fragment you expect, you don't need to read the xsd:simpleType element, since the attribute that contains the type you want can be directly obtained from the @type attribute of xsd:element using xsd:value-of, and you can just print the $a immediately after it:

XmlString(<xsl:value-of select="@type"/> $a)

Since you are generating text, you should use the <xsl:text> elements to control how your whitespace will be distributed. For instance, if you use:

<xsl:template match="/">
    <xsl:text>&lt;?php </xsl:text>
    <xsl:apply-templates select="xsd:schema"/>
</xsl:template>

You won't need to worry about always placing the &lt;?php text immediately after the <xsl:template> and can indent your code normally. You can also include newlines with the &#xa; character (and it won't break your formatting):

<xsl:template match="xsd:schema">
    <xsl:text>interface FromXsd {&#xa;</xsl:text>
    <xsl:apply-templates select="xsd:element"/><xsl:text>&#xa;</xsl:text>
    <xsl:apply-templates select="xsd:annotation"/>
    <xsl:text>&#xa;}</xsl:text>
</xsl:template>

I edited your XSL and made these changes in the XSL document below:

<xsl:stylesheet version="1.0" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>

    <xsl:template match="*"/>

    <xsl:template match="/">
        <xsl:text>&lt;?php </xsl:text>
        <xsl:apply-templates select="xsd:schema"/>
    </xsl:template>

    <xsl:template match="xsd:schema">
        <xsl:text>interface FromXsd {&#xa;</xsl:text>
        <xsl:apply-templates select="xsd:element"/><xsl:text>&#xa;</xsl:text>
        <xsl:apply-templates select="xsd:annotation"/>
        <xsl:text>&#xa;}</xsl:text>
    </xsl:template>

    <xsl:template match="xsd:element">
        <xsl:apply-templates select="xsd:annotation"/>
        <xsl:text>    abstract public function build</xsl:text>
        <xsl:value-of select="normalize-space(@name)"/>
        <xsl:text>XmlString(</xsl:text>
        <xsl:value-of select="@type"/><xsl:text> $a</xsl:text>
        <xsl:text>);</xsl:text>
    </xsl:template>

    <xsl:template match="xsd:annotation">
        <xsl:text>    /* </xsl:text>
        <xsl:value-of select="normalize-space(.)"/>
        <xsl:text> */</xsl:text>
    </xsl:template>

</xsl:stylesheet>

If you have an input such as:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:element name="Foo" type="Bar"/>
    <xsd:simpleType name="Bar">
        <xsd:restriction base="xsd:string">
            <xsd:maxLength value="32"/>
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:annotation>
        <xsd:documentation>This is a comment</xsd:documentation>
    </xsd:annotation>
</xsd:schema>

It will produce the result below:

<?php interface FromXsd {
    abstract public function buildFooXmlString(Bar $a);
    /* This is a comment */
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top