XSD: constrain attribute value of element to a substring of parent attribute - java

Consider this piece of XML
<Parent id="MyParent1">
<Child id="MyParent1.MyChild1"/>
</Parent>
<Parent id="MyParent2">
<Child id="MyParent2.MyChild1"/>
<Child id="MySillyIncorrectPrefix.MyChild2"/>
</Parent>
How can I validate (possibly with XSD) Child elements whose id contains the Parent element id as a prefix, so that:
<Child id="MyParent2.MyChild1"/> <!-- is valid -->
<Child id="MySillyIncorrectPrefix.MyChild2"/> <!-- is not valid -->
I'm not bound to XSD version 1.0, so I could try with XSD 1.1 (and features like assertions), but I would like to know:
how to express the above constraint with an xs:assertion or more suitable XSD 1.1
feature.
if it's possible to build a validator in Java leveraging on
that xsd? "Xerces-J" is a feasible solution ?
In my limited knowledge of XSD 1.1 I came up with this attempt:
<xs:element name="Child">
<xs:complexType>
<xs:attribute name="type" type="xs:String" />
<xs:assert test="starts-with(#type,../#type)"/>
</xs:complexType>
</xs:element>
Is this correct? Is there a tool where I could test it?
Being more general: is there a tool to assist the build&test of such XSD 1.1 featured schemas? (afaik Eclipse supports only XSD 1.0)

What you describe is not feasible in XSD 1.0.
In XSD 1.1 you can use assertions on the type of the parent to require each child's id attribute to begin with the value of the parent's id attribute.

This schema should validate the example (use an xsd 1.1 assertion)
<?xml version="1.1" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/stackoverflow" xmlns:tns="http://www.example.org/stackoverflow" elementFormDefault="qualified">
<complexType name="ParentType">
<sequence>
<element name="Child" type="tns:ChildType"
maxOccurs="unbounded" minOccurs="0">
</element>
</sequence>
<attribute name="id" type="string"></attribute>
</complexType>
<element name="Parent" type="tns:ParentType"></element>
<complexType name="ChildType">
<attribute name="id" type="string"></attribute>
<assert test="starts-with(#id,../#id)"/>
</complexType>
</schema>

Related

Using SAML Assertion in XSD

