
Anyway, what do you think?
xs:alternative
instruction which allows you to change the type used to validate the element based on some condition. Instead of defining one type and then adding assertions to check the variations, just define one type per variation, then assign that type based on the condition. xs:alternative
as a child of xs:element
. Here's an example of a co-constraint - this
and that
are allowed based on the value of the type
attribute of node
- and how to validate it:<root>
<node type="A">
<this/>
</node>
<node type="B">
<that/>
</node>
</root>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:element name="root" type="root"/>
<xs:element name="node" type="node">
<xs:alternative type="node-type-A" test="@type = 'A'"/>
<xs:alternative type="node-type-B" test="@type = 'B'"/>
</xs:element>
<xs:element name="this"/>
<xs:element name="that"/>
<xs:complexType name="root">
<xs:sequence>
<xs:element ref="node" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<-- Base type -->
<xs:complexType name="node">
<xs:sequence>
<xs:any/>
</xs:sequence>
<xs:attribute name="type" type="allowed-node-types"/>
</xs:complexType>
<xs:simpleType name="allowed-node-types">
<xs:restriction base="xs:string">
<xs:enumeration value="A"/>
<xs:enumeration value="B"/>
</xs:restriction>
</xs:simpleType>
<-- Type A -->
<xs:complexType name="node-type-A">
<xs:complexContent>
<xs:restriction base="node">
<xs:sequence>
<xs:element ref="this"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<-- Type B -->
<xs:complexType name="node-type-B">
<xs:complexContent>
<xs:restriction base="node">
<xs:sequence>
<xs:element ref="that"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
</xs:schema>
<elem type="typeA">
<typeA/>
</elem>
<elem type="typeB">
<typeB/>
</elem>
<elem>
based on the value of the type attribute.
<elem type="typeA" xsi:type="elem_typeA">
<typeA/>
</elem>
<elem type="typeB" xsi:type="elem_typeB">
<typeB/>
</elem>
<xs:complexType name="elem_typeA">
<xs:sequence>
<xs:element ref="typeA"/>
...
<xs:complexType name="elem_typeB">
<xs:sequence>
<xs:element ref="typeB"/>
...
document(@path)
<xsl:variable name="path" select="@path" as="xs:string"/>
...
document($path)
@path
contains a relative path, then you could get a document not found error, or worse if your XML and XSLT are in the same directory, you won't notice...
<foo bar="bar" baz="baz"/>
<xs:complexType name="foo">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="bar" type="xs:string"/>
<xs:attribute name="baz" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<currency refid="001"/> <!-- 001 is Sterling -->
<currency refid="002"/> <!-- 002 is Euros -->
<currency refid="003"/> <!-- 003 is United States Dollars -->
public enum Currency {
GBP ("001", "GBP", "Sterling"),
USD ("002", "EUR", "Euros"),
USD ("003", "USD", "United States Dollar");
private final String refId;
private final String code;
private final String desc;
Currency(String refId, String code, String desc) {
this.refId = refId;
this.code = code;
this.desc = desc;
}
public String refId() {
return refId;
}
public String code() {
return code;
}
public String desc() {
return desc;
}
// Returns the enum based on it's property rather than its name
// (This loop could possibly be replaced with a static map, but be aware
// that static member variables are initialized *after* the enum and therefore
// aren't available to the constructor, so you'd need a static block.
public static Currency getTypeByRefId(String refId) {
for (Currency type : Currency.values()) {
if (type.refId().equals(refId)) {
return type;
}
}
throw new IllegalArgumentException("Don't have enum for: " + refId);
}
}
Currency.getTypeByRefId(String refId)
passing in the @refid from the XML. The benefit of using the Enum is that you can then do things like:if (currency.equals(Currency.GBP))
currency.refId()
and currency.desc()
to get to the other values. /net.sf.saxon.sql.SQLElementFactory
"oracle.jdbc.driver.OracleDriver
"jdbc:oracle:thin:@1.2.3.4:1234:sid
" (note the colon between thin and @ - I missed that first time round) where the IP, port and sid are placeholders for the real valuessaxon8-sql.jar
and ojdbc14.jar
needed to be on the classpath
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:sql="/net.sf.saxon.sql.SQLElementFactory"
exclude-result-prefixes="xs"
extension-element-prefixes="sql">
<xsl:output indent="yes"/>
<xsl:param name="driver"
select="'oracle.jdbc.driver.OracleDriver'"
as="xs:string"/>
<xsl:param name="database"
select="'jdbc:oracle:thin:@123.123.123.123:1234:sid'"
as="xs:string"/>
<xsl:param name="user" select="'un'" as="xs:string"/>
<xsl:param name="password" select="'pw'" as="xs:string"/>
<xsl:variable name="connection"
as="java:java.sql.Connection"
xmlns:java="http://saxon.sf.net/java-type">
<sql:connect driver="{$driver}" database="{$database}"
user="{$user}" password="{$password}"/>
</xsl:variable>
<xsl:template match="/" name="main">
<root>
<sql:query connection="$connection"
table="some_table"
column="*"
row-tag="row"
column-tag="col"/>
</root>
</xsl:template>
</xsl:stylesheet>
<root>
<row>
<col>data1</col>
<col>data2</col>
<col>data3</col>
<col>data4</col>
</row>
....
</root>
<root>
is the wrapper element, and <row>
and <col>
are the element names specified in the <sql:query>
element.
<node>I'm a node</node>
<node>I'm a node</node>/data(.)
<root>
<node>foo</node>
<node>bar</node>
</root>/node/data(.)
<root>
<node>foo</node>
<node>bar</node>
</root>/node[1]
<node>foo</node>
_howmany
parameter on the query - without this it defaults to 10.