How to validate XML variable against XSD in PostGreSQL - sql

I am trying to port an MS SQL script to execute in PostGreSQL. It uses an XML schema collection script that is defined as follows:
CREATE XML SCHEMA COLLECTION [MySchema] AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xsd:element name="MyRootElement">
<xsd:complexType> <xsd:complexContent> <xsd:restriction base="xsd:anyType"> <xsd:sequence> <xsd:element name="Child1" minOccurs="0"> <xsd:complexType> <xsd:complexContent> <xsd:restriction base="xsd:anyType"> <xsd:sequence>
.
.
Can I convert this XML schema type for use in PostGreSQL? So I can use the definition for stored procedure xml-type input parameter validation. If not, how do I ensure that the input parameter has the required schema?
I read that The xml type does not validate input values against a document type declaration (DTD), even when the input value specifies a DTD. There is also currently no built-in support for validating against other XML schema languages such as XML Schema

I would write a custom function (in PL/Perl or PL/Python perhaps) that does the required validation, and hook that to the column using a check constraint.

Related

XQuery Error when reading attribute from parentnode with *typed* XML (value() requires a singleton)

I have a simple XML:
DECLARE #x1 xml = '<r>
<o a="1">
<o a="2">
</o>
</o>
</r>';
And select the following:
SELECT r.o.value('(../#a)[1]','varchar(20)') FROM #x1.nodes('/r//o') r(o);
Everything is nice:
NULL
1
When I use typed XML:
CREATE XML SCHEMA COLLECTION [dbo].test AS '
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="r">
<xsd:complexType>
<xsd:complexContent>
<xsd:restriction base="xsd:anyType">
<xsd:sequence>
<xsd:element name="o" type="o" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="o">
<xsd:complexContent>
<xsd:restriction base="xsd:anyType">
<xsd:sequence>
<xsd:element name="o" type="o" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="a" type="xsd:string" />
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
</xsd:schema>'
GO
DECLARE #x2 xml(dbo.test) = '<r>
<o a="1">
<o a="2">
</o>
</o>
</r>';
SELECT r.o.value('(../#a)[1]','varchar(20)') FROM #x2.nodes('/r//o') r(o);
I got the Error:
XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *'
Why does the untyped XML works and the typed XML not?
It looks as though you well understand that this is not the usual problem of having to convince the parser that there is only one thing being passed into value(). This problem is specific to typed XML, as you have observed.
Using google I found this ancient blogpost with the promising text
rt is part of the XQuery 1.0/XPath 2.0 data model.
Most people get by that. The real fun starts when you do examples using untyped XML and XPath expressions with the text() node test. text() works just fine when using untyped XML, but fails against typed XML with simple content
It talks about the SQL Books Online and the SQL Server 2005 XML Best Practices paper, which I think is this book here. I haven't looked for a more up-to-date reference. But using what I found there I have been able to fix your problem: Simple replace
SELECT r.o.value('(../#a)[1]','varchar(20)') FROM #x2.nodes('/r//o') r(o);
with
SELECT r.o.value('fn:string(../#a)[1]','varchar(20)') FROM #x2.nodes('/r//o') r(o);
Basically, value() doesn't like being given values of a typed-xml-type, but wants strings. So we give it a string.

Invalid XML element location. (SQL-XSD)

I just started learning XML today. I was trying to create a 'sample' XSD and populate it but.. I made up my data and it looks fine, but I can't make this schema work..
CREATE XML SCHEMA COLLECTION GenreTestSchema
AS
<?xml version = "1.0" encoding = "UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!--create group for GENRELIST-->
<xsd:group name="GENRELISTGROUP">
<xsd:element name="GENRELIST">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="GENRE" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="REFERENCE" minOccurs="0" maxOccurs="100" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:group>
</xsd:schema>
Now If I remove this element
<xsd:element name="GENRELIST">
and all the code that goes with it so (element and complextype) then the schema works fine. I can't figure out what is wrong here and why it doesn't let me create this element?
Errorlog
Element <element> is not valid at location '/*:schema[1]/*:group[1]/*:element[1]'.
It talks about invalid location but I literally have no idea why?
Data sample
<GENRELIST xmlns="http://myGenres">
<GENRE GenreNo="1">
<GENRE>Fiction</GENRE>
<REFERENCE>Alien</REFERENCE>
</GENRE>
<GENRE GenreNo="2">
<GENRE>Tragedy</GENRE>
<REFERENCE>Titanic</REFERENCE>
</GENRE>
</GENRELIST>
The problem is that you're missing to take the namespace xmlns="http://myGenres" into account on the element GENRELIST.
So transform your anonymous complex type to a named type and use the namespace on it:
<?xml version = "1.0" encoding = "UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:gl="http://myGenres"
targetNamespace="http://myGenres"
elementFormDefault="qualified">
<xsd:complexType name="genrelist"> <!-- named type 'genrelist' -->
<xsd:sequence>
<xsd:element name="GENRE" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="REFERENCE" minOccurs="0" maxOccurs="100" />
</xsd:sequence>
</xsd:complexType>
<!--create group for GENRELIST-->
<xsd:element name="GENRELIST" type="gl:genrelist" /> <!-- applying the 'gl' namespace to named type 'genrelist' -->
</xsd:schema>
This way your XML will be validated.
For the purpose of targetNamespace look here and for
elementFormDefault look there.
I don't know why you're trying to create an xs:group (a "model group"). A model group is a reusable chunk of schema declarations that can be used in several places, handy when you have multiple elements with similar structure. It might sometimes make sense to have a group with only one reference to it, but it can't make sense to have a group with no references to it.
Now there's an additional (and related) problem). Typically when you start validating an instance document, the validator looks for a global element declaration whose name matches the root element of the instance. But you don't have a global element declaration in your schema; your declaration of GENRELIST isn't global because it is in a group.