I have a webservice operation where i'll be getting SAML Assertion as part of the request Body.
I have following XSD:
<xsd:element name="CreateRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="info" type="SomeRequestObj"/>
<xsd:element ref="saml:Assertion" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
The saml:Assertion refers to:
<xsd:import namespace="urn:oasis:names:tc:SAML:2.0:assertion"schemaLocation="../samlv2_0/saml-schema-assertion-2.0.xsd"/>
This saml schema is copied from SAML 2.0.
This generates classes with name *Type.java.
And i am having a hard time creating a unit test for this (which is a separate application with UI).
My Request requires a SAML AssertionType element in the request Body.
So, i cannot use OpneSaml for generating that as it gives me a SAML Assertion object and not AssertionType.
I tried generating the AssertionType object manually but i am having a hard time doing so.
Is there a way to use OpenSaml for generating this?
As i see the xml is going to be the same that i would get in case i just use OpenSaml to generate Assertion object.
Is there a way to simplify this?
EDIT: Added XSD snippet of Assertion
<element name="Assertion" type="saml:AssertionType"/>
<complexType name="AssertionType">
<sequence>
<element ref="saml:Issuer"/>
<element ref="ds:Signature" minOccurs="0"/>
<element ref="saml:Subject" minOccurs="0"/>
<element ref="saml:Conditions" minOccurs="0"/>
<element ref="saml:Advice" minOccurs="0"/>
<choice minOccurs="0" maxOccurs="unbounded">
<element ref="saml:Statement"/>
<element ref="saml:AuthnStatement"/>
<element ref="saml:AuthzDecisionStatement"/>
<element ref="saml:AttributeStatement"/>
</choice>
</sequence>
<attribute name="Version" type="string" use="required"/>
<attribute name="ID" type="ID" use="required"/>
<attribute name="IssueInstant" type="dateTime" use="required"/>
</complexType>
This generates AssertionType Object.
SAML Assertions are of complex type "AssertionType", but the element name is "Assertion". The <Assertion> element generated by OpenSaml should be just fine.
The element is defined in section 2.3.3 in the SAML core spec.
Try to use an external binding file when generating the classes from the XSD with JAXB. See this topic (I guess the second answer of it is what you're looking for): JAXB: How to change XJC-generated classes names when attr type is specified in XSD?

Xsd conditional implementation

I am trying to validate a pdf files documentation using xsd, where I convert the given pdf to xml and parse it through the schema xsd, and it validates, but lets assume there is a heading and it has 2 subheadings how do I change to xsd schema such that for a particular type of heading it should and must have minimum 2 subheadings of particular text(words/sentences), how do I add conditions to the xsd file for it validate specifically designed documents ?
here is the xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="elements">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" ref="element"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="element">
<xs:complexType>
<xs:sequence>
<xs:element ref="pageno"/>
</xs:sequence>
<xs:attribute name="level" use="required" type="xs:integer"/>
<xs:attribute name="title" use="required"/>
<xs:attribute name="type" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="pageno" type="xs:integer"/>
</xs:schema>
and here is the xml I used to generate this xsd:
<elements>
<element type ="Introduction" level="1" title="Introduction">
<pageno>4</pageno>
</element>
<element type ="Introduction" level="2" title="Enhancements to the HP CSA vCenter Simple Compute">
<pageno>4</pageno>
</element>
<element type ="System requirements" level="1" title="System requirements">
<pageno>5</pageno>
</element>
<element type ="System requirements" level="2" title="Software components">
<pageno>5</pageno>
</element>
<element type ="Configuration requirements" level="1" title="Configuration requirements">
<pageno>7</pageno>
</element>
<element type ="Configuration requirements" level="2" title="Installing content capsule">
<pageno>7</pageno>
</element>
<element type ="Configuring offerings in HP CSA" level="1" title="Configuring offerings in HP CSA">
<pageno>8</pageno>
</element>
<element type ="Configuring offerings in HP CSA" level="2" title="Configuring subscriber options">
<pageno>8</pageno>
</element>
<element type ="Configuring subscriber options" level="2" title="Adding providers">
<pageno>8</pageno>
</element>
<element type ="Adding providers" level="2" title="Associating resource offerings with providers">
<pageno>9</pageno>
</element>
<element type ="Associating resource offerings with providers" level="2" title="Changing component properties">
<pageno>10</pageno>
</element>
<element type ="Changing component properties" level="2" title="Creating the service offering">
<pageno>12</pageno>
</element>
<element type ="Creating the service offering" level="2" title="Publishing the service offering">
<pageno>13</pageno>
</element>
<element type ="Publishing the service offering" level="3" title="Publishing service offering to a Catalog">
<pageno>13</pageno>
</element>
<element type ="Subscribing to the service" level="1" title="Subscribing to the service">
<pageno>14</pageno>
</element>
<element type ="Subscribing to the service" level="2" title="Canceling a subscription">
<pageno>14</pageno>
</element>
<!-- <element type ="adasdasd" level = "5" title= "dasdsad">
</element> -->
<element type ="Limitations" level="1" title="Limitations">
<pageno>16</pageno>
</element>
<element type ="Appendix A: HP Operations Orchestration flows" level="1" title="Appendix A: HP Operations Orchestration flows">
<pageno>17</pageno>
</element>
<element type ="Appendix B: Integrating with IP Address Management solutions" level="1" title="Appendix B: Integrating with IP Address Management solutions">
<pageno>19</pageno>
</element>
<element type ="Additional resources" level="1" title="Additional resources">
<pageno>20</pageno>
</element>
<element type ="Send Documentation Feedback" level="1" title="Send Documentation Feedback">
<pageno>21</pageno>
</element>
</elements>
If you think I am lacking in clarity in question then please let me know I will answer any queries.
Thank you
You can define a Schema type of your own that is a heading that must contain specific text, and use the conditional type assignment feature of XSD 1.1, if your implementation supports it.
A more widely supported approach is to embed Schematron rules in your schema to do what you want.
Remember that if you over-constrain your input the schema may become fragile -- that is, hard to maintain in the event of changes.

XSD Configuration to Reference Attributes Configured in XML file at Runtime

Question: Is there a way (with something like tns:) to set up the .xsd configuration for an XML file to reference a list of attributes that are configured in XML File at runtime?
This is my situation:
I have an .xsd schema file, Labels.xsd for an xml file Labels.xml.
Labels.xsd contains the following:
<complexType name="LabelList">
<sequence>
<element name="label" type="tns:MyLabel" maxOccurs="unbounded"
minOccurs="1"/>
</sequence>
</complexType>
<complexType name="MyLabel">
<attribute name="labelName" type="token" use="required"/>
<!-- There are other attributes and sequences in MyLabel -->
</complexType>
Users can add to the Labels.xml file and add their own custom labels and change the names while my java application runs. The changes are picked up at run time.
I have another file, MyMainTable.xml that uses the schema from Tables.xsd. This file creates a table using the labels from the Labels.xml file above. The schema is in a different schema file than the schema for the Labels.xml file.
The Tables.xml file contains the following:
<complexType name="MyMainTable">
<sequence>
<element name="table" maxOccurs="unbounded">
<complexType>
<sequence>
<element name="tableElement" maxOccurs="unbounded">
<complexType>
<attribute name="name" type="string" use="required">
<annotation>
<documentation>
This should be one of the types of
MyLabel.label.labelName that are configured in the
LabelList sequence
Users can change the names of and add to the
types in MyLabel.labelName xml file.
</documentation>
</annotation>
</attribute>
</complexType>
</element>
</sequence>
<!-- There are other attributes in this type, but they are not relevant -->
</complexType>
</element>
</sequence>
</complexType>
Example of the Labels.xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Labels xmlns="<my schema locaiton>">
<FavoriteLabels>
<label labelName="LabelA">
<!-- other attributes such as color -->
</label>
<label labelName="LabelB">
<!-- other attributes such as color -->
</label>
<label labelName="LabelC">
<!-- other attributes such as color -->
</label>
</FavoriteLabels>
<OtherLabels>
<label labelName="OtherA">
<!-- other attributes such as color -->
</label>
<label labelName="OtherB">
<!-- other attributes such as color -->
</label>
<label labelName="OtherC">
<!-- other attributes such as color -->
</label>
</OtherLabels>
</Labels>
The name in the tableElement can be in the FavoriteLabels list or the OtherLabels list
Currently I do the check in my Java code when the table is built, but I would like the check to be done when the XML file is parsed. Similar to how a check is down for an Enum configured in an .xsd file.
Is there a way in the .xsd configuration to ensure the name attribute of the tableElement is contained in the list of the labels the user has configured at runtime in the Labels.xml file?
I adapted your example below. It might capture the problem which you are trying to solve or at least show you a possible strategy.
Here is a XML file with both labels and tables.
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://my-namespace my-schema.xsd"
xmlns="http://my-namespace">
<labels>
<label label="LabelA"/>
<label label="LabelB"/>
<label label="LabelC"/>
</labels>
<tables>
<table name="LabelB"/>
<table name="LabelB"/>
<table name="LabelA"/>
<table name="LabelC"/>
</tables>
</root>
You can probably use separate files (I wasn't able to do it using only XML Schema because the key/keyref association uses XPath; but it might be possible, or you can merge the files in memory before processing them).
In the schema, I defined a key/keyref pair associating the labels to the tables. The associations are made using XPath in the context of the element where they are defined. I used ID and IDREF as token types because they are stricter and guarantee uniqueness in the whole document.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
xmlns:tns="http://my-namespace" targetNamespace="http://my-namespace">
<xs:complexType name="LabelList">
<xs:sequence>
<xs:element name="label" maxOccurs="unbounded" minOccurs="1">
<xs:complexType>
<xs:attribute name="label" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TableList">
<xs:sequence>
<xs:element name="table" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:IDREF" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="labels" type="tns:LabelList"/>
<xs:element name="tables" type="tns:TableList"/>
</xs:sequence>
</xs:complexType>
<!-- These are the keys and references; they are in the current element's context -->
<xs:key name="LabelsKey">
<xs:selector xpath="labels/label"/>
<xs:field xpath="#label"/>
</xs:key>
<xs:keyref name="TablesRef" refer="tns:LabelsKey">
<xs:selector xpath="tables/table"/>
<xs:field xpath="#name"/>
</xs:keyref>
</xs:element>
</xs:schema>
Now if you add a table of a label which is not defined on the list, such as:
<table name="LabelZ"/>
You will get a validation error.

Generated XSD does not unmarshall the XML

I have an XML string, and I could not use the supplied XSD to unmarshal the object in java. So I tried to use an online tool (www.freeformatter.com/xsd-generator.html) to generate a valid xsd and got the same error. I don't understand what I'm seeing.
Here's the XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Message xmlns:ns1="http://www.domain.com/ws" xmlns="http://www.domain.com/ws/protocol">
<HeaderMessage>
<MSGTYPE>reply</MSGTYPE>
<ORIGINATOR>XXXX</ORIGINATOR>
<SENDER>XXXX</SENDER>
<TIMESTAMP>2013-12-12 17:48:09.649</TIMESTAMP>
<IDPROCESS>2013-12-12 17:48:09.649</IDPROCESS>
<IDMESSAGE>AN-1386866889649</IDMESSAGE>
<IDREQUEST>AN-1386866889649</IDREQUEST>
<SERVICENAME>RESULT</SERVICENAME>
<ERRORFLAG>OK</ERRORFLAG>
<ERRORCODE>300</ERRORCODE>
<ERRORMSG>Success</ERRORMSG>
</HeaderMessage>
<BodyMessage>
<ns1:ServiceResultObject isin="XX0000000000">
<ns1:ResultObject value="true" codIsin="XX0000000000" />
</ns1:ServiceResultObject>
</BodyMessage>
</Message>
And here's the XSD I got from the tool:
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.domain.com/ws" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="ServiceResultObject">
<xs:complexType>
<xs:sequence>
<xs:element name="ResultObject">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="value"/>
<xs:attribute type="xs:string" name="codIsin"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute type="xs:string" name="isin"/>
</xs:complexType>
</xs:element>
</xs:schema>
After I generate the classes, I get the error
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.domain.com/ws/protocol", local:"Message"). Expected elements are <{http://www.domain.com/ws}ServiceResultObject>
Why do I lose all this header information? Why does the XSD not result in a schema that actually unmarshals the object? The XSD supplied by the service guys here also only defined the inner object.
Since your XML document has 2 namespaces (http://www.domain.com/ws/protocol & http://www.domain.com/ws) you are going to need 2 XML schemas to represent it. One schema can reference another with an import element.
XML Schemas
Below I have started the XML Schemas that you will need for your XML.
ws.xsd (for http://www.domain.com/ws namespace)
This is part of the XML schema for the http://www.domain.com/ws. The whole one is what you have already generated.
<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.domain.com/ws"
xmlns:tns="http://www.domain.com/ws"
elementFormDefault="qualified">
<element name="ServiceResultObject">
<complexType>
<sequence/>
<attribute name="isin" type="string"/>
</complexType>
</element>
</schema>
ws_protocol.xsd (for http://www.domain.com/ws/protocol namespace)
Here is a partial version of the schema that you are missing for the http://www.domain.com/ws/protocol namespace. Note the import element that references the other XML Schema, and <element ref="ws:ServiceResultObject"/> which references an element from the other XML Schema.
<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.domain.com/ws/protocol"
xmlns:tns="http://www.domain.com/ws/protocol"
xmlns:ws="http://www.domain.com/ws"
elementFormDefault="qualified">
<import namespace="http://www.domain.com/ws" schemaLocation="ws.xsd"/>
<element name="Message">
<complexType>
<sequence>
<element name="HeaderMessage">
<complexType>
<sequence>
<element name="MSGTYPE" type="string"/>
</sequence>
</complexType>
</element>
<element name="BodyMessage">
<complexType>
<sequence>
<element ref="ws:ServiceResultObject"/>
</sequence>
</complexType>
</element>
</sequence>
</complexType>
</element>
</schema>
Creating the JAXBContext
Once you have the two XML Schemas the classes will generate to 2 different packages. Below is an example of how to bootstrap the JAXBContext. Note that the package names are delimited by the : character.
JAXBContext jc = JAXBContext.newInstance("com.domain.ws:com.domain.ws.protocol");

get Jboss 7 to use custom top down WSDL definition of your choosing, without having CXF generate a wsdl

we are moving a topdown (wsdl first) ws from jboss4 to jboss7 and are having some difficulties with CXF. I am noticing that the large wsdl file located in
standalone\data\wsdl\x.ear\x.war\x.wsdl
is not generated off the supplied xsd set, it seems to be generating off the classes. For instance our definition from JBoss4 reads (obfuscated)
<element name="x">
<complexType>
<sequence>
<element minOccurs="0" name="x" type="x"/>
<choice>
<element name="x" type="x"/>
<element name="x" type="x"/>
<element name="x" type="x"/>
<element name="x" type="x"/>
<element name="x" type="x"/>
</choice>
</sequence>
</complexType>
</element>
and the one generated by CXF has all the coice element replaced with minoccurs = 0, totally invalid for our actual use.
<xs:element name="x">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="x" type="xs:string"/>
<xs:element minOccurs="0" name="x" type="ns1:x"/>
<xs:element minOccurs="0" name="x" type="ns1:x"/>
<xs:element minOccurs="0" name="x" type="ns1:x"/>
<xs:element minOccurs="0" name="x" type="ns1:x"/>
<xs:element minOccurs="0" name="x" type="ns1:x"/>
</xs:sequence>
</xs:complexType>
</xs:element>
This is because wsconsume builds our java classes and annotates them well enough to be parsed by cxf, but not well enough to actually present the initial intent of the complexTypes, so after much looking, how do you get Jboss 7 to use the WSDL definition of your choosing, without having CXF generate a wsdl? We will not be using Spring to make this happen, so... Discuss!
so setting the wsdlLocation in the #webservice annotation lead to the error described here
http://mail-archives.apache.org/mod_mbox/cxf-users/200806.mbox/%3C1932ACF3-DCD2-4073-83DD-981FC0F68F53#apache.org%3E
so it turned way simpler after reading it
the directions there list two options:
1) Update the #WebService annotation on the FooDocumentImpl class to
have the serviceName/portName attributes that match the values in the
wsdl.
2) Update the element in your config to add the
qnames for the service name and portname.
the first is if you are not using Spring, so after explicitly bringing the #WebService annotation to:
#WebService(endpointInterface = "main.package.InterfaceWS", serviceName = "InterfaceWSService", name = "InterfaceWS", targetNamespace = "http://Interface.namespace.main", wsdlLocation = "WEB-INF/wsdl/InterfaceWS.wsdl" ,portName="InterfaceWSPort")
to match the service description in the InterfaceWS.wsdl:
<service name="InterfaceWSService">
<port binding="tns:InterfaceWSSOAPBinding" name="InterfaceWSPort">
<soap:address location="http://localhost:8080/publishedLocationOfInterface />
</port>
</service>
it worked like a charm

Categories