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.
Related
I am working with JAXB generated classes from schema and got stuck with following problem. I am trying to handle both null and empty string values with single element.
What I want to achieve is to be able to marshal both null and empty string values and receive the same while unmarshaling.
Element("key1", "value1") -> <element key="key1">value1</element>
Element("key2", "") -> <element key="key2"/>
Element("key3", null) -> <element key="key3" xsi:nil="true"/>
Schema xsd:
<xs:element name="element" nillable="true">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="key" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
So far I get the following results, if value is null or "" I get the same xml:
Element("key1", null) -> <element key="key1"/> -> Element("key1", "")
Element("key2", "") -> <element key="key2"/> -> Element("key2", "")
Any idea if it is even possible with this xml structure? The only idea I have is to change structure to something like this, but it will generate more useless classes to deal with.
<element>
<key>key1</key>
<value>value1</value>
</element>
Edit
With given schema change it works as expected. The question if it is possible with original structure is still open.
<xs:element name="element">
<xs:complexType>
<xs:sequence>
<xs:element name="key" type="xs:string"/>
<xs:element name="value" type="xs:string" nillable="true"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<element>
<key>key1</key>
<value xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</element>
<element>
<key>key2</key>
<value/>
</element>
Here is the validation.xml
<constraint-mappings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.1.xsd"
xmlns="http://jboss.org/xml/ns/javax/validation/mapping" version="1.1">
<default-package>com.esq.rbac.model</default-package>
<bean class="Tenant" ignore-annotations="true">
...
<field name="ivrPin">
<constraint annotation="javax.validation.constraints.Size">
<element name="min">6</element>
<element name="max">6</element>
<message>IVR Pin must be 6 digit numeric</message>
</constraint>
<constraint annotation="javax.validation.constraints.Pattern">
<element name="regexp">[0-9]+</element>
</constraint>
</field>
</bean>
During execution getting error saying
c.e.u.imports.Main [run] - HV000115: Error parsing mapping file.
javax.validation.ValidationException: HV000115: Error parsing mapping file.
Caused by: org.xml.sax.SAXParseException: cvc-complex-type.2.4.a:
Invalid content was found starting with element 'message'. One of '{"http://jboss.org/xml/ns/javax/validation/mapping":element}' is expected.
at org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source)
How to include the error message properly
You just need to make one change by placing your message directly after constraint annotation tag, before the element tags.
<bean class="Tenant" ignore-annotations="true">
...
<field name="ivrPin">
<constraint annotation="javax.validation.constraints.Size">
<message>IVR Pin must be 6 digit numeric</message>
<element name="min">6</element>
<element name="max">6</element>
</constraint>
The reason for this is as follows:
If you look at the xsd file for validation-mapping:
validation-mapping-1.1.xsd
<xs:complexType name="constraintType">
<xs:sequence>
<xs:element type="xs:string" name="message" minOccurs="0"/>
<xs:element type="map:groupsType"
name="groups"
minOccurs="0"/>
<xs:element type="map:payloadType"
name="payload"
minOccurs="0"/>
<xs:element type="map:elementType"
name="element"
maxOccurs="unbounded"
minOccurs="0"/>
</xs:sequence>
<xs:attribute type="xs:string" name="annotation" use="required"/>
</xs:complexType>
It is using xs:sequence tag which specifies that the child elements must appear in a sequence. Refer element sequence. Here message attribute comes before other attributes like groups, payload or element.
I wish to create a grammar able to validate both a full XML document and a fragment of it.
I have a set of documents aggregate in a “batch”. Each document has a set of meta-data:
<batch>
<document>
<metadata1 />
<metadata2 />
<metadata3 />
</document>
<document>
<metadata1 />
<metadata2 />
<metadata3 />
</document>
</batch>
My SpringBatch process splits the batch in documents (with StaxEventItemReader)
I wish to validate a sub XML representing a single document:
<document>
<metadata1 />
<metadata2 />
<metadata3 />
</document>
I read here that I can’t use partial XSD to validate XML.
However, is there is a way, while avoiding duplication, to validate with two XSDs, where one would validate the fragment, and the other would validate the batch?
You can achieve your goal with a single XSD by specifying multiple possible root elements:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="batch">
<xs:complexType>
<xs:sequence>
<xs:element ref="document" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="document">
<xs:complexType>
<xs:sequence>
<xs:element name="metadata1" type="xs:string" />
<xs:element name="metadata2" type="xs:string" />
<xs:element name="metadata3" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
This way documents may have either a batch or a document root element, and there is no definition duplication.
I have xsd schema below describe the
<xs:element name="ReqStartTest">
<xs:complexType>
<xs:sequence>
<xs:element name="Version" >
<xs:simpleType>
<xs:restriction base="xs:string" />
</xs:simpleType>
</xs:element>
<xs:element name="Time" >
<xs:simpleType>
<xs:restriction base="xs:string" />
</xs:simpleType>
</xs:element>
<xs:element ref="tns:StartTestRequest" minOccurs="1" maxOccurs="1"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="StartTestRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="Name" >
<xs:simpleType>
<xs:restriction base="xs:string" />
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
After i generate java classes from xsd file, and recieve *.wsdl file. After i testing *.wsdl file in SOAPUI, I see just "StartTestRequest" request. My question, why/where my input data ("Version","Time") in request?
Thanks in advance.
Spring only generates request/response for the elements that has the postfixes "request" and "response". In this case, you are referencing "StartTestRequest" from "ReqStartTest". So, you will only see "StartTestRequest" which only has the name field in it. You should extend or reference "ReqStartTest" in your "StartTestRequest"
Following should work fine.
<complexType name="ReqStartTest">
<sequence>
<element name="Version" type="string"/>
<element name="Time" type="string"/>
</sequence>
</complexType>
<element name="StartTestRequest">
<complexType>
<complexContent>
<extension base="tns:ReqStartTest">
<sequence>
<element name="Name" type="string"/>
</sequence>
</extension>
</complexContent>
</complexType>
</element>
Update:
Spring requires predefined suffixes to identify elements that are requests or responses of a web service. Default suffixes are "Request" and "Response". You can change it in the configuration. This thread might help you: Spring-ws: How to create Wsdl from an xsd with no "Request" element
Also this is the closest you will get without changing suffixes:
<element name="ReqStartTestRequest">
<complexType>
<sequence>
<element name="version" type="string"/>
<element name="time" type="string"/>
<element name="startTestRequest" type="tns:StartTestRequest"/>
</sequence>
</complexType>
</element>
<complexType name="StartTestRequest">
<sequence>
<element name="name" type="string"/>
</sequence>
</complexType>
I highly advise you to follow naming conventions. For example, first letter of a variable or non constant field should be lower case (camelcase notation).
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");