Validate XML against a schema having any element

I have a schema say called BPMN20.xsd having an element called extensionElements.see sample below:
<xsd:element name="extensionElements" type="tExtensionElements" />
<xsd:complexType name="tExtensionElements">
<xsd:sequence>
<xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
Now the xml contains elements inside extensionElements, which conform to another schema say ufl.xsd. Example:
<extensionElements>
<ufl:java method="calculate"
class="com.companyx.orchestration.bpmn.ordermanagement.data.CalculateOrderAmount">
<ufl:arg type="com.companyx.orchestration.bpmn.ordermanagement.data.OrderLine"
var="item" />
</ufl:java>
</extensionElements>
Now i want to validate my XML against both the schemas. How to do this ?
If i just validate my xml against BPMN20.xsd it works (since content if extensionElements is defined as any. However i also want to validate the contents inside extensionElements against ufl.xsd
Please help...
Thanks,
Siddharth
Use processContents as lax in the schema where you defined the any element.
This will enforce the converter in finding the schema element for the passed xml.

any element in XML schema blocks XQuery

I have the following XML Schema:
CREATE XML SCHEMA COLLECTION test AS '
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="PointConf">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="GlobalFlags">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Order" type="OrderType"/>
<xsd:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="OrderType">
<xsd:attribute name="value" type="xsd:int" />
</xsd:complexType>
</xsd:schema>
'
GO
Then I use it in this way:
DECLARE #xml xml(test)
SET #xml='<PointConf>
<GlobalFlags>
<Order value="1" />
</GlobalFlags>
</PointConf>'
SELECT #xml.value('(/PointConf/GlobalFlags/Order/#value)[1]','int')
SELECT gives me the following error:
XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type '(xs:int | xdt:anyAtomicType *) ?'
Without the xsd:any element in the schema the code above works without any errors. What am I doing wrong?
I got the select to work by using the statement
SELECT #xml.value('string(/PointConf[1]/GlobalFlags/Order[1]/#value)','int')
I understand the requirement for the [1] index on the Order node, as this can be a list, however I don't see why its required for the PointConf node.
The [1] needs to be used at the actual level where a list exists to restrict that list to a single return value
The string(...) turns the node set into a string (or empty string). I think this helps with the xsd:any although I'm not completely sure why - something to do with handling the possibility of the node Order being missing completely I think.
Update:
Investigating further:
SELECT #xml.value('string((/PointConf/GlobalFlags/Order/#value)[1])','int')
also works.
So its just the string function that's required to make it work in this instance.

SQL Server 2008: How to use SQL to output XML from Query?

I would like to declare a variable 'XMLOutput' and have it produce the contents of a table in XML format. If you could provide a really simple example I could work off of I would really appreciate it. I tried using the xmlelement() but could not get it to work.
SQL Server provides the ability to generate XML based on table structure via the FOR XML clause. It's options are:
RAW
AUTO
PATH
EXPLICIT
There are examples for each in the link.
Try using
FOR XML RAW
In the end of your query. This will return results as XML. Does that do what you want ? If not, I think you might need to elaborate your question a bit further. You could also have a look at the documentation, to see what options you have with FOR XML.
create xml schema collection cricketschemacollection
AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLschema">
<xsd:element name="MatchDetails">
<xsd:complexType>
<xsd::complexContent>
<xsd:restiriction base="xsd:anyType">
<xsd:sequences>
<xsd:element name="Team" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd::complexContent>
<xsd:restiriction base="xsd:anyType">
<xsd:sequences/>
<xsd:attribute name="country"type="xsd:string"/>
<xsd:attribute name="score"type="xsd:string"/>
</xsd:restiriction>
</xsd::complexContent>
</xsd:complexType>
</xsd:element>
</xsd:sequences>
</xsd:restiriction>
</xsd::complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>'