Jaxb validation against schema with only complex types
Asked Answered
P

1

7

I have a wsdl that contains request and response elements:

<xsd:element name="someRequest" type="ns:SomeRequest"/>

<xsd:element name="someResponse" type="ns:SomeResponse"/>

This wsdl imports a few xsd's, which contain, among others, these complexTypes:

<xsd:complexType name="SomeRequest">
    ...
</xsd:complexType>

<xsd:complexType name="SomeResponse">
    ...
</xsd:complexType>

I want to validate an xml against this wsdl, but I can't get it to work. I'm basically using the method described on http://actimem.com/java/jaxb-validation/#Marshalling_Validation, by marshalling the object and setting a Schema and EventHandler.

If I set the Schema to the xsd, it won't validate because there's no element in the xsd. This is pretty logical, I get the same with other tools. When I use the wsdl as Schema, I get this error because of inline documentation:

s4s-elt-character: Non-whitespace characters are not allowed in schema elements other than 'xs:appinfo' and 'xs:documentation'.

Is there any way to get this to validate?

Since I'm working on existing projects (currently using Xmlbeans instead of JAXB) with a set workflow, I have to work with a few constraints:

  • These schemas were not made by us and can't be edited, so removing the docs or adding the elements isn't an option.
  • I would prefer to load the xsd's from 1 place because of duplication and possible human errors. Xsd's are stored in a separate project, which builds jars we include as dependencies in our projects. The jars that contain the xjc generated classes also contain the schemas.
  • The xsd's contain a lot of imports.
  • Because of namespacing and classloading collisions, I can't use the ObjectFactory generated by xjc. Multiple xsd's have the same namespace but are actually different, and this has caused issues with the wrong ObjectFactory getting loaded in generic projects that use multiple xsd's.

I've tried transforming the schema to add the element manually, but I can't find a way to do it on an existing Schema object. If I do this through DOM manipulation, I get the new schema as a String object, which won't parse into a Schema because the imported xsd's can't be found.

If I copy the xsd's from the jar to the project, I can transform them and get the validation to succeed, but I don't really like this solution. If there's no other way, I'll revert to that, but I would like to find a solution that requires as little work as possible when reusing this method and is the least prone to errors. Other developers will use this code and 1 validate method without needing extra steps to work would be perfect.

I ran into basically this problem before and didn't find a solution then, either. But before, the complexType wasn't the root element and I could just wrap it into other objects until I found an object that was represented as element in the xsd.

Peplos answered 8/3, 2017 at 14:44 Comment(4)
So are you not running a SOAP endpoint which would support schema validation (using WSDL)?Shoulder
I am, but one of the business requirements is that objects have to be validated multiple times during a call, after certain transformations. I have to be able to do it in code.Peplos
So what about adding a new schema which imports the schema you want to validate against, adding the base element types? Then exclude what ever duplicated classes are generated.Shoulder
My first workaround was something like that, but with the xsd's in the jar that contains the xjc compiled classes, and the xsd's duplicated to the project that uses that jar. I could do something like that, but add the xsd to the source jar to avoid duplication... I was looking for a cleaner and easier way, since there are over 100 projects that have to be migrated, but it seems I'll have to do it like this.Peplos
A
3

You may consider to look at javax.xml.validation.Validator class. There is a way to create it for schema in the WSDL document and then you will have your someRequest element you can validate against that schema.

code may look like:

import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.xml.sax.ErrorHandler;

...
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
String testXml = "{your XML document}"
Document domDoc = docBuilder.parse(new ByteArrayInputStream(testXml.getBytes()));
Source inputSource = new DOMSource(domDoc);
Validator validator = requestedSchema.newValidator();
MySchemaValidationErrorHandler errHandler = new MySchemaValidationErrorHandler();

SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema requestedSchema = schemaFactory.newSchema({DOMSource from WSDL document/types/xsd:schema node}));
validator.setErrorHandler(errHandler);
validator.validate(inputSource);

Note that you need to have your own implementation of org.xml.sax.ErrorHandler interface where all warnings and errors will be collected during validation.

In general it is not hard to make all of that. You just need

  1. have Schema Source from WSDL needed definitions/types/xsd:schema Node. it is doable through DOMSource
  2. Pass it as Schema parameter when creates Schema object from schemaFactory.newSchema(...) method
  3. Implement org.xml.sax.ErrorHandler interface where all errors be collected. It is very simple
  4. Handle those errors either right out of handler or collect them and deal with them after validator completes its work. It is up to you...

PS. Usually I create needed Schemas once and keep them in the Map. Just keep in mind - Validator is not thread safe.

of course you can use any other javax.xml.transform.Source implementations, not only DOMSource

Altorilievo answered 23/3, 2017 at 1:33 Comment(8)
I was put on a different task for the next few weeks so I can't immediately test this. I tried this approach as well, but didn't try making a Schema from a DOMSource element gotten from the wsdl. I'll definitely try this when I'm working on this issue again. Unless someone has a catch all solution to validate anything regardless of XmlRootElement and complexTypes, I'll give you the 50 points when the bounty ends.Peplos
Thanks. Take your time and let me know will it work. I may need to do the same sometime. For now I have XSD with elements outside of WSDL, so I just create schemas from files. Also biggest plus to use Validator is that it does full validation against all Schema defined restrictions which JAXB does not. As example if value is a restricted string by length or pattern, JAXB does not care. Validator does.Altorilievo
When you say JAXB doesn't care, do you mean during default marshalling? I thought it did check every restriction if you set a Schema to the Marshaller?Peplos
yes. It does not. I tested that. I had restricted in XSD string by length 80. But I was able to marshal/unmarshal much longer with no problems. Then I had to use either custom validation from POJO or use that ValidatorAltorilievo
I tested your solution and creating a Schema from the wsdl's turned out to be quite hard. Since all the wsdl's and xsd's are in dependency jars, every single include and import had to be parsed to a Source as well, and it just became one big namespace mess...Peplos
Yes... I expected something like that... but I do not see other ways for now. It is actual problem of how WSDL/XSD organized. ..sorry... Do you have an ability to use not exact version of WSDL/XSD but "extend" them to your local version and have elements in XSD rather than in WSDL? It will be easiest solution. Sometime we do that... Actually target Webservice does not care about how you created request it just must be valid in terms of namespaces/structures.Altorilievo
Our solution now is adding the root elements to the xsd in the ant script that calls xjc. Since that code is shared between all dependency builds that require that schema, it's a one time thing per problematic schema. Not the cleanest solution but the least error prone I found.Peplos
IMHO it is the best solution to "extend" such xsd either by "local copy" or by script as you said. Script is better. There is no need to keep and maintain not original "local copy"Altorilievo

© 2022 - 2024 — McMap. All rights reserved.