XQuery/XQuery and XML Schema
Motivation
[edit | edit source]In learning any modelling language, it is helpful to see sample instances of any formal model. This is equally true when developing an XML Schema. XML Development tools like Oxygen and XML-Spy have tools to generate a random instance of a supplied XML schema, but it is useful to have a web service to do this. The service can also be used for test data generation.
The following is a test XML Schema file:
<xs:group name="TechnicalOrderVersionChoiceGroup">
<xs:choice>
<xs:element name="ettcsMigrationTov" type="ord:ETTCSMigrationTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="ettcsNewSiteBuildTov" type="ord:ETTCSNewSiteBuildTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="ettcsNewCabinetTov" type="ord:ETTCSNewCabinetTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="ettcsSimpleTov" type="ord:ETTCSSimpleTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="lteNewCabinetTov" type="ord:LTENewCabinetTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="gsmoeTov" type="ord:GSMoETechnicalOrderVersionType" minOccurs="0" />
<xs:element name="ettcsUpspeedTov" type="ord:ETTCSUpspeedTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="ettcsRehomeTov" type="ord:ETTCSRehomeTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="ettcsRebalanceTov" type="ord:ETTCSRebalanceTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="retTechnicalOrderVersion" type="ord:RETTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="ettcsNSBNGUNIEVCUplinkTechnicalOrderVersion" type="ord:ETTCSNSBNGUNIEVCUplinkTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="ettcsNSBNGUNIEVCAdditionalNode" type="ord:ETTCSNSBNGUNIEVCAdditionalNodeTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="eireTov" type="ord:EIRETechnicalOrderVersionType" minOccurs="0" />
<!-- Granite Initiated Order Types -->
<xs:element name="addEttcsMtsoCollectorCircuitTechnicalOrderVersion" type="ord:AddEttcsMtsoCollectorCircuitTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="r2BToR4BConversionTechnicalOrderVersion" type="ord:R2BToR4BConversionTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="eoSONETMtsoCollectorCircuitTechnicalOrderVersion" type="ord:EoSONETMtsoCollectorCircuitTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="disconnectEthernetEvcSegmentTechnicalOrderVersion" type="ord:DisconnectEthernetEvcSegmentTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="ettcOc3MtsoR4DTechnicalOrderVersion" type="ord:EttcOc3MtsoR4DTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="gSMoEMtsoTechnicalOrderVersion" type="ord:GSMoEMtsoTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="disconnectLnsOewBaseSegmentTechnicalOrderVersionType" type="ord:DisconnectLnsOewBaseSegmentTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="disconnectLnsOewBaselinePathTechnicalOrderVersionType" type="ord:DisconnectLnsOewBaselinePathTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="discoEthernetSonetRehomeTov" type="ord:DiscoEthernetOverSonetForRehomeTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="ethernetSonetRehomeMtsoTov" type="ord:EthernetOverSonetRehomeMtsoTechnicalOrderVersionType" minOccurs="0" />
<!-- Iteration 17 - Two new Granite Initiated Order Types - for Aug2013 -->
<xs:element name="gEttcsNSBNextGenUniEvcDualUplinkTov" type="ord:EttcsNSBNextGenUNIEVCDualUplinkTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="gEttcsNSBNextGenUniEvcMultiCabTov" type="ord:EttcsNSBNextGenUNIEVCMultiCabTechnicalOrderVersionType" minOccurs="0" />
<!-- R12.1 INDOOR SMALL CELL EAMIS -->
<xs:element name="smallCellEamis" type="ord:IndoorSmallCellEamisTechnicalOrderVersionType" minOccurs="0" />
<!-- R13.1 Rapid Deployment-->
<xs:element name="eoSonetOc12MtsoCollectorCircuitTov" type="ord:EoSonetOc12MtsoCollectorCircuitTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="indoorSmallCellBdslTov" type="ord:IndoorSmallCellBdslTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="indoorSmallCell3RdPartyTov" type="ord:IndoorSmallCell3RdPartyTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="indoorSmallCellSharedTransportTov" type="ord:IndoorSmallCellSharedTransportTechnicalOrderVersionType" minOccurs="0" />
<!-- MLPPP WO Types -->
<xs:element name="mlpppMigrationUMTS" type="ord:MLPPPMigrationUMTSTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="mlpppMigrationDS1UMTS" type="ord:MLPPPMigrationDS1UMTSTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="mlpppNewCabinetAddLTE" type="ord:MLPPPNewCabinetAddLTETechnicalOrderVersionType" minOccurs="0" />
<xs:element name="mlpppNewCabinetAddDS1LTE" type="ord:MLPPPNewCabinetAddDS1LTETechnicalOrderVersionType" minOccurs="0" />
<xs:element name="mlpppNewCabinetAddUMTS" type="ord:MLPPPNewCabinetAddUMTSTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="mlpppNewCabinetAddDS1UMTS" type="ord:MLPPPNewCabinetAddDS1UMTSTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="mlpppUpspeed" type="ord:MLPPPUpspeedTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="mlpppUpspeedDS1" type="ord:MLPPPUpspeedDS1TechnicalOrderVersionType" minOccurs="0" />
<xs:element name="mlpppRehome" type="ord:MLPPPRehomeTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="mlpppRehomeDS1" type="ord:MLPPPRehomeDS1TechnicalOrderVersionType" minOccurs="0" />
<xs:element name="mlpppRebalance" type="ord:MLPPPRebalanceTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="mlpppRebalanceDS1" type="ord:MLPPPRebalanceDS1TechnicalOrderVersionType" minOccurs="0" />
<!-- ESCORE Initiated SmallCell WO Type - since R14.0 -->
<xs:element name="smallcellTransport" type="ord:SmallCellTransportTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="smallcellSCR" type="ord:SmallCellSCRTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="smallcellSCS" type="ord:SmallCellSCSTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="smallcellAP" type="ord:SmallCellAPTechnicalOrderVersionType" minOccurs="0" /> <!-- Self install and Disconnect will use this same AP payload -->
<!-- Since R1406 - Two new ETTCS automation IPv6 Types -->
<xs:element name="ettcsIPv6Siad" type="ord:ETTCSIPv6MigrationSIADTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="ettcsIPv6ENodeB" type="ord:ETTCSIPv6MigrationENodeBTechnicalOrderVersionType" minOccurs="0" />
<!-- Since R1404 - RD Types -->
<xs:element name="ettcsUniversalEvcDownspeed" type="ord:EttcsUniversalEvcDownspeedTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="outdoorSmallCell3rdPartytTov" type="ord:OutdoorSmallCell3rdPartyTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="outdoorSmallCellBdslTov" type="ord:OutdoorSmallCellBdslTechnicalOrderVersionType" minOccurs="0" />
<!-- Since R1406 - ETTCS Site Migration -->
<xs:element name="ettcsSiteMigrBackhaul" type="ord:ETTCSSiteMigrationBackhaulTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="ettcsSiteMigrShorthaul" type="ord:ETTCSSiteMigrationShorthaulTechnicalOrderVersionType" minOccurs="0" />
<!-- Since R1410 - IUB Trunking Support -->
<xs:element name="iubTrunkingTov" type="ord:IUBTrunkingTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="infrastructureTov" type="ord:InfrastructureTechnicalOrderVersionType" minOccurs="0" />
<xs:element name="EOMWTargetTov" type="ord:ETTCSNSBNGUplinkEoMWTargetTechnicalOrderVersionType" minOccurs="0" />
</xs:choice>
</xs:group>
XQuery to convert Simple XML Schema into an Instance Document
[edit | edit source]We can use a recursive function to convert this XML Schema into an instance document. Here is an implementation of this function which covers some of the key XML Schema constructs:
declare namespace xs = "http://www.w3.org/2001/XMLSchema";
declare function local:process-element($element as node()) {
typeswitch ($element)
case element(xs:complexType) return
for $sub-element in $element/*
return local:process-element($sub-element)
case element(xs:annotation) return
()
case element(xs:element) return
let $min := ($element/@minOccurs,1)[1]
let $max :=
if ($element/@maxOccurs = "unbounded")
then 10
else ($element/@maxOccurs,1)[1]
let $count := $min + round(math:random() * ($max - $min))
return
if ($count >0 )
then
for $i in 1 to $count
return
if ($element/@ref)
then
local:process-element(root($element)/xs:schema/xs:element[@name=$element/@ref])
else
if (root($element)/xs:schema/xs:complexType[@name=$element/@type])
then
element {$element/@name} {
local:process-element(root($element)/xs:schema/xs:complexType[@name=$element/@type])
}
else
element {$element/@name} {
if ($element/*)
then for $sub-element in $element/*
return local:process-element($sub-element)
else local:process-type(($element/@type,"xs:string")[1])
}
else ()
case element(xs:attribute) return
if ($element/@use="required" or math:random() > 0.5)
then
attribute {$element/@name} {
local:process-type($element/@type)
}
else ()
case element(xs:sequence) return
for $sub-element in $element/*
return local:process-element($sub-element)
case element(xs:all) return
for $sub-element in $element/*
order by math:random()
return local:process-element($sub-element)
case element(xs:choice) return
let $count := count($element/*)
let $i := ceiling(math:random()*$count)
let $sub-element := $element/*[ $i]
return local:process-element($sub-element)
default return
()
};
This function uses the element construct to create a new XML instance tree based on the information in the XML Schema file and the typeswitch construct to select the appropriate processing of a given element type.
Type Instance Generation
[edit | edit source]Some helper functions are required to generate random instances of (some of ) the standard types.
declare function local:pad($string as xs:string, $char as xs:string, $max as xs:integer) as xs:string {
concat(string-pad($char,$max - string-length($string)), $string)
};
declare function local:pad2($string) {
local:pad($string,"0",2)
};
declare function local:process-type($name ) {
(: just simple types - xs namespace :)
if ($name = ("xs:string","xs:NMTOKEN","xs:NCName"))
then string-join(
for $c in 1 to math:random() * 12
return ("A","B","C","D")[ceiling(math:random() * 4)]
, "")
else if ($name="xs:integer")
then round(math:random()*100)
else if ($name="xs:decimal")
then math:random() * 100 - 50
else if ($name="xs:boolean")
then ("true","false")[ ceiling( math:random() *2)]
else if ($name="xs:date")
then string-join((1990 + round(math:random() * 20),
local:pad2(ceiling(math:random()*12)) ,
local:pad2(ceiling(math:random()*30))),"-")
else if ($name="xs:time")
then string-join((local:pad2(floor(math:random() * 24)),
local:pad2(floor(math:random()*60)) ,
local:pad2(floor(math:random()*60))),":")
else if ($name="xs:dateTime")
then concat (
string-join((1990 + round(math:random() * 20),
local:pad2(ceiling(math:random()*12)) ,
local:pad2(ceiling(math:random()*30))),"-"),
"T",
string-join((local:pad2(floor(math:random() * 24)),
local:pad2(floor(math:random()*60)) ,
local:pad2(floor(math:random()*60))),":")
)
else ()
};
Script
[edit | edit source]Here is a basic script which applies this function to the example schema. By default, the root of the generated instance is the first element in the schema or a named element if a root parameter is supplied.
let $file := request:get-parameter("file",())
let $root := request:get-parameter("root",())
let $schema := doc($file)
return
if ($root)
then local:process-element($schema/xs:schema/xs:element[@name=$root])
else local:process-element($schema/xs:schema/xs:element[1])
Sample Output
[edit | edit source]<Root>
<Start>C</Start>
<Any>
<AnyA>BADADAD</AnyA>
<AnyC>BA</AnyC>
<AnyB>D</AnyB>
</Any>
<Multiple>
<Choice>
<Third AttributeDecimal="8.937041402178778" AttributeTime="20:38:04" AttributeDateTime="1995-08-06T21:08:43"/>
</Choice>
<Unbounded>BDAACAAAA</Unbounded>
<Ref>
<Ref1>52</Ref1>
<Ref2>1992-09-02</Ref2>
</Ref>
<WithRefType>
<RefTypeA>BBBDD</RefTypeA>
<RefTypeB>DAADCABDC</RefTypeB>
</WithRefType>
</Multiple>
<Multiple>
<Choice>
<Second AttributeString="ADAB" AttributeDate="2005-12-16"/>
</Choice>
<Unbounded/>
<Unbounded>CABBB</Unbounded>
<Unbounded/>
<Unbounded>B</Unbounded>
<Unbounded>AACBABAABCB</Unbounded>
<Ref>
<Ref1>56</Ref1>
<Ref2>1992-05-22</Ref2>
</Ref>
<WithRefType>
<RefTypeA>CDAA</RefTypeA>
<RefTypeB>CBDBDBDB</RefTypeB>
</WithRefType>
<Optional>CC</Optional>
</Multiple>
<Multiple>
<Choice>
<First AttributeInt="21" AttributeString="AABC" AttributeBoolean="true"/>
</Choice>
<Unbounded>ADADDDDDB</Unbounded>
<Unbounded>DCDCBDDD</Unbounded>
<Unbounded>CBAD</Unbounded>
<Unbounded>DAAAD</Unbounded>
<Ref>
<Ref1>13</Ref1>
<Ref2>1997-04-30</Ref2>
</Ref>
<WithRefType>
<RefTypeA>CB</RefTypeA>
<RefTypeB/>
</WithRefType>
</Multiple>
<Multiple>
<Choice>
<Second AttributeString="DBDA" AttributeDate="1999-05-16"/>
</Choice>
<Unbounded>CCDDDDCD</Unbounded>
<Unbounded>DDABACC</Unbounded>
<Ref>
<Ref1>44</Ref1>
<Ref1>30</Ref1>
<Ref1>57</Ref1>
<Ref2>1997-01-26</Ref2>
</Ref>
<WithRefType>
<RefTypeA>CBC</RefTypeA>
<RefTypeB>CA</RefTypeB>
</WithRefType>
</Multiple>
<End>95</End>
</Root>
To do
[edit | edit source]- full set of xml types
- mixed
- restriction (only enumeration so far)
- Group
- AttributeGroup
- problem with missing attributes in a complexType
- hinting for distributions
- namespaces
- randomisation configuration