What is Query XSD Analysis?
Query XSD Analysis is a set of features that provides the ability to analyse XML Schemas (XSD) files using common query languages. Typical analysis tasks include checking for conformance to naming conventions, use of anonymous types, elements and attributes, namespaces, spell checking, organizational patterns and detection and analysis of class hierarchies.
Generated reports are typically used as part of an ongoing quality and compliance programs, and are usually integrated with an automated build processes. A report item pinpoints the exact line and column position in the XSD file related to analysis item; integrating specific messages providing details about the test condition further helps with the usability of the analysis result. The intent is to reduce the learning curve associated with developing XSD models within large organizations subject to complex conformance profiles.
Our team will release in the near future conformance packs, which consist of predefined rule bundles that capture best industry practices, as well as more specific design patterns to help with customizations of industry standard XSDs.
To analyze XSDs, the entire set of XSD files must be valid.
|
We currently support SQL only. |
Relational Metamodel
The relational metamodel used to construct the queries is available in the Model View tab of the QXSD designer. It is similar to the .NET XML Schema API.
The object browser on the right can be used to quickly find entities.
Examples
For illustration we're using the purchase order sample XSD published by W3C with XSD 1.0 Primer (a downloadable file is available here; we've posted a quick overview here). First step is to create an XSR XML Schema Collection object, and attach the po.xsd file to it. From the context menu on the Version object, invoke the Query XSD Analysis command.
Authoring Styles
There are four major authoring styles:
- Salami Slice
- Venetian Blind
- Russian Doll
- The Garden of Eden
For each of the authoring styles, we provide a list of rules to illustrate analysis examples. The rule sets are not meant to be exhaustive.
Salami Slice
Locally defined elements are not allowed.
The query below finds all locally defined elements; in effect, it produces the list of exceptions to the rule.
Select XSElement.LocalName,
XSObject.LineNumber,
XSObject.LinePosition,
XSObject.SourceUri
From XSElement
Inner Join XSObject On XSElement.RowId = XSObject.RowId
Where XSElement.RefLocalName Is Null And XSObject.IsGlobal = 0
Results:
Local Name |
Line Number |
Line Position |
Source Uri |
street |
28 |
5 |
file:///.../QTAssistant/po.xsd |
productName |
41 |
8 |
file:///.../QTAssistant/po.xsd |
state |
30 |
5 |
file:///.../QTAssistant/po.xsd |
city |
29 |
5 |
file:///.../QTAssistant/po.xsd |
shipTo |
17 |
5 |
file:///.../QTAssistant/po.xsd |
zip |
31 |
5 |
file:///.../QTAssistant/po.xsd |
item |
38 |
5 |
file:///.../QTAssistant/po.xsd |
shipDate |
51 |
8 |
file:///.../QTAssistant/po.xsd |
quantity |
42 |
8 |
file:///.../QTAssistant/po.xsd |
USPrice |
49 |
8 |
file:///.../QTAssistant/po.xsd |
items |
20 |
5 |
file:///.../QTAssistant/po.xsd |
billTo |
18 |
5 |
file:///.../QTAssistant/po.xsd |
name |
27 |
5 |
file:///.../QTAssistant/po.xsd |
User defined global types are not allowed.
The query below finds all the global types; in effect, it produces the list of exceptions to the rule.
Select XSType.LocalName,
XSObject.LineNumber,
XSObject.LinePosition,
XSObject.SourceUri
From XSType
Inner Join XSObject On XSType.RowId = XSObject.RowId
Where XSObject.IsGlobal = 1
Results
Local Name |
Line Number |
Line Position |
Source Uri |
PurchaseOrderType |
15 |
3 |
file:///.../QTAssistant/po.xsd |
USAddress |
25 |
3 |
file:///.../QTAssistant/po.xsd |
SKU |
60 |
3 |
file:///.../QTAssistant/po.xsd |
Items |
36 |
3 |
file:///.../QTAssistant/po.xsd |
Venetian Blind
Locally defined types are not allowed.
The query below finds all the local types; in effect, it produces the list of exceptions to the rule.
Select XSObject.LineNumber,
XSObject.LinePosition,
XSObject.SourceUri
From XSType
Inner Join XSObject On XSType.RowId = XSObject.RowId
Where XSObject.IsGlobal = 0
References to globally defined elements are not allowed.
This rule implies that all global elements are "there" to define XML root elements. The query below finds all particle elements that reference a global definition, thus it produces the list of exceptions to the rule.
Select XSElement.LocalName,
XSObject.LineNumber,
XSObject.LinePosition,
XSObject.SourceUri
From XSObject
Inner Join XSElement On XSElement.RowId = XSObject.RowId
Where XSElement.RefLocalName Is Not Null
Results:
Local Name |
Line Number |
Line Position |
Source Uri |
comment |
50 |
8 |
file:///.../QTAssistant/po.xsd |
comment |
19 |
5 |
file:///.../QTAssistant/po.xsd |
Russian Doll
User defined global types are not allowed.
See the same rule under Salami Slice.
References to globally defined elements are not allowed.
See the same rule under Venetian Blind.
The Garden Of Eden
Locally defined elements are not allowed.
See the same rule under Salami Slice.
Locally defined types are not allowed.
See the same rule under Venetian Blind.
Spell Check
The ability to spell check relies on buit in dictionaries. The user has the ability to override the built in dictionaries, and add a custom one to allow inclusion of specific terms in order to manage false hits.
A more complex SQL script, such as the one used in this example, requires the use of the SQL tab.
--// Spellcheck
DECLARE @dictkey NVARCHAR
DECLARE @optionsspelling INT
set @optionsspelling = SpellCheckOptionsMask(0, 0, 0, 0, 0, 0)
set @dictkey = SpellCheckAddDictionary('custom', 'PATH TO CUSTOM DICT', '', '', NULL, @optionsspelling)
set @dictkey = SpellCheckAddDictionary(NULL, NULL, NULL, NULL, NULL, @optionsspelling)
--// Splits camel case names for elements and types.
SELECT Names.LocalName, RegexSplitSpellCheckValid(
RegexReplace(
RegexReplace(
--// Use it to clean things that shouldn't go in
--// the custom dictionary such as prefixes, suffixes, etc.
RegexReplace(
Names.LocalName,
'(-Suffix$|^Prefix)',
'',
NULL
),
'(\P{Ll})(\P{Ll}\p{Ll})',
'$1 $2',
NULL
),
'(\p{Ll})(\P{Ll})',
'$1 $2',
NULL
), ' ', NULL, NULL, @dictkey), XSObject.LineNumber, XSObject.LinePosition, XSObject.SourceUri
FROM
(SELECT XSType.LocalName, XSType.RowId FROM XSType WHERE XSType.LocalName IS NOT NULL
UNION
SELECT XSElement.LocalName, XSElement.RowId FROM XSElement WHERE XSElement.LocalName IS NOT NULL
) AS Names
INNER JOIN XSObject on Names.RowId = XSObject.RowId
Results:
The result set captures in Column1 the offending component and, if applicable, suggestions.
Local Name |
Column1 |
Line Number |
Line Position |
Source Uri |
billTo |
|
18 |
5 |
file:///.../QTAssistant/po.xsd |
city |
|
29 |
5 |
file:///.../QTAssistant/po.xsd |
comment |
|
13 |
3 |
file:///.../QTAssistant/po.xsd |
comment |
|
19 |
5 |
file:///.../QTAssistant/po.xsd |
comment |
|
50 |
8 |
file:///.../QTAssistant/po.xsd |
item |
|
38 |
5 |
file:///.../QTAssistant/po.xsd |
items |
|
20 |
5 |
file:///.../QTAssistant/po.xsd |
Items |
|
36 |
3 |
file:///.../QTAssistant/po.xsd |
name |
|
27 |
5 |
file:///.../QTAssistant/po.xsd |
productName |
|
41 |
8 |
file:///.../QTAssistant/po.xsd |
purchaseOrder |
|
11 |
3 |
file:///.../QTAssistant/po.xsd |
PurchaseOrderType |
|
15 |
3 |
file:///.../QTAssistant/po.xsd |
quantity |
|
42 |
8 |
file:///.../QTAssistant/po.xsd |
shipDate |
|
51 |
8 |
file:///.../QTAssistant/po.xsd |
shipTo |
|
17 |
5 |
file:///.../QTAssistant/po.xsd |
SKU |
SKU - SK SKUA SKA SKI SKY |
60 |
3 |
file:///.../QTAssistant/po.xsd |
state |
|
30 |
5 |
file:///.../QTAssistant/po.xsd |
street |
|
28 |
5 |
file:///.../QTAssistant/po.xsd |
USAddress |
|
25 |
3 |
file:///.../QTAssistant/po.xsd |
USPrice |
|
49 |
8 |
file:///.../QTAssistant/po.xsd |
zip |
|
31 |
5 |
file:///.../QTAssistant/po.xsd |
By adding SKU to the custom dictionary, the offending line will clear out.